@ttsc/graph 0.30.4 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -1,81 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import readline from "node:readline";
|
|
3
|
-
import typia from "typia";
|
|
1
|
+
import { CapabilityPluginResolver } from "ttsc";
|
|
4
2
|
|
|
5
3
|
import { ensureExecutable } from "../nativeExecutable";
|
|
6
4
|
import { resolveGraphBinary } from "../resolveGraphBinary";
|
|
7
|
-
import {
|
|
5
|
+
import type { IPublishedArtifactsResident } from "./IPublishedArtifactsResident";
|
|
6
|
+
import { TtscGraphLinePeer } from "./TtscGraphLinePeer";
|
|
8
7
|
import { TtscGraphMemory } from "./TtscGraphMemory";
|
|
9
|
-
import {
|
|
8
|
+
import { TtscGraphNativeArguments } from "./TtscGraphNativeArguments";
|
|
9
|
+
import { TtscGraphProtocol } from "./TtscGraphProtocol";
|
|
10
|
+
import type { TtscGraphRequestOptions } from "./TtscGraphRequestOptions";
|
|
11
|
+
import type { TtscGraphSessionOptions } from "./TtscGraphSessionOptions";
|
|
12
|
+
import { TtscGraphSessionState } from "./TtscGraphSessionState";
|
|
10
13
|
import { TtscLintDaemon } from "./TtscLintDaemon";
|
|
11
|
-
import { DUMP_SCHEMA_VERSION } from "./loadGraph";
|
|
12
14
|
import {
|
|
13
|
-
|
|
14
|
-
artifactsAreStale,
|
|
15
|
-
publishArtifacts,
|
|
15
|
+
artifactsAreStaleResident,
|
|
16
16
|
publishArtifactsResident,
|
|
17
17
|
} from "./publishedArtifacts";
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* The serve protocol version this client speaks.
|
|
21
|
-
*
|
|
22
|
-
* Keep it equal to `serveProtocolVersion` in
|
|
23
|
-
* `packages/ttsc/cmd/ttscgraph/serve.go`. The two are hand-synchronized, and
|
|
24
|
-
* `serve_protocol_version_matches_the_typescript_client_test.go` reads this
|
|
25
|
-
* constant out of this file and fails if the pair drifts.
|
|
26
|
-
*/
|
|
27
|
-
const PROTOCOL_VERSION = 1;
|
|
28
|
-
const GRAPH_SNAPSHOT_PROTOCOL_VERSION = 1;
|
|
29
|
-
const TERMINATION_GRACE_MS = 1_000;
|
|
30
|
-
|
|
31
|
-
interface Pending {
|
|
32
|
-
child: NativeChild;
|
|
33
|
-
resolve: (response: ITtscGraphSnapshot) => void;
|
|
34
|
-
reject: (error: Error) => void;
|
|
35
|
-
signal?: AbortSignal;
|
|
36
|
-
abort?: () => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface NativeChild {
|
|
40
|
-
process: ChildProcessWithoutNullStreams;
|
|
41
|
-
lines: readline.Interface;
|
|
42
|
-
stderr: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Construction options for a resident native graph session. */
|
|
46
|
-
export interface TtscGraphSessionOptions {
|
|
47
|
-
/** Project root passed to `ttscgraph serve`. */
|
|
48
|
-
cwd: string;
|
|
49
|
-
/** Project tsconfig passed to `ttscgraph serve`. */
|
|
50
|
-
tsconfig: string;
|
|
51
|
-
/** Absolute native binary path, resolved from `cwd` when omitted. */
|
|
52
|
-
binary?: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Per-call controls for a native graph refresh. */
|
|
56
|
-
export interface TtscGraphRequestOptions {
|
|
57
|
-
/** Cancel this refresh and retire its native session. */
|
|
58
|
-
signal?: AbortSignal;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
19
|
/**
|
|
62
20
|
* Resident bridge to `ttscgraph serve`.
|
|
63
21
|
*
|
|
64
|
-
*
|
|
65
|
-
* snapshot. Unchanged requests reuse the
|
|
66
|
-
* edited source reuses tsgo's resident
|
|
67
|
-
* config and root-file-set changes
|
|
22
|
+
* Requests admitted before a freshness check starts share that check and its
|
|
23
|
+
* snapshot. Later arrivals require another check. Unchanged requests reuse the
|
|
24
|
+
* existing {@link TtscGraphMemory}; an edited source reuses tsgo's resident
|
|
25
|
+
* Program through `driver.Session`, while config and root-file-set changes
|
|
26
|
+
* force a safe full reload.
|
|
27
|
+
*
|
|
28
|
+
* Cold publisher discovery, proof validation and direct publisher commands run
|
|
29
|
+
* through the SDK's asynchronous owner. Request-group cancellation and session
|
|
30
|
+
* shutdown join those producers before a later admission can publish.
|
|
31
|
+
*
|
|
32
|
+
* @evidence contracts/common.md#principled-implementation Validated versioned responses and atomic shard transactions preserve generation consistency while serialized requests correlate native replies by id.
|
|
33
|
+
* @evidence contracts/common.md#clear-and-simple-design This facade owns project/binary and artifact sidecars; the state owns queue/model/shards, the protocol owns generated decoding and the line adapter owns the native child.
|
|
34
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Compatible full dumps remain an explicit protocol fallback; malformed or mismatched responses retire the child rather than producing partial trusted facts.
|
|
35
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain resident reuse, reload boundaries and artifact ownership, while public methods describe request and shutdown behavior.
|
|
36
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms graph delegates admission and refresh to the state owner; this facade describes project, binary and artifact sidecar identity.
|
|
37
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work graph owns coordination of model/producer reuse and its invalidation decisions.
|
|
38
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources graph/close own actual acquisition and retirement; the class declaration describes the owner without a separate lifecycle operation.
|
|
39
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Native project resolution and process control are reviewed through graph and close, including their private helpers.
|
|
68
40
|
*/
|
|
69
41
|
export class TtscGraphSession {
|
|
70
42
|
private readonly cwd: string;
|
|
71
43
|
private readonly tsconfig: string;
|
|
72
44
|
private readonly binary: string;
|
|
73
|
-
private child: NativeChild | undefined;
|
|
74
|
-
private nextId = 0;
|
|
75
|
-
private readonly pending = new Map<number, Pending>();
|
|
76
|
-
private queue: Promise<void> = Promise.resolve();
|
|
77
|
-
private current: TtscGraphMemory | undefined;
|
|
78
|
-
private shardStore = new TtscGraphShardStore();
|
|
79
45
|
/**
|
|
80
46
|
* The artifact answer the resident child was last handed, and the state of
|
|
81
47
|
* the inputs it came from.
|
|
@@ -85,7 +51,9 @@ export class TtscGraphSession {
|
|
|
85
51
|
* still carries inputs, so that adding a publisher is something a running
|
|
86
52
|
* session can notice.
|
|
87
53
|
*/
|
|
88
|
-
private artifacts:
|
|
54
|
+
private artifacts: IPublishedArtifactsResident | undefined;
|
|
55
|
+
private readonly resolver = new CapabilityPluginResolver();
|
|
56
|
+
|
|
89
57
|
/**
|
|
90
58
|
* One resident `@ttsc/lint` sidecar per plugin binary, opened lazily.
|
|
91
59
|
*
|
|
@@ -96,7 +64,10 @@ export class TtscGraphSession {
|
|
|
96
64
|
* binary answering for the same project.
|
|
97
65
|
*/
|
|
98
66
|
private readonly daemons = new Map<string, TtscLintDaemon>();
|
|
99
|
-
private
|
|
67
|
+
private readonly daemonIdentities = new Map<string, string>();
|
|
68
|
+
private readonly state: TtscGraphSessionState;
|
|
69
|
+
private readonly shutdown = new AbortController();
|
|
70
|
+
private readonly retiredDaemons = new Set<Promise<void>>();
|
|
100
71
|
|
|
101
72
|
public constructor(options: TtscGraphSessionOptions) {
|
|
102
73
|
// Resolve the platform binary from the project this session serves, so the
|
|
@@ -115,129 +86,78 @@ export class TtscGraphSession {
|
|
|
115
86
|
this.cwd = options.cwd;
|
|
116
87
|
this.tsconfig = options.tsconfig;
|
|
117
88
|
this.binary = binary;
|
|
89
|
+
this.state = new TtscGraphSessionState({
|
|
90
|
+
open: (events) => this.open(events),
|
|
91
|
+
decode: TtscGraphProtocol.decode,
|
|
92
|
+
beforeRequest: (signal) => this.republishArtifacts(signal),
|
|
93
|
+
artifacts: () =>
|
|
94
|
+
this.artifacts === undefined ? undefined : (this.artifacts.file ?? ""),
|
|
95
|
+
close: async () => {
|
|
96
|
+
for (const daemon of this.daemons.values()) this.retireDaemon(daemon);
|
|
97
|
+
this.daemons.clear();
|
|
98
|
+
this.daemonIdentities.clear();
|
|
99
|
+
const results = await Promise.allSettled([
|
|
100
|
+
...this.retiredDaemons,
|
|
101
|
+
this.resolver.close(),
|
|
102
|
+
]);
|
|
103
|
+
this.artifacts = undefined;
|
|
104
|
+
const failures = results
|
|
105
|
+
.filter(
|
|
106
|
+
(result): result is PromiseRejectedResult =>
|
|
107
|
+
result.status === "rejected",
|
|
108
|
+
)
|
|
109
|
+
.map((result) => result.reason);
|
|
110
|
+
if (failures.length > 0)
|
|
111
|
+
throw new AggregateError(
|
|
112
|
+
failures,
|
|
113
|
+
"@ttsc/graph: sidecar shutdown failed",
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Return a graph refreshed after this caller's admission.
|
|
121
|
+
*
|
|
122
|
+
* Callers already waiting when a check starts share it. Cancellation rejects
|
|
123
|
+
* only that caller. The last cancellation aborts shared work; an outstanding
|
|
124
|
+
* native request then retires its peer. Closed sessions reject new requests
|
|
125
|
+
* and do not respawn.
|
|
126
|
+
*
|
|
127
|
+
* @evidence contracts/common.md#principled-implementation The state groups only callers admitted before its refresh starts, so every returned snapshot was validated after that caller's admission; independent cancellation cannot settle siblings.
|
|
128
|
+
* @evidence contracts/common.md#clear-and-simple-design The state owner handles admission, cancellation, response semantics and model replacement; facade callbacks synchronize artifacts and open the actual native transport.
|
|
129
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts A cancelled or closed request cannot be fulfilled from fabricated empty facts or restart a disposed session.
|
|
130
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states admission-based snapshot sharing, independent cancellation and closed-session behavior.
|
|
131
|
+
* @evidence contracts/portability.md#os-neutral-implementation Native binary resolution, argv spawning and Node termination APIs own host differences; project coordinates are never passed through a shell.
|
|
132
|
+
* @evidence contracts/performance.md#efficient-algorithms An admission group validates artifact inputs and a native frame once, then shares its model among live callers; changed frames additionally validate shards and rebuild indexes.
|
|
133
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Only callers admitted before validation starts share that validation. Later groups check again; unchanged responses reuse current memory, while changed generations replace it and changed artifacts are republished before requesting facts.
|
|
134
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources The session owns one graph child, current model, shard map and current publisher sidecars; republishing retires removed publishers, while pending/queued demand grows with submitted requests and completion/cancellation remove registrations.
|
|
135
|
+
*/
|
|
121
136
|
public graph(
|
|
122
137
|
options: TtscGraphRequestOptions = {},
|
|
123
138
|
): Promise<TtscGraphMemory> {
|
|
124
|
-
|
|
125
|
-
return Promise.reject(new Error("@ttsc/graph: native session is closed"));
|
|
126
|
-
}
|
|
127
|
-
let resolve!: (graph: TtscGraphMemory) => void;
|
|
128
|
-
let reject!: (error: Error) => void;
|
|
129
|
-
let started = false;
|
|
130
|
-
let settled = false;
|
|
131
|
-
const result = new Promise<TtscGraphMemory>((res, rej) => {
|
|
132
|
-
resolve = (graph) => {
|
|
133
|
-
if (settled) return;
|
|
134
|
-
settled = true;
|
|
135
|
-
res(graph);
|
|
136
|
-
};
|
|
137
|
-
reject = (error) => {
|
|
138
|
-
if (settled) return;
|
|
139
|
-
settled = true;
|
|
140
|
-
rej(error);
|
|
141
|
-
};
|
|
142
|
-
});
|
|
143
|
-
const cancelQueued = () => {
|
|
144
|
-
if (!started) reject(cancelledError(options.signal));
|
|
145
|
-
};
|
|
146
|
-
if (options.signal?.aborted) {
|
|
147
|
-
reject(cancelledError(options.signal));
|
|
148
|
-
return result;
|
|
149
|
-
}
|
|
150
|
-
options.signal?.addEventListener("abort", cancelQueued, { once: true });
|
|
151
|
-
this.queue = this.queue
|
|
152
|
-
.catch(() => undefined)
|
|
153
|
-
.then(async () => {
|
|
154
|
-
started = true;
|
|
155
|
-
options.signal?.removeEventListener("abort", cancelQueued);
|
|
156
|
-
if (settled) return;
|
|
157
|
-
try {
|
|
158
|
-
resolve(await this.refresh(options.signal));
|
|
159
|
-
} catch (error) {
|
|
160
|
-
reject(asError(error));
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
return result;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Close the native session. Safe to call more than once. */
|
|
167
|
-
public close(): void {
|
|
168
|
-
if (this.closed) return;
|
|
169
|
-
this.closed = true;
|
|
170
|
-
// The sidecars outlive nothing. A daemon is a child process this session
|
|
171
|
-
// opened, and a session that closed without stopping them would leave one
|
|
172
|
-
// resident Program per configured publisher alive for as long as the parent
|
|
173
|
-
// ran.
|
|
174
|
-
for (const daemon of this.daemons.values()) daemon.close();
|
|
175
|
-
this.daemons.clear();
|
|
176
|
-
const error = new Error("@ttsc/graph: native session closed");
|
|
177
|
-
if (this.child !== undefined) this.failChild(this.child, error);
|
|
178
|
-
else this.failPending(error);
|
|
139
|
+
return this.state.graph(options);
|
|
179
140
|
}
|
|
180
141
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
} else if (response.dump !== undefined) {
|
|
202
|
-
// A serve-protocol-v1 binary predating graph shard negotiation ignores
|
|
203
|
-
// the optional request field and retains the complete-dump response.
|
|
204
|
-
this.current = TtscGraphMemory.from(response.dump);
|
|
205
|
-
} else {
|
|
206
|
-
throw new Error(
|
|
207
|
-
`@ttsc/graph: native ${response.mode} response omitted both graph snapshot protocol v${String(GRAPH_SNAPSHOT_PROTOCOL_VERSION)} data and a compatible dump`,
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (this.current === undefined) {
|
|
212
|
-
throw new Error(
|
|
213
|
-
"@ttsc/graph: native session returned no initial graph snapshot",
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
return this.current;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private assertResponseSemantics(response: ITtscGraphSnapshot): void {
|
|
220
|
-
const bodies =
|
|
221
|
-
Number(response.dump !== undefined) +
|
|
222
|
-
Number(response.snapshot !== undefined);
|
|
223
|
-
let problem: string | undefined;
|
|
224
|
-
if (response.error !== undefined) {
|
|
225
|
-
if (response.mode !== "error" || response.changed || bodies !== 0) {
|
|
226
|
-
problem = "an error response carried snapshot state";
|
|
227
|
-
}
|
|
228
|
-
} else if (response.mode === "error") {
|
|
229
|
-
problem = "an error-mode response omitted its error";
|
|
230
|
-
} else if (response.changed) {
|
|
231
|
-
if (response.mode === "unchanged" || bodies !== 1) {
|
|
232
|
-
problem = "a changed response did not carry exactly one snapshot body";
|
|
233
|
-
}
|
|
234
|
-
} else if (response.mode !== "unchanged" || bodies !== 0) {
|
|
235
|
-
problem = "an unchanged response carried changed mode or snapshot state";
|
|
236
|
-
}
|
|
237
|
-
if (problem === undefined) return;
|
|
238
|
-
const error = new Error(`@ttsc/graph: native session returned ${problem}`);
|
|
239
|
-
if (this.child !== undefined) this.failChild(this.child, error);
|
|
240
|
-
throw error;
|
|
142
|
+
/**
|
|
143
|
+
* Close the native session. Safe to call more than once.
|
|
144
|
+
*
|
|
145
|
+
* Retires artifact sidecars, discovery worker and supervised publisher
|
|
146
|
+
* commands, and rejects pending native requests. Queued requests observe the
|
|
147
|
+
* closed state before they can create another child.
|
|
148
|
+
*
|
|
149
|
+
* @evidence contracts/common.md#principled-implementation The closed flag precedes sidecar disposal and child failure, so every pending or subsequent native operation observes retired ownership.
|
|
150
|
+
* @evidence contracts/common.md#clear-and-simple-design The explicit close path reuses failChild/failPending cleanup instead of a second cancellation implementation.
|
|
151
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Disposing a session cannot trigger a compensating respawn or pretend outstanding requests succeeded.
|
|
152
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose documents idempotence, pending rejection and queued-request behavior.
|
|
153
|
+
* @evidence contracts/portability.md#os-neutral-implementation Node child termination ends stdin, signals the process and escalates after a grace period without platform shell commands.
|
|
154
|
+
* @evidence contracts/performance.md#efficient-algorithms Shutdown visits sidecars and pending replies once; a delayed force-kill timer is cancelled when the child exits.
|
|
155
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Closure ends this owner's permission to reuse native Program, model and sidecars; subsequent graph calls reject.
|
|
156
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Shutdown aborts discovery and publisher commands, joins the resolver and sidecars, and drops opaque proofs. Native readers and reply listeners are removed, model/shards are released with child retirement, and graph-peer termination owns its finite grace timer.
|
|
157
|
+
*/
|
|
158
|
+
public close(): Promise<void> {
|
|
159
|
+
this.shutdown.abort(new Error("@ttsc/graph: native session closed"));
|
|
160
|
+
return this.state.close();
|
|
241
161
|
}
|
|
242
162
|
|
|
243
163
|
/**
|
|
@@ -261,317 +181,118 @@ export class TtscGraphSession {
|
|
|
261
181
|
* evaluation per verb. An already-cancelled request does not start one.
|
|
262
182
|
*/
|
|
263
183
|
private async republishArtifacts(signal?: AbortSignal): Promise<void> {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
184
|
+
const active =
|
|
185
|
+
signal === undefined
|
|
186
|
+
? this.shutdown.signal
|
|
187
|
+
: AbortSignal.any([signal, this.shutdown.signal]);
|
|
188
|
+
active.throwIfAborted();
|
|
189
|
+
await Promise.all(this.retiredDaemons);
|
|
190
|
+
active.throwIfAborted();
|
|
191
|
+
if (
|
|
192
|
+
this.artifacts !== undefined &&
|
|
193
|
+
!(await artifactsAreStaleResident(this.artifacts, { signal: active }))
|
|
194
|
+
)
|
|
195
|
+
return;
|
|
196
|
+
const publishers = new Set<string>();
|
|
197
|
+
let next: IPublishedArtifactsResident | undefined;
|
|
198
|
+
let failure: unknown;
|
|
199
|
+
try {
|
|
200
|
+
next = await publishArtifactsResident(
|
|
201
|
+
{ cwd: this.cwd, tsconfig: this.tsconfig, signal: active },
|
|
202
|
+
(plugin) => {
|
|
203
|
+
publishers.add(plugin.binary);
|
|
204
|
+
return this.daemon(plugin);
|
|
205
|
+
},
|
|
206
|
+
this.resolver,
|
|
207
|
+
);
|
|
208
|
+
for (const [binary, daemon] of this.daemons) {
|
|
209
|
+
if (publishers.has(binary)) continue;
|
|
210
|
+
await this.retireDaemon(daemon);
|
|
211
|
+
this.daemons.delete(binary);
|
|
212
|
+
this.daemonIdentities.delete(binary);
|
|
213
|
+
}
|
|
214
|
+
active.throwIfAborted();
|
|
215
|
+
// Successful absence replaces the old overlay too. The exact old proof
|
|
216
|
+
// is released before the new one becomes this session's authority.
|
|
217
|
+
await this.artifacts?.discovery.release();
|
|
218
|
+
active.throwIfAborted();
|
|
219
|
+
this.artifacts = next;
|
|
220
|
+
next = undefined;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
failure = error;
|
|
223
|
+
throw error;
|
|
224
|
+
} finally {
|
|
225
|
+
const closing: Promise<void>[] = [];
|
|
226
|
+
if (next !== undefined) closing.push(next.discovery.release());
|
|
227
|
+
if (active.aborted) {
|
|
228
|
+
for (const binary of publishers) {
|
|
229
|
+
const daemon = this.daemons.get(binary);
|
|
230
|
+
if (daemon !== undefined) closing.push(this.retireDaemon(daemon));
|
|
231
|
+
this.daemons.delete(binary);
|
|
232
|
+
this.daemonIdentities.delete(binary);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const results = await Promise.allSettled(closing);
|
|
236
|
+
const failures = results
|
|
237
|
+
.filter(
|
|
238
|
+
(result): result is PromiseRejectedResult =>
|
|
239
|
+
result.status === "rejected",
|
|
240
|
+
)
|
|
241
|
+
.map((result) => result.reason);
|
|
242
|
+
if (failures.length > 0)
|
|
243
|
+
throw new AggregateError(
|
|
244
|
+
[...(failure === undefined ? [] : [failure]), ...failures],
|
|
245
|
+
"@ttsc/graph: artifact cleanup failed",
|
|
246
|
+
);
|
|
247
|
+
}
|
|
280
248
|
}
|
|
281
249
|
|
|
282
250
|
/** The open sidecar for one plugin, opened on first use. */
|
|
283
|
-
private daemon(plugin: {
|
|
251
|
+
private async daemon(plugin: {
|
|
284
252
|
binary: string;
|
|
285
253
|
manifest: string;
|
|
286
254
|
projectContext?: string;
|
|
287
|
-
}): TtscLintDaemon {
|
|
255
|
+
}): Promise<TtscLintDaemon> {
|
|
256
|
+
this.shutdown.signal.throwIfAborted();
|
|
288
257
|
const open = this.daemons.get(plugin.binary);
|
|
289
|
-
|
|
258
|
+
const identity = JSON.stringify([plugin.manifest, plugin.projectContext]);
|
|
259
|
+
if (
|
|
260
|
+
open !== undefined &&
|
|
261
|
+
this.daemonIdentities.get(plugin.binary) === identity
|
|
262
|
+
)
|
|
263
|
+
return open;
|
|
264
|
+
if (open !== undefined) {
|
|
265
|
+
const closing = this.retireDaemon(open);
|
|
266
|
+
await closing;
|
|
267
|
+
}
|
|
268
|
+
this.shutdown.signal.throwIfAborted();
|
|
290
269
|
const created = new TtscLintDaemon(plugin, this.cwd, this.tsconfig);
|
|
291
270
|
this.daemons.set(plugin.binary, created);
|
|
271
|
+
this.daemonIdentities.set(plugin.binary, identity);
|
|
292
272
|
return created;
|
|
293
273
|
}
|
|
294
274
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
signal,
|
|
305
|
-
};
|
|
306
|
-
if (signal !== undefined) {
|
|
307
|
-
pending.abort = () =>
|
|
308
|
-
this.failChild(child, cancelledError(signal, child));
|
|
309
|
-
signal.addEventListener("abort", pending.abort, { once: true });
|
|
310
|
-
}
|
|
311
|
-
this.pending.set(id, pending);
|
|
312
|
-
if (signal?.aborted) {
|
|
313
|
-
pending.abort!();
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
child.process.stdin.write(
|
|
317
|
-
`${JSON.stringify({
|
|
318
|
-
id,
|
|
319
|
-
graphSnapshotVersion: GRAPH_SNAPSHOT_PROTOCOL_VERSION,
|
|
320
|
-
// Empty when the project publishes nothing, which withdraws whatever
|
|
321
|
-
// the server holds: a publisher the user removed must stop being
|
|
322
|
-
// answered with, and omitting the field instead would say only that
|
|
323
|
-
// this client has no opinion. Omitted only before a child exists,
|
|
324
|
-
// which no request reaches.
|
|
325
|
-
artifacts:
|
|
326
|
-
this.artifacts === undefined
|
|
327
|
-
? undefined
|
|
328
|
-
: (this.artifacts.file ?? ""),
|
|
329
|
-
})}\n`,
|
|
330
|
-
(error) => {
|
|
331
|
-
if (error === null || error === undefined) return;
|
|
332
|
-
if (this.pending.get(id) !== pending) return;
|
|
333
|
-
this.failChild(
|
|
334
|
-
child,
|
|
335
|
-
new Error(
|
|
336
|
-
`@ttsc/graph: could not request native snapshot: ${error.message}`,
|
|
337
|
-
),
|
|
338
|
-
);
|
|
339
|
-
},
|
|
340
|
-
);
|
|
341
|
-
});
|
|
275
|
+
/** Keep failures visible to shutdown, releasing fulfilled historical owners. */
|
|
276
|
+
private retireDaemon(daemon: TtscLintDaemon): Promise<void> {
|
|
277
|
+
const closing = daemon.close();
|
|
278
|
+
this.retiredDaemons.add(closing);
|
|
279
|
+
void closing.then(
|
|
280
|
+
() => this.retiredDaemons.delete(closing),
|
|
281
|
+
() => undefined,
|
|
282
|
+
);
|
|
283
|
+
return closing;
|
|
342
284
|
}
|
|
343
285
|
|
|
344
|
-
private
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
throw new Error("@ttsc/graph:
|
|
349
|
-
|
|
350
|
-
if (
|
|
351
|
-
this.child !== undefined &&
|
|
352
|
-
this.child.process.exitCode === null &&
|
|
353
|
-
this.child.process.signalCode === null
|
|
354
|
-
) {
|
|
355
|
-
return this.child;
|
|
356
|
-
}
|
|
357
|
-
const artifacts = publishArtifacts({
|
|
358
|
-
cwd: this.cwd,
|
|
359
|
-
tsconfig: this.tsconfig,
|
|
360
|
-
});
|
|
361
|
-
this.artifacts = artifacts;
|
|
362
|
-
const process = spawn(
|
|
286
|
+
private open(events: TtscGraphLinePeer.Events): TtscGraphLinePeer.Connection {
|
|
287
|
+
this.shutdown.signal.throwIfAborted();
|
|
288
|
+
const artifacts = this.artifacts;
|
|
289
|
+
if (artifacts === undefined)
|
|
290
|
+
throw new Error("@ttsc/graph: artifact publication was not admitted");
|
|
291
|
+
return TtscGraphLinePeer.open(
|
|
363
292
|
this.binary,
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
this.cwd,
|
|
368
|
-
"--tsconfig",
|
|
369
|
-
this.tsconfig,
|
|
370
|
-
...(artifacts.file === null ? [] : ["--artifacts", artifacts.file]),
|
|
371
|
-
],
|
|
372
|
-
{ stdio: ["pipe", "pipe", "pipe"], windowsHide: true },
|
|
373
|
-
);
|
|
374
|
-
const lines = readline.createInterface({ input: process.stdout });
|
|
375
|
-
const child: NativeChild = { process, lines, stderr: "" };
|
|
376
|
-
this.child = child;
|
|
377
|
-
process.stderr.setEncoding("utf8");
|
|
378
|
-
process.stderr.on("data", (chunk: string) => {
|
|
379
|
-
child.stderr = (child.stderr + chunk).slice(-64 * 1024);
|
|
380
|
-
});
|
|
381
|
-
lines.on("line", (line) => this.onLine(child, line));
|
|
382
|
-
process.on("error", (error) =>
|
|
383
|
-
this.failChild(
|
|
384
|
-
child,
|
|
385
|
-
new Error(`@ttsc/graph: native session failed: ${error.message}`),
|
|
386
|
-
),
|
|
293
|
+
TtscGraphNativeArguments.serve(this.cwd, this.tsconfig, artifacts.file),
|
|
294
|
+
events,
|
|
295
|
+
{ stderr: "capture" },
|
|
387
296
|
);
|
|
388
|
-
process.on("exit", (code, signal) => {
|
|
389
|
-
if (this.child !== child) return;
|
|
390
|
-
this.failChild(
|
|
391
|
-
child,
|
|
392
|
-
new Error(
|
|
393
|
-
`@ttsc/graph: native session exited (code=${String(code)}, signal=${String(signal)})${stderrSuffix(
|
|
394
|
-
child,
|
|
395
|
-
)}`,
|
|
396
|
-
),
|
|
397
|
-
false,
|
|
398
|
-
);
|
|
399
|
-
});
|
|
400
|
-
return child;
|
|
401
297
|
}
|
|
402
|
-
|
|
403
|
-
private onLine(child: NativeChild, line: string): void {
|
|
404
|
-
if (this.child !== child) return;
|
|
405
|
-
let parsed: unknown;
|
|
406
|
-
try {
|
|
407
|
-
parsed = JSON.parse(line);
|
|
408
|
-
} catch (error) {
|
|
409
|
-
this.failChild(
|
|
410
|
-
child,
|
|
411
|
-
new Error(
|
|
412
|
-
`@ttsc/graph: native session returned invalid JSON: ${asError(error).message}`,
|
|
413
|
-
),
|
|
414
|
-
);
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Read the version before the shape, because a server speaking another
|
|
419
|
-
// version is entitled to a different shape. Asserting first would report
|
|
420
|
-
// that mismatch as a field complaint — "expected string at $input.mode" —
|
|
421
|
-
// about a contract the other side never agreed to, which is the misparse
|
|
422
|
-
// this field exists to prevent. Ask what protocol it is first, then hold it
|
|
423
|
-
// to that protocol.
|
|
424
|
-
const version: number | undefined = typia.is<{ protocolVersion: number }>(
|
|
425
|
-
parsed,
|
|
426
|
-
)
|
|
427
|
-
? parsed.protocolVersion
|
|
428
|
-
: undefined;
|
|
429
|
-
if (version !== PROTOCOL_VERSION) {
|
|
430
|
-
// Session-wide: a version mismatch is not one bad frame, it is the wrong
|
|
431
|
-
// binary, and every request against it is equally doomed.
|
|
432
|
-
this.failChild(
|
|
433
|
-
child,
|
|
434
|
-
new Error(
|
|
435
|
-
`@ttsc/graph: ttscgraph speaks serve protocol ${
|
|
436
|
-
version === undefined ? "an unknown version" : `v${String(version)}`
|
|
437
|
-
}, this client speaks v${String(PROTOCOL_VERSION)}. ` +
|
|
438
|
-
"Install a matching `ttsc` (the binary resolves from the target " +
|
|
439
|
-
"project, or from TTSC_GRAPH_BINARY).",
|
|
440
|
-
),
|
|
441
|
-
);
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
let response: ITtscGraphSnapshot;
|
|
446
|
-
try {
|
|
447
|
-
// Validate the envelope, not just the dump it carries. The dump was
|
|
448
|
-
// typia-asserted while the envelope around it was a bare cast, so the
|
|
449
|
-
// fields the client actually branches on — the mode, and the id that
|
|
450
|
-
// routes the frame — were the unchecked ones. Anything added to the
|
|
451
|
-
// envelope belongs on this side of that line.
|
|
452
|
-
response = typia.assert<ITtscGraphSnapshot>(parsed);
|
|
453
|
-
} catch (error) {
|
|
454
|
-
this.failChild(
|
|
455
|
-
child,
|
|
456
|
-
new Error(
|
|
457
|
-
`@ttsc/graph: native session returned an unreadable response: ${asError(error).message}`,
|
|
458
|
-
),
|
|
459
|
-
);
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
// The envelope's version is not the body's, and only the envelope has been
|
|
463
|
-
// held to one so far. A producer can speak this protocol and still carry a
|
|
464
|
-
// dump from another schema — the two move apart the moment a node field is
|
|
465
|
-
// added without the frame around it changing — and then the facts that field
|
|
466
|
-
// holds are silently absent rather than refused. `literals` is exactly that
|
|
467
|
-
// shape: an older producer resolves no value set, so a union comes back
|
|
468
|
-
// looking like a type with no members. Hold the body to its own number too,
|
|
469
|
-
// once the frame is understood.
|
|
470
|
-
if (
|
|
471
|
-
(response.dump !== undefined &&
|
|
472
|
-
response.dump.provenance.schemaVersion !== DUMP_SCHEMA_VERSION) ||
|
|
473
|
-
(response.snapshot !== undefined &&
|
|
474
|
-
response.snapshot.schemaVersion !== DUMP_SCHEMA_VERSION)
|
|
475
|
-
) {
|
|
476
|
-
// Session-wide, for the same reason the protocol mismatch above is: it is
|
|
477
|
-
// the wrong binary, not one bad frame.
|
|
478
|
-
this.failChild(
|
|
479
|
-
child,
|
|
480
|
-
new Error(
|
|
481
|
-
`@ttsc/graph: ttscgraph sends dump schema v${String(
|
|
482
|
-
response.dump?.provenance.schemaVersion ??
|
|
483
|
-
response.snapshot?.schemaVersion,
|
|
484
|
-
)}, this client reads v${String(DUMP_SCHEMA_VERSION)}. ` +
|
|
485
|
-
"Install a matching `ttsc` (the binary resolves from the target " +
|
|
486
|
-
"project, or from TTSC_GRAPH_BINARY).",
|
|
487
|
-
),
|
|
488
|
-
);
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const pending = this.pending.get(response.id);
|
|
493
|
-
if (pending === undefined || pending.child !== child) return;
|
|
494
|
-
this.settlePending(response.id, pending, response);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
private failChild(child: NativeChild, error: Error, terminate = true): void {
|
|
498
|
-
if (this.child !== child) return;
|
|
499
|
-
this.child = undefined;
|
|
500
|
-
this.current = undefined;
|
|
501
|
-
this.shardStore = new TtscGraphShardStore();
|
|
502
|
-
child.lines.close();
|
|
503
|
-
this.failPending(error, child);
|
|
504
|
-
if (terminate) terminateChild(child.process);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
private failPending(error: Error, child?: NativeChild): void {
|
|
508
|
-
for (const [id, pending] of this.pending) {
|
|
509
|
-
if (child === undefined || pending.child === child) {
|
|
510
|
-
this.settlePending(id, pending, error);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
private settlePending(
|
|
516
|
-
id: number,
|
|
517
|
-
pending: Pending,
|
|
518
|
-
result: ITtscGraphSnapshot | Error,
|
|
519
|
-
): void {
|
|
520
|
-
if (this.pending.get(id) !== pending) return;
|
|
521
|
-
this.pending.delete(id);
|
|
522
|
-
if (pending.signal !== undefined && pending.abort !== undefined) {
|
|
523
|
-
pending.signal.removeEventListener("abort", pending.abort);
|
|
524
|
-
}
|
|
525
|
-
if (result instanceof Error) pending.reject(result);
|
|
526
|
-
else pending.resolve(result);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function terminateChild(child: ChildProcessWithoutNullStreams): void {
|
|
531
|
-
if (!child.stdin.destroyed) child.stdin.destroy();
|
|
532
|
-
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
533
|
-
try {
|
|
534
|
-
child.kill();
|
|
535
|
-
} catch {
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
const force = setTimeout(() => {
|
|
539
|
-
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
540
|
-
try {
|
|
541
|
-
child.kill("SIGKILL");
|
|
542
|
-
} catch {
|
|
543
|
-
// The process exited between the liveness check and the signal.
|
|
544
|
-
}
|
|
545
|
-
}, TERMINATION_GRACE_MS);
|
|
546
|
-
force.unref();
|
|
547
|
-
child.once("exit", () => clearTimeout(force));
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
function cancelledError(signal?: AbortSignal, child?: NativeChild): Error {
|
|
551
|
-
const error = new Error(
|
|
552
|
-
`@ttsc/graph: native snapshot request cancelled${abortDetail(signal)}${
|
|
553
|
-
child === undefined ? "" : stderrSuffix(child)
|
|
554
|
-
}`,
|
|
555
|
-
);
|
|
556
|
-
error.name = "AbortError";
|
|
557
|
-
return error;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
function abortDetail(signal?: AbortSignal): string {
|
|
561
|
-
const reason = signal?.reason;
|
|
562
|
-
if (reason === undefined) return "";
|
|
563
|
-
try {
|
|
564
|
-
return `: ${reason instanceof Error ? reason.message : String(reason)}`;
|
|
565
|
-
} catch {
|
|
566
|
-
return "";
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function stderrSuffix(child: NativeChild): string {
|
|
571
|
-
const stderr = child.stderr.trim();
|
|
572
|
-
return stderr === "" ? "" : `: ${stderr}`;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function asError(error: unknown): Error {
|
|
576
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
577
298
|
}
|