@ttsc/graph 0.30.4 → 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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native argument vectors for graph and lint resident processes.
|
|
3
|
+
*
|
|
4
|
+
* @evidence contracts/common.md#principled-implementation Separate argv elements preserve project and artifact coordinates exactly, while lint's existing equals-form flags carry its manifest and context.
|
|
5
|
+
* @evidence contracts/common.md#clear-and-simple-design Two builders centralize the actual resident launch contracts used by their respective facades.
|
|
6
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Supported native flags are retained without new CLI options, shell strings or fixture-specific arguments.
|
|
7
|
+
* @evidence contracts/common.md#meaningful-documentation Each builder states its protocol and the absence meaning of optional artifact/context coordinates.
|
|
8
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation The namespace groups argv builders; serve and lint own their native coordinate representation.
|
|
9
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources returns plain arrays and holds no handle.
|
|
10
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms groups two builders that each return a fixed-shape array.
|
|
11
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work its builders recompute a small array per spawn, so nothing is shared.
|
|
12
|
+
*/
|
|
13
|
+
export namespace TtscGraphNativeArguments {
|
|
14
|
+
/**
|
|
15
|
+
* Graph serve argv; a null artifact answer adds no startup overlay flag.
|
|
16
|
+
*
|
|
17
|
+
* @evidence contracts/common.md#principled-implementation Separate argv fields preserve graph cwd/config and the optional artifact path without shell parsing.
|
|
18
|
+
* @evidence contracts/common.md#clear-and-simple-design This builder is the actual facade's single launch-vector owner for serve.
|
|
19
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Existing supported flags are mapped directly; there is no shell command or fixture branch.
|
|
20
|
+
* @evidence contracts/common.md#meaningful-documentation The native headline states the protocol and the optional coordinate's absence meaning.
|
|
21
|
+
* @evidence contracts/portability.md#os-neutral-implementation Node spawn consumes the returned argv vector with native coordinate strings intact; no slash rewriting or shell quoting interprets paths, manifests or contexts.
|
|
22
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources returns a fresh value and keeps no handle or state.
|
|
23
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms concatenates a fixed argv with at most one optional pair, in constant work.
|
|
24
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work builds one argv per spawn.
|
|
25
|
+
*/
|
|
26
|
+
export function serve(
|
|
27
|
+
cwd: string,
|
|
28
|
+
tsconfig: string,
|
|
29
|
+
artifacts: string | null,
|
|
30
|
+
): string[] {
|
|
31
|
+
return [
|
|
32
|
+
"serve",
|
|
33
|
+
"--cwd",
|
|
34
|
+
cwd,
|
|
35
|
+
"--tsconfig",
|
|
36
|
+
tsconfig,
|
|
37
|
+
...(artifacts === null ? [] : ["--artifacts", artifacts]),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Lint daemon argv; absent context retains the context-free rule contract.
|
|
43
|
+
*
|
|
44
|
+
* @evidence contracts/common.md#principled-implementation Equals-form argv fields retain the sidecar manifest and optional context as one native argument each.
|
|
45
|
+
* @evidence contracts/common.md#clear-and-simple-design This builder is the actual facade's single launch-vector owner for lint.
|
|
46
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Existing supported flags are mapped directly; there is no shell command or fixture branch.
|
|
47
|
+
* @evidence contracts/common.md#meaningful-documentation The native headline states the protocol and the optional coordinate's absence meaning.
|
|
48
|
+
* @evidence contracts/portability.md#os-neutral-implementation Node spawn consumes the returned argv vector with native coordinate strings intact; no slash rewriting or shell quoting interprets paths, manifests or contexts.
|
|
49
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources returns a fresh value and keeps no handle or state.
|
|
50
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms concatenates a fixed argv with at most one optional entry, in constant work.
|
|
51
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work builds one argv per spawn.
|
|
52
|
+
*/
|
|
53
|
+
export function lint(
|
|
54
|
+
cwd: string,
|
|
55
|
+
tsconfig: string,
|
|
56
|
+
manifest: string,
|
|
57
|
+
context?: string,
|
|
58
|
+
): string[] {
|
|
59
|
+
return [
|
|
60
|
+
"lsp-serve",
|
|
61
|
+
`--cwd=${cwd}`,
|
|
62
|
+
`--tsconfig=${tsconfig}`,
|
|
63
|
+
`--plugins-json=${manifest}`,
|
|
64
|
+
...(context === undefined ? [] : [`--project-context-json=${context}`]),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1,7 +1,23 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* The structured components carried by a graph symbol id.
|
|
3
|
+
*
|
|
4
|
+
* @evidence contracts/common.md#principled-implementation Decoded path and name preserve quoted identity components; optional kind supports legacy ids without a suffix.
|
|
5
|
+
* @evidence contracts/common.md#clear-and-simple-design Three components isolate identity grammar from graph node payloads.
|
|
6
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Legacy kind absence is explicit rather than replaced by a guessed declaration category.
|
|
7
|
+
* @evidence contracts/common.md#meaningful-documentation Native member comments describe decoded components and the legacy optional suffix.
|
|
8
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources data shape only: it holds no handle, task or retained state.
|
|
9
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms data shape only: it contains no loop or algorithm.
|
|
10
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work data shape only: it computes nothing another request could share.
|
|
11
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation data shape only: it touches no file, path or process.
|
|
12
|
+
*/
|
|
2
13
|
export interface ITtscGraphNodeId {
|
|
14
|
+
/** Unquoted producer path coordinate. */
|
|
3
15
|
path: string;
|
|
16
|
+
|
|
17
|
+
/** Unquoted simple or owner-qualified declaration name. */
|
|
4
18
|
name: string;
|
|
19
|
+
|
|
20
|
+
/** Declaration kind suffix, absent on accepted legacy identities. */
|
|
5
21
|
kind?: string;
|
|
6
22
|
}
|
|
7
23
|
|
|
@@ -10,6 +26,17 @@ export interface ITtscGraphNodeId {
|
|
|
10
26
|
*
|
|
11
27
|
* The producer quotes `#` and `\\` inside path and name. This reader also
|
|
12
28
|
* accepts pre-codec ordinary ids, so a current package can read an older dump.
|
|
29
|
+
*
|
|
30
|
+
* Returns undefined for a missing unquoted separator or empty name/kind.
|
|
31
|
+
*
|
|
32
|
+
* @evidence contracts/common.md#principled-implementation Escape-parity locates the component separator, then the final colon and inverse quoting recover identity parts without position dependence.
|
|
33
|
+
* @evidence contracts/common.md#clear-and-simple-design Separator discovery and quoting are private codec helpers; this function owns assembling the parsed record.
|
|
34
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Legacy UNC handling preserves a supported older encoding rather than guessing identity from basename or source position.
|
|
35
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose documents quoting, legacy support and malformed-input absence before acknowledgment tags.
|
|
36
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources returns a small record and retains nothing.
|
|
37
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms one scan for the unescaped hash, one lastIndexOf and one unescape pass, linear in the id length.
|
|
38
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work pure per-call work with no cache, so there is nothing to share or invalidate.
|
|
39
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation decodes an id string whose path component is text that is never resolved or opened.
|
|
13
40
|
*/
|
|
14
41
|
export function parseTtscGraphNodeId(id: string): ITtscGraphNodeId | undefined {
|
|
15
42
|
const hash = graphNodeIdHash(id);
|
|
@@ -25,7 +52,21 @@ export function parseTtscGraphNodeId(id: string): ITtscGraphNodeId | undefined {
|
|
|
25
52
|
};
|
|
26
53
|
}
|
|
27
54
|
|
|
28
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Encode a symbol identity without making its component boundaries ambiguous.
|
|
57
|
+
*
|
|
58
|
+
* Quotes backslashes and hashes in path/name before adding the hash separator
|
|
59
|
+
* and kind suffix. The caller supplies a supported declaration kind.
|
|
60
|
+
*
|
|
61
|
+
* @evidence contracts/common.md#principled-implementation Escaping delimiter characters before concatenation makes path/name boundaries recoverable by the corresponding parser.
|
|
62
|
+
* @evidence contracts/common.md#clear-and-simple-design One writer shares a quoting helper for both components.
|
|
63
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Identity uses actual path/name/kind rather than shortened fixture-specific aliases.
|
|
64
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose identifies quoting and the kind precondition without exposing implementation bodies.
|
|
65
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources returns a string and retains nothing.
|
|
66
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms two replaceAll passes over path and name, linear in their length.
|
|
67
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work pure per-call work with no cache, so there is nothing to share or invalidate.
|
|
68
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation quotes path text without resolving it or touching the filesystem.
|
|
69
|
+
*/
|
|
29
70
|
export function writeTtscGraphNodeId(
|
|
30
71
|
path: string,
|
|
31
72
|
name: string,
|
|
@@ -34,7 +75,20 @@ export function writeTtscGraphNodeId(
|
|
|
34
75
|
return `${escapeGraphNodeIdPart(path)}#${escapeGraphNodeIdPart(name)}:${kind}`;
|
|
35
76
|
}
|
|
36
77
|
|
|
37
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Return the unquoted path component when id is a symbol identity.
|
|
80
|
+
*
|
|
81
|
+
* Malformed ids return undefined under the shared parser's rules.
|
|
82
|
+
*
|
|
83
|
+
* @evidence contracts/common.md#principled-implementation Delegating to the full parser preserves escape and legacy grammar before selecting its path.
|
|
84
|
+
* @evidence contracts/common.md#clear-and-simple-design The adapter adds no independent delimiter parsing policy.
|
|
85
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Paths are not sliced at the first raw hash, which may be quoted identity content.
|
|
86
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states decoded output and malformed-input absence.
|
|
87
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources returns a string and retains nothing.
|
|
88
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms delegates to the id parser and reads one field.
|
|
89
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work pure per-call work with no cache, so there is nothing to share or invalidate.
|
|
90
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation extracts the path component of an id as text without resolving it.
|
|
91
|
+
*/
|
|
38
92
|
export function ttscGraphNodeIdPath(id: string): string | undefined {
|
|
39
93
|
return parseTtscGraphNodeId(id)?.path;
|
|
40
94
|
}
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { ITtscGraphDump } from "../structures/ITtscGraphDump";
|
|
2
|
+
import { ITtscGraphEdge } from "../structures/ITtscGraphEdge";
|
|
3
|
+
import { ITtscGraphEvidence } from "../structures/ITtscGraphEvidence";
|
|
4
|
+
import { ITtscGraphNode } from "../structures/ITtscGraphNode";
|
|
5
|
+
import { ITtscGraphSpan } from "../structures/ITtscGraphSpan";
|
|
6
|
+
import { isArtifactNodeKind } from "../structures/TtscGraphArtifactNodeKind";
|
|
7
|
+
import { ttscGraphNodeIdPath } from "./TtscGraphNodeId";
|
|
8
|
+
import { TtscGraphReadonly } from "./TtscGraphReadonly";
|
|
9
|
+
import { copyGraphSnapshot } from "./copyGraphSnapshot";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Immutable structural projection components for validated resident facts.
|
|
13
|
+
*
|
|
14
|
+
* Node refinement and containment depend on all declarations in the same file;
|
|
15
|
+
* export reanchoring depends on the source node's module declaration. Grouping
|
|
16
|
+
* both raw nodes and source-owned edges by that file captures these
|
|
17
|
+
* dependencies, including metadata/artifact nodes sharing it. Global metadata
|
|
18
|
+
* invalidates all components, and complete generation order is reconstructed
|
|
19
|
+
* independently.
|
|
20
|
+
*
|
|
21
|
+
* @evidence contracts/common.md#principled-implementation Frozen raw reference sequences prove unchanged component inputs; all same-file declarations and source edges participate, so lexical collisions, module folding and artifact parents cannot borrow stale derived facts.
|
|
22
|
+
* @evidence contracts/common.md#clear-and-simple-design Full synthesis remains one implementation used by cold construction and invalidated components; create owns dependency partitioning and immutable component reuse.
|
|
23
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The store still validates the complete generation first. No content witness, owner or raw edge is skipped to obtain reuse, and public DTOs are never frozen by borrowing.
|
|
24
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains actual synthesis dependencies, metadata invalidation and generation ordering rather than claiming universally delta-only work.
|
|
25
|
+
* @evidence contracts/performance.md#efficient-algorithms Grouping and generation ordering scan all raw references. Only changed file components synthesize outer records and detach/freeze nested facts once; metadata changes rebuild all. Reference arrays/maps remain linear in generation size.
|
|
26
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Equal frozen node/edge sequences and equal project/config/producer/capability/universe metadata retain the component. Cross-file edge changes invalidate their owning component; target absence remains a store validation error.
|
|
27
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Returned state retains only current file components and generation arrays; deleted components are omitted and no previous State reference is stored.
|
|
28
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation In-memory fact coordinates and reference identity select reuse; no native filesystem or process operation runs here.
|
|
29
|
+
*/
|
|
30
|
+
export namespace TtscGraphProjection {
|
|
31
|
+
/**
|
|
32
|
+
* Current projection and the inputs qualifying each immutable component.
|
|
33
|
+
*
|
|
34
|
+
* @evidence contracts/common.md#principled-implementation The metadata witness and complete raw reference sequences distinguish reusable components from invalidated ones; generation arrays preserve global order.
|
|
35
|
+
* @evidence contracts/common.md#clear-and-simple-design One current state groups dependency witnesses and projected output; it contains no previous-state link.
|
|
36
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Component validity is established by create after store validation rather than assumed from a file name.
|
|
37
|
+
* @evidence contracts/common.md#meaningful-documentation The headline describes the dependency witness; member comments distinguish its coordinates and ordered outputs.
|
|
38
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms State describes data; create owns scanning and synthesis.
|
|
39
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work State carries witnesses; create decides whether they qualify reuse.
|
|
40
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources State owns no lifecycle operation; its model and session control lifetime.
|
|
41
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation In-memory data only, without native coordinates being opened or resolved.
|
|
42
|
+
*/
|
|
43
|
+
export interface State {
|
|
44
|
+
/** Project, build-universe and producer authority for these components. */
|
|
45
|
+
readonly metadata: string;
|
|
46
|
+
|
|
47
|
+
/** Complete same-file synthesis inputs and their projected immutable facts. */
|
|
48
|
+
readonly components: ReadonlyMap<string, Component>;
|
|
49
|
+
|
|
50
|
+
/** Non-module declarations followed by file containers in full-model order. */
|
|
51
|
+
readonly nodes: readonly TtscGraphReadonly<ITtscGraphNode>[];
|
|
52
|
+
|
|
53
|
+
/** Original relation order followed by synthesized ownership relations. */
|
|
54
|
+
readonly edges: readonly TtscGraphReadonly<ITtscGraphEdge>[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Detach a caller's full dump before exposing synthesized immutable facts.
|
|
59
|
+
*
|
|
60
|
+
* Synthesis borrows nested facts synchronously and changes only new outer
|
|
61
|
+
* records. Final recursive copying detaches those facts before freezing or
|
|
62
|
+
* exposing the result, for mutable callers and frozen resident inputs alike.
|
|
63
|
+
*
|
|
64
|
+
* @evidence contracts/common.md#principled-implementation Readonly borrowed synthesis inputs preserve caller ownership; only fresh outer records change, and final recursive copying isolates every nested record before freezing and publication.
|
|
65
|
+
* @evidence contracts/common.md#clear-and-simple-design Cold models and invalidated components use the same synthesis implementation.
|
|
66
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Native export relations remain authoritative and no caller-owned DTO is frozen in place.
|
|
67
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states caller ownership, synchronous borrowing, construction-local changes and the final immutable publication boundary.
|
|
68
|
+
* @evidence contracts/performance.md#efficient-algorithms Synthesis scans facts and indexes owner handles; only colliding handles require candidate scans. One final recursive copy costs the selected fact population's nested bytes, without an intermediate mutable DTO copy.
|
|
69
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation constructs a supplied population; create selects components requiring construction.
|
|
70
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources The returned frozen facts transfer to the model/component owner; no historical state is retained here.
|
|
71
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation In-memory synthesis and copying perform no native operations.
|
|
72
|
+
*/
|
|
73
|
+
export function full(
|
|
74
|
+
dump: TtscGraphReadonly<Pick<ITtscGraphDump, "nodes" | "edges">>,
|
|
75
|
+
) {
|
|
76
|
+
return copyGraphSnapshot(synthesize(dump));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Reuse only components with the same complete synthesis input sequence.
|
|
81
|
+
*
|
|
82
|
+
* The caller supplies store-owned frozen facts after complete validation.
|
|
83
|
+
* Same-file declarations determine lexical owners and module anchors; source
|
|
84
|
+
* edges belong to that same component. Output ordering remains
|
|
85
|
+
* generation-wide.
|
|
86
|
+
*
|
|
87
|
+
* @evidence contracts/common.md#principled-implementation Complete ordered raw inputs and global metadata qualify each component. Synthesis dependencies are local to a node's file, while cross-file edge target existence has already been validated by the store.
|
|
88
|
+
* @evidence contracts/common.md#clear-and-simple-design Partitioning, component selection and global output ordering are separate stages; full owns the shared synthesis implementation.
|
|
89
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Every raw node and source edge participates; no ownership, provenance or manifest validation is skipped and no native relation is invented.
|
|
90
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain the frozen-input premise and why same-file declarations and source edges form the dependency boundary.
|
|
91
|
+
* @evidence contracts/performance.md#efficient-algorithms Linear reference grouping/equality and generation ordering remain necessary. Invalidated components borrow nested inputs during synthesis and detach/freeze once at full's publication boundary, with temporary maps/arrays proportional to current facts.
|
|
92
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Exact raw reference sequences plus project/config/schema/capability/producer/universe metadata retain equivalent components. Changed owner declarations or source relations invalidate the whole dependent file component.
|
|
93
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources New state retains only current components and output arrays; deleted components disappear and no previous state is linked.
|
|
94
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Fact coordinates select in-memory groups; no filesystem or process operation interprets them.
|
|
95
|
+
*/
|
|
96
|
+
export function create(
|
|
97
|
+
dump: TtscGraphReadonly<ITtscGraphDump>,
|
|
98
|
+
previous?: State,
|
|
99
|
+
): State {
|
|
100
|
+
const metadata = JSON.stringify({
|
|
101
|
+
project: dump.project,
|
|
102
|
+
tsconfig: dump.tsconfig,
|
|
103
|
+
schemaVersion: dump.provenance.schemaVersion,
|
|
104
|
+
capabilities: dump.provenance.capabilities,
|
|
105
|
+
producer: dump.provenance.producer,
|
|
106
|
+
artifactProducer: dump.provenance.artifactProducer,
|
|
107
|
+
universe: dump.provenance.universe,
|
|
108
|
+
});
|
|
109
|
+
const inputs = new Map<string, { nodes: RawNode[]; edges: RawEdge[] }>();
|
|
110
|
+
const files = new Map<string, string>();
|
|
111
|
+
for (const node of dump.nodes) {
|
|
112
|
+
let input = inputs.get(node.file);
|
|
113
|
+
if (input === undefined) {
|
|
114
|
+
input = { nodes: [], edges: [] };
|
|
115
|
+
inputs.set(node.file, input);
|
|
116
|
+
}
|
|
117
|
+
input.nodes.push(node);
|
|
118
|
+
files.set(node.id, node.file);
|
|
119
|
+
}
|
|
120
|
+
for (const edge of dump.edges)
|
|
121
|
+
inputs.get(files.get(edge.from)!)!.edges.push(edge);
|
|
122
|
+
const components = new Map<string, Component>();
|
|
123
|
+
for (const [file, input] of inputs) {
|
|
124
|
+
const old =
|
|
125
|
+
metadata === previous?.metadata
|
|
126
|
+
? previous.components.get(file)
|
|
127
|
+
: undefined;
|
|
128
|
+
if (
|
|
129
|
+
old !== undefined &&
|
|
130
|
+
same(input.nodes, old.rawNodes) &&
|
|
131
|
+
same(input.edges, old.rawEdges)
|
|
132
|
+
) {
|
|
133
|
+
components.set(file, old);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const projected = full(input);
|
|
137
|
+
const count = input.nodes.filter((node) => node.kind !== "module").length;
|
|
138
|
+
components.set(file, {
|
|
139
|
+
rawNodes: Object.freeze(input.nodes),
|
|
140
|
+
rawEdges: Object.freeze(input.edges),
|
|
141
|
+
nodes: new Map(
|
|
142
|
+
projected.nodes.slice(0, count).map((node) => [node.id, node]),
|
|
143
|
+
),
|
|
144
|
+
file: projected.nodes[count],
|
|
145
|
+
edges: new Map(
|
|
146
|
+
input.edges.map((edge, index) => [edge, projected.edges[index]!]),
|
|
147
|
+
),
|
|
148
|
+
structural: new Map(
|
|
149
|
+
projected.edges
|
|
150
|
+
.slice(input.edges.length)
|
|
151
|
+
.map((edge) => [edge.to, edge]),
|
|
152
|
+
),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const nodes: TtscGraphReadonly<ITtscGraphNode>[] = [];
|
|
156
|
+
const structural: TtscGraphReadonly<ITtscGraphEdge>[] = [];
|
|
157
|
+
const containers = new Set<string>();
|
|
158
|
+
for (const node of dump.nodes) {
|
|
159
|
+
if (node.kind === "module") continue;
|
|
160
|
+
const component = components.get(node.file)!;
|
|
161
|
+
nodes.push(component.nodes.get(node.id)!);
|
|
162
|
+
const edge = component.structural.get(node.id);
|
|
163
|
+
if (edge !== undefined) structural.push(edge);
|
|
164
|
+
if (!node.external && !isArtifactNodeKind(node.kind) && node.file !== "")
|
|
165
|
+
containers.add(node.file);
|
|
166
|
+
}
|
|
167
|
+
for (const node of dump.nodes)
|
|
168
|
+
if (node.kind === "module" && node.file !== "") containers.add(node.file);
|
|
169
|
+
for (const file of containers) nodes.push(components.get(file)!.file!);
|
|
170
|
+
const edges = dump.edges.map(
|
|
171
|
+
(edge) => components.get(files.get(edge.from)!)!.edges.get(edge)!,
|
|
172
|
+
);
|
|
173
|
+
for (const edge of structural) edges.push(edge);
|
|
174
|
+
return Object.freeze({
|
|
175
|
+
metadata,
|
|
176
|
+
components,
|
|
177
|
+
nodes: Object.freeze(nodes),
|
|
178
|
+
edges: Object.freeze(edges),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type RawNode = TtscGraphReadonly<ITtscGraphDump.INode>;
|
|
184
|
+
type RawEdge = TtscGraphReadonly<ITtscGraphDump.IEdge>;
|
|
185
|
+
/** Fresh outer node whose borrowed nested facts stay readonly until detachment. */
|
|
186
|
+
type SynthesizedNode = Omit<TtscGraphReadonly<ITtscGraphNode>, "kind"> & {
|
|
187
|
+
kind: ITtscGraphNode["kind"];
|
|
188
|
+
};
|
|
189
|
+
/** One file's complete immutable raw dependencies and derived output. */
|
|
190
|
+
interface Component {
|
|
191
|
+
readonly rawNodes: readonly RawNode[];
|
|
192
|
+
readonly rawEdges: readonly RawEdge[];
|
|
193
|
+
readonly nodes: ReadonlyMap<string, TtscGraphReadonly<ITtscGraphNode>>;
|
|
194
|
+
readonly file: TtscGraphReadonly<ITtscGraphNode> | undefined;
|
|
195
|
+
readonly edges: ReadonlyMap<RawEdge, TtscGraphReadonly<ITtscGraphEdge>>;
|
|
196
|
+
readonly structural: ReadonlyMap<string, TtscGraphReadonly<ITtscGraphEdge>>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Reference order is part of synthesis and query result identity. */
|
|
200
|
+
function same<T>(left: readonly T[], right: readonly T[]): boolean {
|
|
201
|
+
return (
|
|
202
|
+
left.length === right.length &&
|
|
203
|
+
left.every((value, index) => value === right[index])
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Append to a construction-local bucket; published outputs are frozen later. */
|
|
208
|
+
function push<K, V>(map: Map<K, V[]>, key: K, value: V): void {
|
|
209
|
+
const bucket = map.get(key);
|
|
210
|
+
if (bucket) bucket.push(value);
|
|
211
|
+
else map.set(key, [value]);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The within-file identity of a node: its owner-qualified name when it has one
|
|
216
|
+
* (`Class.method`), else its simple name. Merged declarations can share this
|
|
217
|
+
* handle; declaration ranges disambiguate their member ownership.
|
|
218
|
+
*/
|
|
219
|
+
function keyOf(node: TtscGraphReadonly<ITtscGraphNode>): string {
|
|
220
|
+
return node.qualifiedName ?? node.name;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* The owner key derived from facts the producer serialized separately.
|
|
225
|
+
*
|
|
226
|
+
* A quoted member named `"a.b"` has Name `a.b` and QualifiedName `Box.a.b`.
|
|
227
|
+
* Cutting the qualified name at its last dot invents owner `Box.a`; removing
|
|
228
|
+
* the exact `.${name}` suffix instead preserves the producer's real boundary.
|
|
229
|
+
* Object keys containing dots, brackets or no characters carry a JSON bracket
|
|
230
|
+
* suffix, keeping their identity distinct from nested object ownership.
|
|
231
|
+
*/
|
|
232
|
+
function ownerKey(node: TtscGraphReadonly<ITtscGraphNode>): string | undefined {
|
|
233
|
+
if (node.qualifiedName === undefined || node.qualifiedName === node.name)
|
|
234
|
+
return undefined;
|
|
235
|
+
const suffix = `.${node.name}`;
|
|
236
|
+
let owner: string | undefined;
|
|
237
|
+
if (node.qualifiedName.endsWith(suffix)) {
|
|
238
|
+
owner = node.qualifiedName.slice(0, -suffix.length);
|
|
239
|
+
} else if (node.qualifiedName.endsWith('"]')) {
|
|
240
|
+
// The native producer brackets reserved/empty object keys using JSON. Find
|
|
241
|
+
// the opening string quote without treating an escaped quote inside the
|
|
242
|
+
// key as a boundary; JSON decoding accepts equivalent Unicode escapes.
|
|
243
|
+
const qualified = node.qualifiedName;
|
|
244
|
+
for (let index = qualified.length - 3; index > 0; index--) {
|
|
245
|
+
if (qualified[index] !== '"') continue;
|
|
246
|
+
let escapes = 0;
|
|
247
|
+
for (
|
|
248
|
+
let before = index - 1;
|
|
249
|
+
before >= 0 && qualified[before] === "\\";
|
|
250
|
+
before--
|
|
251
|
+
)
|
|
252
|
+
escapes++;
|
|
253
|
+
if (escapes % 2 !== 0) continue;
|
|
254
|
+
if (qualified[index - 1] !== "[") break;
|
|
255
|
+
try {
|
|
256
|
+
if (JSON.parse(qualified.slice(index, -1)) === node.name)
|
|
257
|
+
owner = qualified.slice(0, index - 1);
|
|
258
|
+
} catch {
|
|
259
|
+
// A malformed qualified coordinate proves no owner.
|
|
260
|
+
}
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return owner === "" ? undefined : owner;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Prove lexical ownership using complete same-file declaration coordinates. */
|
|
268
|
+
function enclosesDeclaration(
|
|
269
|
+
owner: TtscGraphReadonly<ITtscGraphNode>,
|
|
270
|
+
member: TtscGraphReadonly<ITtscGraphNode>,
|
|
271
|
+
): boolean {
|
|
272
|
+
const outer = owner.evidence;
|
|
273
|
+
const inner = member.evidence;
|
|
274
|
+
if (
|
|
275
|
+
outer === undefined ||
|
|
276
|
+
inner === undefined ||
|
|
277
|
+
outer.file !== inner.file ||
|
|
278
|
+
outer.startCol === undefined ||
|
|
279
|
+
outer.endLine === undefined ||
|
|
280
|
+
outer.endCol === undefined ||
|
|
281
|
+
inner.startCol === undefined ||
|
|
282
|
+
inner.endLine === undefined ||
|
|
283
|
+
inner.endCol === undefined
|
|
284
|
+
)
|
|
285
|
+
return false;
|
|
286
|
+
return (
|
|
287
|
+
(outer.startLine < inner.startLine ||
|
|
288
|
+
(outer.startLine === inner.startLine &&
|
|
289
|
+
outer.startCol <= inner.startCol)) &&
|
|
290
|
+
(outer.endLine > inner.endLine ||
|
|
291
|
+
(outer.endLine === inner.endLine && outer.endCol >= inner.endCol))
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** A file's id and node name from its dump path coordinate. */
|
|
296
|
+
function fileNodeId(file: string): string {
|
|
297
|
+
return file;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* A wire span with its file put back: the one the builder left out because the
|
|
302
|
+
* reader has it, or the one it kept because it could not be derived (an
|
|
303
|
+
* implementation in another file).
|
|
304
|
+
*/
|
|
305
|
+
function spanIn(
|
|
306
|
+
span: TtscGraphReadonly<ITtscGraphSpan>,
|
|
307
|
+
file: string,
|
|
308
|
+
): ITtscGraphEvidence {
|
|
309
|
+
return { ...span, file: span.file ?? file };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* The source file a node id names. An id is `path#Qualified.Name:kind`, and a
|
|
314
|
+
* file node's id is the path itself.
|
|
315
|
+
*/
|
|
316
|
+
function fileOfNodeId(id: string): string {
|
|
317
|
+
return ttscGraphNodeIdPath(id) ?? id;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function basename(file: string): string {
|
|
321
|
+
const slash = file.lastIndexOf("/");
|
|
322
|
+
return slash >= 0 ? file.slice(slash + 1) : file;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Derive the structural layer from a dump's faithful facts: refine class-member
|
|
327
|
+
* variables to properties, add a `file` node per workspace source, connect the
|
|
328
|
+
* `contains` ownership tree, and re-anchor compiler-owned `exports` edges.
|
|
329
|
+
*/
|
|
330
|
+
function synthesize(
|
|
331
|
+
dump: TtscGraphReadonly<Pick<ITtscGraphDump, "nodes" | "edges">>,
|
|
332
|
+
): {
|
|
333
|
+
nodes: TtscGraphReadonly<ITtscGraphNode>[];
|
|
334
|
+
edges: TtscGraphReadonly<ITtscGraphEdge>[];
|
|
335
|
+
} {
|
|
336
|
+
// A module node is the dump's name for a source file's export surface, and a
|
|
337
|
+
// file node is this layer's name for the same file. Fold the two: the module
|
|
338
|
+
// keeps its file present here even when the file declares nothing (a barrel),
|
|
339
|
+
// and its `exports` edges are re-anchored on the file id every other tool
|
|
340
|
+
// already traverses. What the module carried, the file now carries.
|
|
341
|
+
const moduleFiles = new Set(
|
|
342
|
+
dump.nodes.filter((n) => n.kind === "module").map((n) => n.file),
|
|
343
|
+
);
|
|
344
|
+
const moduleIds = new Map(
|
|
345
|
+
dump.nodes.filter((n) => n.kind === "module").map((n) => [n.id, n.file]),
|
|
346
|
+
);
|
|
347
|
+
// Create outer nodes so property refinement does not mutate the caller's dump, and
|
|
348
|
+
// put back the file the builder left out of every span: a node's span is in
|
|
349
|
+
// the node's file, an edge's span is in the file its `from` id names. The
|
|
350
|
+
// builder omits both because they are exactly reconstructible and they are not
|
|
351
|
+
// small — the two copies are 17% of the document, 55 MB of VS Code's 323 MB,
|
|
352
|
+
// paid again in the encode, the pipe, the parse and the validation. Nothing
|
|
353
|
+
// downstream of this line sees a span without its file.
|
|
354
|
+
const nodes: SynthesizedNode[] = dump.nodes.flatMap((n): SynthesizedNode[] => {
|
|
355
|
+
if (n.kind === "module") return [];
|
|
356
|
+
const { evidence, implementation, ...rest } = n;
|
|
357
|
+
return [
|
|
358
|
+
{
|
|
359
|
+
...rest,
|
|
360
|
+
kind: n.kind,
|
|
361
|
+
...(evidence !== undefined
|
|
362
|
+
? { evidence: spanIn(evidence, n.file) }
|
|
363
|
+
: {}),
|
|
364
|
+
...(implementation !== undefined
|
|
365
|
+
? { implementation: spanIn(implementation, n.file) }
|
|
366
|
+
: {}),
|
|
367
|
+
},
|
|
368
|
+
];
|
|
369
|
+
});
|
|
370
|
+
const edges: TtscGraphReadonly<ITtscGraphEdge>[] = dump.edges.map((edge) => {
|
|
371
|
+
const { evidence, ...rest } = edge;
|
|
372
|
+
const from = moduleIds.get(edge.from);
|
|
373
|
+
return {
|
|
374
|
+
...rest,
|
|
375
|
+
...(from !== undefined ? { from: fileNodeId(from) } : {}),
|
|
376
|
+
...(evidence !== undefined
|
|
377
|
+
? { evidence: spanIn(evidence, fileOfNodeId(edge.from)) }
|
|
378
|
+
: {}),
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Index workspace nodes by (file, within-file key) so ownership can resolve a
|
|
383
|
+
// member to its declaring class/namespace.
|
|
384
|
+
const byFileKey = new Map<string, SynthesizedNode[]>();
|
|
385
|
+
for (const node of nodes) {
|
|
386
|
+
if (!node.external) push(byFileKey, node.file + "\0" + keyOf(node), node);
|
|
387
|
+
}
|
|
388
|
+
const owners = new Map<SynthesizedNode, SynthesizedNode | undefined>();
|
|
389
|
+
const owner = (node: SynthesizedNode): SynthesizedNode | undefined => {
|
|
390
|
+
if (owners.has(node)) return owners.get(node);
|
|
391
|
+
const parent = ownerKey(node);
|
|
392
|
+
const candidates =
|
|
393
|
+
parent === undefined
|
|
394
|
+
? undefined
|
|
395
|
+
: byFileKey.get(node.file + "\0" + parent);
|
|
396
|
+
// A unique checker handle also owns merged namespace members outside its
|
|
397
|
+
// primary declaration span. Only a colliding handle needs lexical evidence.
|
|
398
|
+
// Ambiguous or incomplete ranges prove no declaration owner; retain file
|
|
399
|
+
// containment rather than assigning an arbitrary merged declaration.
|
|
400
|
+
const enclosing =
|
|
401
|
+
candidates?.length === 1
|
|
402
|
+
? candidates
|
|
403
|
+
: candidates?.filter((candidate) =>
|
|
404
|
+
enclosesDeclaration(candidate, node),
|
|
405
|
+
);
|
|
406
|
+
const selected = enclosing?.length === 1 ? enclosing[0] : undefined;
|
|
407
|
+
owners.set(node, selected);
|
|
408
|
+
return selected;
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// Refine: a `variable` whose owner is a class or interface is a property.
|
|
412
|
+
for (const node of nodes) {
|
|
413
|
+
if (node.kind !== "variable" || node.external) continue;
|
|
414
|
+
const parent = owner(node);
|
|
415
|
+
if (parent && (parent.kind === "class" || parent.kind === "interface")) {
|
|
416
|
+
node.kind = "property";
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// One file container node per distinct workspace source file, plus every file
|
|
421
|
+
// the dump saw an export surface on — a barrel declares nothing, so its only
|
|
422
|
+
// trace in the dump is its module node, and it is exactly the file a consumer
|
|
423
|
+
// imports the package from.
|
|
424
|
+
const fileNodes = new Map<string, ITtscGraphNode>();
|
|
425
|
+
const addFileNode = (file: string): void => {
|
|
426
|
+
if (file === "" || fileNodes.has(file)) return;
|
|
427
|
+
fileNodes.set(file, {
|
|
428
|
+
id: fileNodeId(file),
|
|
429
|
+
kind: "file",
|
|
430
|
+
name: basename(file),
|
|
431
|
+
file,
|
|
432
|
+
external: false,
|
|
433
|
+
});
|
|
434
|
+
};
|
|
435
|
+
for (const node of nodes) {
|
|
436
|
+
if (node.external || isArtifactNodeKind(node.kind)) continue;
|
|
437
|
+
addFileNode(node.file);
|
|
438
|
+
}
|
|
439
|
+
for (const file of moduleFiles) addFileNode(file);
|
|
440
|
+
|
|
441
|
+
// Ownership: a member is contained by its owner; a top-level declaration by
|
|
442
|
+
// its file. Exports are not synthesized here: the dump's `exports` edges come
|
|
443
|
+
// from the checker's export table, which follows re-exports and barrels, so
|
|
444
|
+
// they say which module puts a symbol on the wire. Deriving them from the
|
|
445
|
+
// `exported` flag instead would say only that the declaring file made it
|
|
446
|
+
// public, which is the fact that cannot tell a package's front door from its
|
|
447
|
+
// legacy subpath.
|
|
448
|
+
const structural: ITtscGraphEdge[] = [];
|
|
449
|
+
for (const node of nodes) {
|
|
450
|
+
// An artifact is contained by the artifact its producer named, and by
|
|
451
|
+
// nothing when that producer named none. It is never contained by a `file`
|
|
452
|
+
// node: a document is already its own node, a Prisma address carries no
|
|
453
|
+
// path on purpose, and an API operation has no file at all.
|
|
454
|
+
if (isArtifactNodeKind(node.kind)) {
|
|
455
|
+
if (node.parent !== undefined && node.parent !== "")
|
|
456
|
+
structural.push({ from: node.parent, to: node.id, kind: "contains" });
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (node.external || node.file === "") continue;
|
|
460
|
+
const parent = owner(node);
|
|
461
|
+
const container = parent ? parent.id : fileNodeId(node.file);
|
|
462
|
+
structural.push({
|
|
463
|
+
from: container,
|
|
464
|
+
to: node.id,
|
|
465
|
+
kind: "contains",
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
nodes: [...nodes, ...fileNodes.values()],
|
|
471
|
+
edges: [...edges, ...structural],
|
|
472
|
+
};
|
|
473
|
+
}
|