@ttsc/graph 0.19.1 → 0.19.3
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/lib/TtscGraphApplication.js +3 -1
- package/lib/TtscGraphApplication.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/model/TtscGraphSession.js +509 -162
- package/lib/model/TtscGraphSession.js.map +1 -1
- package/lib/model/loadGraph.d.ts +14 -0
- package/lib/model/loadGraph.js +382 -101
- package/lib/model/loadGraph.js.map +1 -1
- package/lib/resolveGraphBinary.d.ts +9 -3
- package/lib/resolveGraphBinary.js +9 -3
- package/lib/resolveGraphBinary.js.map +1 -1
- package/lib/server/createServer.js +5 -5
- package/lib/server/resultAudit.d.ts +29 -8
- package/lib/server/resultAudit.js +47 -9
- package/lib/server/resultAudit.js.map +1 -1
- package/lib/server/runDetails.d.ts +7 -0
- package/lib/server/runDetails.js +87 -58
- package/lib/server/runDetails.js.map +1 -1
- package/lib/structures/ITtscGraphApplication.d.ts +8 -7
- package/lib/structures/ITtscGraphDetails.d.ts +18 -8
- package/lib/structures/ITtscGraphDump.d.ts +169 -6
- package/lib/structures/ITtscGraphNode.d.ts +37 -0
- package/lib/structures/ITtscGraphSnapshot.d.ts +55 -0
- package/lib/structures/ITtscGraphSnapshot.js +3 -0
- package/lib/structures/ITtscGraphSnapshot.js.map +1 -0
- package/lib/structures/ITtscGraphTour.d.ts +1 -1
- package/lib/structures/index.d.ts +1 -0
- package/lib/structures/index.js +1 -0
- package/lib/structures/index.js.map +1 -1
- package/package.json +5 -2
- package/src/TtscGraphApplication.ts +4 -1
- package/src/index.ts +1 -0
- package/src/model/TtscGraphSession.ts +89 -16
- package/src/model/loadGraph.ts +36 -0
- package/src/resolveGraphBinary.ts +9 -3
- package/src/server/resultAudit.ts +47 -8
- package/src/server/runDetails.ts +95 -70
- package/src/structures/ITtscGraphApplication.ts +8 -7
- package/src/structures/ITtscGraphDetails.ts +18 -8
- package/src/structures/ITtscGraphDump.ts +194 -6
- package/src/structures/ITtscGraphNode.ts +40 -0
- package/src/structures/ITtscGraphSnapshot.ts +69 -0
- package/src/structures/ITtscGraphTour.ts +1 -1
- package/src/structures/index.ts +1 -0
|
@@ -5,9 +5,15 @@ import path from "node:path";
|
|
|
5
5
|
* Resolve the per-platform `ttscgraph` binary, or `null` when it cannot be
|
|
6
6
|
* located.
|
|
7
7
|
*
|
|
8
|
-
* `ttsc` is a peer
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* `ttsc` is a peer dependency pinned to this package's own version, installed
|
|
9
|
+
* alongside `@ttsc/graph` in the user's project rather than nested under this
|
|
10
|
+
* launcher, so resolution starts from the user's project, not from this
|
|
11
|
+
* package's own tree.
|
|
12
|
+
*
|
|
13
|
+
* The pin is what keeps the two halves of the graph in step. `literals` and the
|
|
14
|
+
* rest of the node facts are resolved by the Go builder and ride the dump, so a
|
|
15
|
+
* newer `@ttsc/graph` reading an older `ttscgraph` would find those fields
|
|
16
|
+
* simply absent and answer from a graph missing facts it believes are there.
|
|
11
17
|
*
|
|
12
18
|
* Resolution order:
|
|
13
19
|
*
|
|
@@ -47,14 +47,18 @@
|
|
|
47
47
|
* payload, and told it to stop where the shortlist's coverage was exactly what
|
|
48
48
|
* needed judging.
|
|
49
49
|
*
|
|
50
|
-
* So
|
|
51
|
-
* from an explicit handle or structure (`trace`, `
|
|
52
|
-
* result is what the graph holds for what you named, bounded
|
|
53
|
-
* `truncated` says. {@link
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
50
|
+
* So the audits split by what the reader must judge. {@link RESULT_AUDIT}
|
|
51
|
+
* covers the walks from an explicit handle or structure (`trace`, `overview`):
|
|
52
|
+
* the result is what the graph holds for what you named, bounded where
|
|
53
|
+
* `truncated` says. {@link RESULT_AUDIT_DETAILS} covers `details`, whose result
|
|
54
|
+
* is not one bounded whole but two halves — a symbol's own shape, returned
|
|
55
|
+
* complete, and its fan-out, returned as a slice with `trace` for the rest — so
|
|
56
|
+
* it names which half to trust outright and which to follow. {@link
|
|
57
|
+
* RESULT_AUDIT_SELECTION} covers the ranked shortlists (`lookup`, `entrypoints`,
|
|
58
|
+
* `tour`): it keeps the same fact-verification and adds that the _selection_ is
|
|
59
|
+
* heuristic, so whether the shortlist covers the question is the caller's to
|
|
60
|
+
* judge — and a follow-up request or a read of a cited span is a sound next
|
|
61
|
+
* step, not a failure to trust the result.
|
|
58
62
|
*
|
|
59
63
|
* Keep both narrow, and keep the stop-or-continue decision with the result's
|
|
60
64
|
* `next`, so neither contradicts a partial result that legitimately asks for
|
|
@@ -104,6 +108,41 @@ span is a sound next step, not a failure to trust the result.
|
|
|
104
108
|
Follow \`next\` for where that leaves the question.
|
|
105
109
|
`.trim();
|
|
106
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The audit for `details`, whose contract is not the other exact operations'.
|
|
113
|
+
*
|
|
114
|
+
* `trace` walks and marks where the walk was cut with `truncated`; `details`
|
|
115
|
+
* does not walk. It resolves a named handle, and its result splits in two. What
|
|
116
|
+
* a symbol *is* — its members, its values, its signature — is bounded by the
|
|
117
|
+
* declaration and returned whole, so the old "trust it, do not open the file"
|
|
118
|
+
* is finally true of it rather than contradicted by a capped member list. What
|
|
119
|
+
* a symbol *reaches or is reached by* — its calls, its type references, its
|
|
120
|
+
* implementers, its dependents — is bounded by how widely it is used, not by
|
|
121
|
+
* the symbol, so returning it whole is a `trace`/impact answer of a thousand
|
|
122
|
+
* refs in a "what is this" call. That half is a short orientation slice, and
|
|
123
|
+
* the audit has to say which half is which so the reader trusts the complete
|
|
124
|
+
* one and reaches for `trace` on the other, instead of reading the file for a
|
|
125
|
+
* member list that is already here.
|
|
126
|
+
*/
|
|
127
|
+
export const RESULT_AUDIT_DETAILS: string = `
|
|
128
|
+
AUDITED BEFORE RETURNING. READ FIRST.
|
|
129
|
+
|
|
130
|
+
The server assembled this \`result\`, then checked it: every fact in it — each name, span,
|
|
131
|
+
edge, signature, member, and value — was taken back to the type-checked program for the
|
|
132
|
+
snapshot this call synced to, and resolved there, so for this snapshot a fact here cannot be
|
|
133
|
+
wrong.
|
|
134
|
+
|
|
135
|
+
This is the structure the graph holds for the handles you named. What a symbol is — its
|
|
136
|
+
members, its values, its signature — is complete: trust it and do not open the file to read
|
|
137
|
+
what is already here. What a symbol reaches or is reached by — its calls, its type
|
|
138
|
+
references, its implementers, and under \`neighbors\` its dependents — is a short orientation
|
|
139
|
+
slice, not the whole set, because that grows with how widely a symbol is used; \`trace\`
|
|
140
|
+
follows it in full.
|
|
141
|
+
|
|
142
|
+
Follow \`next\`: answer from this result, and re-call the graph only when it says inspect, or
|
|
143
|
+
after you edit the source.
|
|
144
|
+
`.trim();
|
|
145
|
+
|
|
107
146
|
/** The escape branch carries no graph facts, so it claims none. */
|
|
108
147
|
export const RESULT_AUDIT_ESCAPE: string =
|
|
109
148
|
"This escape carries no graph facts to audit.";
|
package/src/server/runDetails.ts
CHANGED
|
@@ -15,17 +15,20 @@ import { IRunnerOutput, resultNext } from "./resultNext";
|
|
|
15
15
|
const MAX_SIGNATURE_LINES = 4;
|
|
16
16
|
// A doc summary is one sentence; the rest of the comment is the file's to keep.
|
|
17
17
|
const MAX_DOC_CHARS = 200;
|
|
18
|
-
//
|
|
18
|
+
// An object literal's outline is scanned from source, so a runaway literal is
|
|
19
|
+
// bounded here; the members it does find are not capped.
|
|
20
|
+
const MAX_OBJECT_MEMBER_LINES = 300;
|
|
21
|
+
// A symbol's fan-out — what it calls, what names it in a type, what depends on
|
|
22
|
+
// it — scales with how popular it is, not with the symbol: a central type is
|
|
23
|
+
// named in a thousand places, and returning all of them is a hundred thousand
|
|
24
|
+
// tokens of "who uses this", which is a trace/impact question, not "what is
|
|
25
|
+
// this". So fan-out is a small default slice; identity (members, literals) is
|
|
26
|
+
// not, because a class's members and a union's values are the symbol itself and
|
|
27
|
+
// are bounded by the declaration.
|
|
19
28
|
const DEFAULT_NEIGHBORS = 2;
|
|
20
29
|
const MAX_NEIGHBORS = 3;
|
|
21
|
-
// A container outline can be long; default to a scannable first page.
|
|
22
|
-
const DEFAULT_MEMBERS = 6;
|
|
23
|
-
const MAX_MEMBERS = 8;
|
|
24
|
-
// Direct dependency groups are orientation slices, not full fan-out dumps.
|
|
25
30
|
const DEFAULT_DEPENDENCIES = 2;
|
|
26
31
|
const MAX_DEPENDENCIES = 4;
|
|
27
|
-
// Object literal outlines are navigation aids, not source excerpts.
|
|
28
|
-
const MAX_OBJECT_MEMBER_LINES = 300;
|
|
29
32
|
// Structural relationships are navigation, not the dependency picture details is for.
|
|
30
33
|
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports", "imports"]);
|
|
31
34
|
// Kinds whose value is their member outline, not implementation text.
|
|
@@ -47,17 +50,17 @@ export function runDetails(
|
|
|
47
50
|
graph: TtscGraphMemory,
|
|
48
51
|
props: ITtscGraphDetails.IRequest,
|
|
49
52
|
): IRunnerOutput<ITtscGraphDetails> {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const memberLimit =
|
|
57
|
-
const
|
|
53
|
+
// Identity is the whole answer. The caller named this handle to learn what it
|
|
54
|
+
// is, and a class's members or a union's values are the symbol itself — cut
|
|
55
|
+
// them and the model reads the file for the rest, the read this index exists
|
|
56
|
+
// to remove. So `memberLimit` and `literals` default to unlimited. Fan-out
|
|
57
|
+
// does not: what names or uses a symbol is bounded by its popularity, not by
|
|
58
|
+
// it, so those stay a small slice with `trace` for the rest.
|
|
59
|
+
const memberLimit = limitOf(props.memberLimit);
|
|
60
|
+
const neighborLimit = capOf(props.neighborLimit, DEFAULT_NEIGHBORS, MAX_NEIGHBORS);
|
|
61
|
+
const dependencyLimit = capOf(
|
|
58
62
|
props.dependencyLimit,
|
|
59
63
|
DEFAULT_DEPENDENCIES,
|
|
60
|
-
1,
|
|
61
64
|
MAX_DEPENDENCIES,
|
|
62
65
|
);
|
|
63
66
|
const wantNeighbors = props.neighbors === true;
|
|
@@ -101,7 +104,6 @@ export function runDetails(
|
|
|
101
104
|
if (sig !== undefined) detail.signature = sig;
|
|
102
105
|
const doc = docOf(graph.project, node);
|
|
103
106
|
if (doc !== undefined) detail.doc = doc;
|
|
104
|
-
const signatureLiterals = literalSummaries(sig);
|
|
105
107
|
const decorators = decoratorsOf(node);
|
|
106
108
|
if (decorators !== undefined) detail.decorators = decorators;
|
|
107
109
|
const implementation = evidenceCoordinatesOf(node.implementation);
|
|
@@ -150,8 +152,18 @@ export function runDetails(
|
|
|
150
152
|
);
|
|
151
153
|
if (list.length > 0) detail.members = list;
|
|
152
154
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
+
// An enum's members ride on its own node rather than on `contains` edges,
|
|
156
|
+
// because they are not nodes: the outline above finds nothing for an enum
|
|
157
|
+
// and always did. Its signature stops at the `{`, so without this the one
|
|
158
|
+
// kind whose entire content is its member list answered with none of it.
|
|
159
|
+
// Uncapped like every other identity list — the members are the enum.
|
|
160
|
+
if (node.kind === "enum") {
|
|
161
|
+
const list = enumMembers(node, memberLimit);
|
|
162
|
+
if (list.length > 0) detail.members = list;
|
|
163
|
+
}
|
|
164
|
+
if (node.literals !== undefined && node.literals.length > 0) {
|
|
165
|
+
detail.literals = node.literals;
|
|
166
|
+
}
|
|
155
167
|
if (wantNeighbors) {
|
|
156
168
|
detail.dependsOn = refs(
|
|
157
169
|
graph,
|
|
@@ -222,6 +234,28 @@ function members(
|
|
|
222
234
|
return out;
|
|
223
235
|
}
|
|
224
236
|
|
|
237
|
+
/**
|
|
238
|
+
* An enum's members, owner-qualified so the name reads the way the code writes
|
|
239
|
+
* it, with the value each carries as its signature.
|
|
240
|
+
*
|
|
241
|
+
* The name is why this exists. `literals` answers what values the enum admits,
|
|
242
|
+
* but a caller writes `Colors.Red` and never `"red"`, so an enum the graph
|
|
243
|
+
* already held sent a caller that had named it to the file for the one fact it
|
|
244
|
+
* came for (#738).
|
|
245
|
+
*/
|
|
246
|
+
function enumMembers(
|
|
247
|
+
node: ITtscGraphNode,
|
|
248
|
+
limit: number,
|
|
249
|
+
): ITtscGraphDetails.IMember[] {
|
|
250
|
+
return (node.enumMembers ?? []).slice(0, limit).map((member) => ({
|
|
251
|
+
name: `${node.qualifiedName ?? node.name}.${member.name}`,
|
|
252
|
+
kind: "property",
|
|
253
|
+
...(member.value !== undefined
|
|
254
|
+
? { signature: `${member.name} = ${member.value}` }
|
|
255
|
+
: {}),
|
|
256
|
+
}));
|
|
257
|
+
}
|
|
258
|
+
|
|
225
259
|
function objectLiteralMembers(
|
|
226
260
|
project: string,
|
|
227
261
|
span: Pick<ITtscGraphEvidence, "file" | "startLine" | "endLine">,
|
|
@@ -328,13 +362,11 @@ function refs(
|
|
|
328
362
|
if (evidence !== undefined) ref.evidence = evidence;
|
|
329
363
|
ranked.push({ ref, rank: refRank(ref, edge) });
|
|
330
364
|
}
|
|
365
|
+
// Ranked so a caller that does throttle (the tour) keeps the strongest refs,
|
|
366
|
+
// not the ones nearest the top of a file. Uncapped, `limit` is Infinity and
|
|
367
|
+
// the sort is just a stable order.
|
|
331
368
|
ranked.sort((a, b) => a.rank - b.rank);
|
|
332
|
-
|
|
333
|
-
for (const item of ranked) {
|
|
334
|
-
out.push(item.ref);
|
|
335
|
-
if (out.length >= limit) break;
|
|
336
|
-
}
|
|
337
|
-
return out;
|
|
369
|
+
return ranked.map((item) => item.ref).slice(0, limit);
|
|
338
370
|
}
|
|
339
371
|
|
|
340
372
|
const executionKinds = new Set([
|
|
@@ -375,17 +407,7 @@ function dependencyRefs(
|
|
|
375
407
|
rank: refRank(ref, edge),
|
|
376
408
|
});
|
|
377
409
|
}
|
|
378
|
-
ranked
|
|
379
|
-
const out: ITtscGraphDetails.IReference[] = [];
|
|
380
|
-
const seen = new Set<string>();
|
|
381
|
-
for (const item of ranked) {
|
|
382
|
-
const key = `${item.ref.relation}:${item.ref.id}`;
|
|
383
|
-
if (seen.has(key)) continue;
|
|
384
|
-
seen.add(key);
|
|
385
|
-
out.push(item.ref);
|
|
386
|
-
if (out.length >= limit) break;
|
|
387
|
-
}
|
|
388
|
-
return out;
|
|
410
|
+
return rankedRefs(ranked, limit);
|
|
389
411
|
}
|
|
390
412
|
|
|
391
413
|
function incomingDependencyRefs(
|
|
@@ -416,6 +438,14 @@ function incomingDependencyRefs(
|
|
|
416
438
|
rank: refRank(ref, edge),
|
|
417
439
|
});
|
|
418
440
|
}
|
|
441
|
+
return rankedRefs(ranked, limit);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** Sort by rank, drop duplicate (relation, id) pairs, and cut to `limit`. */
|
|
445
|
+
function rankedRefs(
|
|
446
|
+
ranked: Array<{ ref: ITtscGraphDetails.IReference; rank: number }>,
|
|
447
|
+
limit: number,
|
|
448
|
+
): ITtscGraphDetails.IReference[] {
|
|
419
449
|
ranked.sort((a, b) => a.rank - b.rank);
|
|
420
450
|
const out: ITtscGraphDetails.IReference[] = [];
|
|
421
451
|
const seen = new Set<string>();
|
|
@@ -429,38 +459,26 @@ function incomingDependencyRefs(
|
|
|
429
459
|
return out;
|
|
430
460
|
}
|
|
431
461
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
function cleanLiteral(value: string | undefined): string | undefined {
|
|
444
|
-
const text = value?.replace(/\s+/g, " ").trim();
|
|
445
|
-
if (
|
|
446
|
-
text === undefined ||
|
|
447
|
-
text === "" ||
|
|
448
|
-
text.length > 40 ||
|
|
449
|
-
/^[{}()[\],.:;]+$/.test(text)
|
|
450
|
-
) {
|
|
451
|
-
return undefined;
|
|
452
|
-
}
|
|
453
|
-
return text;
|
|
462
|
+
/**
|
|
463
|
+
* An identity list's cap: none by default, honored when a caller passes one.
|
|
464
|
+
* details answers a named handle's own shape in full — its members, its values —
|
|
465
|
+
* so the default is unlimited; the tour passes an explicit number to embed a
|
|
466
|
+
* compact slice of its own.
|
|
467
|
+
*/
|
|
468
|
+
function limitOf(value: number | undefined): number {
|
|
469
|
+
return value === undefined || !Number.isFinite(value)
|
|
470
|
+
? Infinity
|
|
471
|
+
: Math.max(1, Math.floor(value));
|
|
454
472
|
}
|
|
455
473
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
): number {
|
|
474
|
+
/**
|
|
475
|
+
* A fan-out list's cap: a small default, clamped to a ceiling. What names or
|
|
476
|
+
* uses a symbol grows with its popularity, not with the symbol, so the whole
|
|
477
|
+
* list is a trace/impact answer and details returns an orientation slice.
|
|
478
|
+
*/
|
|
479
|
+
function capOf(value: number | undefined, fallback: number, max: number): number {
|
|
462
480
|
const n = value === undefined || !Number.isFinite(value) ? fallback : value;
|
|
463
|
-
return Math.max(
|
|
481
|
+
return Math.max(1, Math.min(max, Math.floor(n)));
|
|
464
482
|
}
|
|
465
483
|
|
|
466
484
|
/**
|
|
@@ -611,6 +629,13 @@ export function docOf(
|
|
|
611
629
|
* The declaration signature: the head of the declaration up to and including
|
|
612
630
|
* the line that opens its body (`{`), or the single declaration line when there
|
|
613
631
|
* is no brace, capped so a wrapped signature cannot run away.
|
|
632
|
+
*
|
|
633
|
+
* It never runs past the declaration's own span. The stop used to be the brace,
|
|
634
|
+
* the trailing semicolon, or the line cap, and a declaration ending in none of
|
|
635
|
+
* them read its neighbors instead: an enum member ends in a comma, so `VIEW`
|
|
636
|
+
* came back as itself plus the two members after it and the closing brace. The
|
|
637
|
+
* span is the fact that says where the declaration ends, and it was already on
|
|
638
|
+
* the node.
|
|
614
639
|
*/
|
|
615
640
|
export function signatureOf(
|
|
616
641
|
project: string,
|
|
@@ -621,12 +646,12 @@ export function signatureOf(
|
|
|
621
646
|
evidence === undefined ? undefined : fileLines(project, evidence.file);
|
|
622
647
|
if (lines === undefined || evidence === undefined) return undefined;
|
|
623
648
|
const start = Math.max(0, evidence.startLine - 1);
|
|
649
|
+
const last =
|
|
650
|
+
evidence.endLine === undefined
|
|
651
|
+
? lines.length - 1
|
|
652
|
+
: Math.min(lines.length - 1, evidence.endLine - 1);
|
|
624
653
|
const out: string[] = [];
|
|
625
|
-
for (
|
|
626
|
-
let i = start;
|
|
627
|
-
i < lines.length && out.length < MAX_SIGNATURE_LINES;
|
|
628
|
-
i++
|
|
629
|
-
) {
|
|
654
|
+
for (let i = start; i <= last && out.length < MAX_SIGNATURE_LINES; i++) {
|
|
630
655
|
const line = lines[i];
|
|
631
656
|
if (line === undefined) break;
|
|
632
657
|
out.push(line);
|
|
@@ -169,13 +169,14 @@ export namespace ITtscGraphApplication {
|
|
|
169
169
|
* resolves to the type-checked program for the snapshot the call synced to,
|
|
170
170
|
* so opening a file it cites only returns a fact already in it.
|
|
171
171
|
*
|
|
172
|
-
* The audit is operation-aware. For the
|
|
173
|
-
* `
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
172
|
+
* The audit is operation-aware. For the walks from a named handle (`trace`,
|
|
173
|
+
* `overview`) it reports the result as the structure the graph holds,
|
|
174
|
+
* bounded where `truncated` says. For `details` it reports the two halves of
|
|
175
|
+
* a resolved symbol: its own shape returned whole, its fan-out returned as a
|
|
176
|
+
* slice with `trace` for the rest. For the ranked operations (`lookup`,
|
|
177
|
+
* `entrypoints`, `tour`) it adds that the selection is heuristic — matched,
|
|
178
|
+
* scored, ranked, and limited against the question — so the facts are
|
|
179
|
+
* verified but the shortlist's coverage is the caller's to judge.
|
|
179
180
|
*/
|
|
180
181
|
audit: string;
|
|
181
182
|
|
|
@@ -49,24 +49,26 @@ export namespace ITtscGraphDetails {
|
|
|
49
49
|
neighbors?: boolean;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
52
|
+
* Dependencies and dependents per side when `neighbors:true`. A small
|
|
53
|
+
* orientation slice by default; what uses a symbol grows with its
|
|
54
|
+
* popularity, so `trace` answers the whole "who uses this".
|
|
54
55
|
*
|
|
55
56
|
* @default 2
|
|
56
57
|
*/
|
|
57
58
|
neighborLimit?: number;
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
61
|
+
* Owned members for a container or object literal. The complete outline by
|
|
62
|
+
* default — a class's members and an enum's are the symbol itself, so they
|
|
63
|
+
* are not sampled. Pass a number to cap.
|
|
63
64
|
*/
|
|
64
65
|
memberLimit?: number;
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
|
-
*
|
|
68
|
+
* Direct execution and type references per group. A small orientation slice
|
|
69
|
+
* by default; `trace` follows the whole fan-out.
|
|
68
70
|
*
|
|
69
|
-
* @default
|
|
71
|
+
* @default 2
|
|
70
72
|
*/
|
|
71
73
|
dependencyLimit?: number;
|
|
72
74
|
|
|
@@ -145,7 +147,15 @@ export namespace ITtscGraphDetails {
|
|
|
145
147
|
/** Concrete nodes that implement or override this interface/base member. */
|
|
146
148
|
implementedBy?: IReference[];
|
|
147
149
|
|
|
148
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* The complete value set a type alias or enum admits, in TypeScript source
|
|
152
|
+
* form (`"a"`, `1`, `true`, `null`) — the checker's resolved union members,
|
|
153
|
+
* not the quoted tokens that happened to fit in `signature`.
|
|
154
|
+
*
|
|
155
|
+
* Absent when the type has no enumerable value set. A `signature` is capped
|
|
156
|
+
* at the declaration head, so for a union or enum written across several
|
|
157
|
+
* lines this is the field that carries the members.
|
|
158
|
+
*/
|
|
149
159
|
literals?: string[];
|
|
150
160
|
|
|
151
161
|
/**
|
|
@@ -7,13 +7,19 @@ import { ITtscGraphSpan } from "./ITtscGraphSpan";
|
|
|
7
7
|
* wire contract between the Go fact-builder and the TypeScript graph engine.
|
|
8
8
|
*
|
|
9
9
|
* It is the complete graph with none of the per-response caps the MCP tools
|
|
10
|
-
* apply: every node and edge the build resolved
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* apply: every node and edge the build resolved, plus the `provenance` that
|
|
11
|
+
* says which program resolved them. The server parses each changed native
|
|
12
|
+
* snapshot (typia-validated) into an in-memory resident graph and reuses that
|
|
13
|
+
* warm model while project inputs stay unchanged; the bundled 3D viewer reduces
|
|
14
|
+
* the same dump.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
* edges
|
|
16
|
+
* `project` is absolute. Every other path is relative to it — `tsconfig`, and
|
|
17
|
+
* the `file` fields on nodes, edges, diagnostics, and the provenance manifest.
|
|
18
|
+
*
|
|
19
|
+
* Two kinds of path fall outside the project and so cannot be relative to it: a
|
|
20
|
+
* dependency keeps its `node_modules/`-relative tail, which is what makes a
|
|
21
|
+
* dependency leaf readable, and anything else the compiler loaded keeps the
|
|
22
|
+
* identity the compiler gave it — a virtual lib stays `bundled:///…`.
|
|
17
23
|
*/
|
|
18
24
|
export interface ITtscGraphDump {
|
|
19
25
|
/** Absolute path of the project root the graph was built for. */
|
|
@@ -22,6 +28,17 @@ export interface ITtscGraphDump {
|
|
|
22
28
|
/** The tsconfig the program was loaded from, relative to `project`. */
|
|
23
29
|
tsconfig: string;
|
|
24
30
|
|
|
31
|
+
/** Evidence about the one program that produced everything below. */
|
|
32
|
+
provenance: ITtscGraphDump.IProvenance;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The compiler's findings for the same generation that produced the facts.
|
|
36
|
+
*
|
|
37
|
+
* Empty means the program reported none. It does not mean they were not
|
|
38
|
+
* collected — `provenance.capabilities` is what says whether they were.
|
|
39
|
+
*/
|
|
40
|
+
diagnostics: ITtscGraphDump.IDiagnostic[];
|
|
41
|
+
|
|
25
42
|
/** Every node the build recorded. */
|
|
26
43
|
nodes: ITtscGraphDump.INode[];
|
|
27
44
|
|
|
@@ -30,6 +47,177 @@ export interface ITtscGraphDump {
|
|
|
30
47
|
}
|
|
31
48
|
|
|
32
49
|
export namespace ITtscGraphDump {
|
|
50
|
+
/**
|
|
51
|
+
* What a snapshot knows about its own origin.
|
|
52
|
+
*
|
|
53
|
+
* The graph's claim is that its nodes, edges, spans, and diagnostics all came
|
|
54
|
+
* from one `Program`. Without this the claim is unprovable from the response:
|
|
55
|
+
* a consumer could only re-read the disk afterwards and hope nothing moved,
|
|
56
|
+
* which is not sound — a write that lands and reverts in between is invisible
|
|
57
|
+
* to it, and a re-read proves what the disk says now, never what the checker
|
|
58
|
+
* resolved against.
|
|
59
|
+
*
|
|
60
|
+
* This carries no source text. A digest is the opposite of inlining: it is
|
|
61
|
+
* what lets a consumer prove byte-identity against text it read itself,
|
|
62
|
+
* without the graph ever shipping that text.
|
|
63
|
+
*/
|
|
64
|
+
export interface IProvenance {
|
|
65
|
+
/**
|
|
66
|
+
* The dump body's schema version, moved when a field is added, removed, or
|
|
67
|
+
* redefined. Independent of the serve protocol's version: a dump written to
|
|
68
|
+
* a file has a schema but never rode the protocol.
|
|
69
|
+
*/
|
|
70
|
+
schemaVersion: number;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* What this snapshot proves. A consumer degrades against this rather than
|
|
74
|
+
* guessing from a field's emptiness, because an empty list and an
|
|
75
|
+
* uncollected one look identical on the wire.
|
|
76
|
+
*
|
|
77
|
+
* The known members are `universe`, `sourceDigests`, `diskDigests`, and
|
|
78
|
+
* `diagnostics`. The type stays `string[]` rather than a union of those on
|
|
79
|
+
* purpose: a union would make `typia.assert` reject a newer producer for
|
|
80
|
+
* naming a capability this client has not heard of, turning "proves more
|
|
81
|
+
* than you know about" into a hard failure. An unknown capability is
|
|
82
|
+
* exactly the case a consumer should ignore.
|
|
83
|
+
*/
|
|
84
|
+
capabilities: string[];
|
|
85
|
+
|
|
86
|
+
/** What built the snapshot. */
|
|
87
|
+
producer: IProducer;
|
|
88
|
+
|
|
89
|
+
/** The inputs that decide which files are in the program at all. */
|
|
90
|
+
universe: IUniverse;
|
|
91
|
+
|
|
92
|
+
/** One entry per file the program loaded, ordered by file. */
|
|
93
|
+
sources: ISourceDigest[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Identifies the binary and the checker behind the facts.
|
|
98
|
+
*
|
|
99
|
+
* `tool` and `version` are separate because more than one binary can produce
|
|
100
|
+
* a dump and they do not share a version line — the shipped `ttscgraph` is
|
|
101
|
+
* stamped at release, the internal viewer tool is not versioned at all — so
|
|
102
|
+
* folding the name in would hand a consumer that parses a version a tool
|
|
103
|
+
* name.
|
|
104
|
+
*/
|
|
105
|
+
export interface IProducer {
|
|
106
|
+
/** The producing binary's name, such as `ttscgraph`. */
|
|
107
|
+
tool: string;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The producing binary's build version, as its `--version` prints it. A
|
|
111
|
+
* local build reports the dev placeholder; a tool that carries no version
|
|
112
|
+
* reports `""`.
|
|
113
|
+
*/
|
|
114
|
+
version: string;
|
|
115
|
+
|
|
116
|
+
/** The TypeScript version typescript-go implements. */
|
|
117
|
+
typescript: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The build universe: the inputs that decide which files the program
|
|
122
|
+
* contains, as opposed to what is inside them. A change to any of them can
|
|
123
|
+
* add or drop whole files, so a consumer reusing facts across snapshots must
|
|
124
|
+
* treat a universe change as invalidating everything.
|
|
125
|
+
*/
|
|
126
|
+
export interface IUniverse {
|
|
127
|
+
/**
|
|
128
|
+
* The tsconfig chain — the project's config and everything it extends.
|
|
129
|
+
*
|
|
130
|
+
* It stays an input regardless of what any source contains: compiler
|
|
131
|
+
* options change the meaning of code the checker resolves without any
|
|
132
|
+
* source file changing.
|
|
133
|
+
*/
|
|
134
|
+
configs: IFileDigest[];
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The resolved root file set, one entry per (config, file) pair. A root a
|
|
138
|
+
* config names but that does not exist is still listed: its absence is part
|
|
139
|
+
* of the fingerprint, and creating it later changes the program.
|
|
140
|
+
*/
|
|
141
|
+
roots: IRootFile[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** A root file attributed to the config that named it. */
|
|
145
|
+
export interface IRootFile {
|
|
146
|
+
/** The tsconfig that named this root, project-relative. */
|
|
147
|
+
config: string;
|
|
148
|
+
|
|
149
|
+
/** The root file, project-relative. */
|
|
150
|
+
file: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** A file and the hex-encoded SHA-256 of its on-disk bytes. */
|
|
154
|
+
export interface IFileDigest {
|
|
155
|
+
/** Project-relative. */
|
|
156
|
+
file: string;
|
|
157
|
+
|
|
158
|
+
/** Hex-encoded SHA-256. */
|
|
159
|
+
digest: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The manifest entry for one source file the program loaded.
|
|
164
|
+
*
|
|
165
|
+
* Two digests, because "the bytes the checker read" and "the bytes on disk"
|
|
166
|
+
* are not always the same string and a consumer needs to know which one it
|
|
167
|
+
* compares against. They diverge when a source-preamble plugin injects text
|
|
168
|
+
* ahead of the file before tsgo parses it, which a real plugin project does
|
|
169
|
+
* on every build.
|
|
170
|
+
*/
|
|
171
|
+
export interface ISourceDigest {
|
|
172
|
+
/** Project-relative. */
|
|
173
|
+
file: string;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Hex-encoded SHA-256 of the text the checker resolved against — the ground
|
|
177
|
+
* truth for the facts. Every node, edge, and span attributed to this file
|
|
178
|
+
* was computed from these bytes.
|
|
179
|
+
*/
|
|
180
|
+
checkerDigest: string;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Hex-encoded SHA-256 of the file's on-disk bytes at snapshot time, or `""`
|
|
184
|
+
* when it could not be read: it vanished mid-load, or it is a virtual
|
|
185
|
+
* source with no on-disk identity.
|
|
186
|
+
*
|
|
187
|
+
* This is the one a consumer that opens the file itself can reproduce. When
|
|
188
|
+
* it equals `checkerDigest`, a matching read proves byte-identity with the
|
|
189
|
+
* facts. When it does not, the checker saw augmented text and that proof is
|
|
190
|
+
* simply not available for this file — which is a thing to report, not to
|
|
191
|
+
* paper over.
|
|
192
|
+
*
|
|
193
|
+
* Read it only when `provenance.capabilities` lists `diskDigests`. Without
|
|
194
|
+
* that claim every one of these is empty because the producer never hashed
|
|
195
|
+
* the disk, which is a different fact from a file that could not be read.
|
|
196
|
+
*/
|
|
197
|
+
diskDigest: string;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** One compiler diagnostic from the generation that produced the facts. */
|
|
201
|
+
export interface IDiagnostic {
|
|
202
|
+
/** Project-relative. */
|
|
203
|
+
file: string;
|
|
204
|
+
|
|
205
|
+
/** 1-based line. */
|
|
206
|
+
line: number;
|
|
207
|
+
|
|
208
|
+
/** 1-based column. */
|
|
209
|
+
column: number;
|
|
210
|
+
|
|
211
|
+
/** The TypeScript diagnostic code, such as 2322. */
|
|
212
|
+
code: number;
|
|
213
|
+
|
|
214
|
+
/** Whether the finding fails a build. */
|
|
215
|
+
category: "error" | "warning";
|
|
216
|
+
|
|
217
|
+
/** The diagnostic text, without the code prefix. */
|
|
218
|
+
message: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
33
221
|
/**
|
|
34
222
|
* A node as the builder sends it: the graph node, minus the file paths inside
|
|
35
223
|
* its spans, which the loader puts back from the node's own `file`.
|