@ttsc/graph 0.30.3 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -30
- package/lib/CapturedProcessOutput.d.ts +47 -0
- package/lib/CapturedProcessOutput.js +3 -0
- package/lib/CapturedProcessOutput.js.map +1 -0
- package/lib/ILauncherOption.d.ts +22 -0
- package/lib/ILauncherOption.js +3 -0
- package/lib/ILauncherOption.js.map +1 -0
- package/lib/IProjectOptions.d.ts +18 -0
- package/lib/IProjectOptions.js +3 -0
- package/lib/IProjectOptions.js.map +1 -0
- package/lib/ParsedLauncherOptions.d.ts +13 -0
- package/lib/ParsedLauncherOptions.js +3 -0
- package/lib/ParsedLauncherOptions.js.map +1 -0
- package/lib/TtscGraphApplication.d.ts +38 -1
- package/lib/TtscGraphApplication.js +26 -1
- package/lib/TtscGraphApplication.js.map +1 -1
- package/lib/TtscGraphLauncherArguments.d.ts +115 -0
- package/lib/TtscGraphLauncherArguments.js +181 -0
- package/lib/TtscGraphLauncherArguments.js.map +1 -0
- package/lib/TtscGraphViewSnapshot.d.ts +35 -0
- package/lib/TtscGraphViewSnapshot.js +48 -0
- package/lib/TtscGraphViewSnapshot.js.map +1 -0
- package/lib/index.d.ts +9 -24
- package/lib/index.js +6 -128
- package/lib/index.js.map +1 -1
- package/lib/internal/GraphProcessTrace.d.ts +1 -0
- package/lib/internal/GraphProcessTrace.js +235 -0
- package/lib/internal/GraphProcessTrace.js.map +1 -0
- package/lib/launcherArgs.d.ts +75 -16
- package/lib/launcherArgs.js +77 -8
- package/lib/launcherArgs.js.map +1 -1
- package/lib/model/IArtifactDirectory.d.ts +26 -0
- package/lib/model/IArtifactDirectory.js +3 -0
- package/lib/model/IArtifactDirectory.js.map +1 -0
- package/lib/model/IArtifactInputs.d.ts +19 -0
- package/lib/model/IArtifactInputs.js +3 -0
- package/lib/model/IArtifactInputs.js.map +1 -0
- package/lib/model/IPublishedArtifacts.d.ts +76 -0
- package/lib/model/IPublishedArtifacts.js +3 -0
- package/lib/model/IPublishedArtifacts.js.map +1 -0
- package/lib/model/IPublishedArtifactsResident.d.ts +19 -0
- package/lib/model/IPublishedArtifactsResident.js +3 -0
- package/lib/model/IPublishedArtifactsResident.js.map +1 -0
- package/lib/model/ITtscLintDaemonTarget.d.ts +20 -0
- package/lib/model/ITtscLintDaemonTarget.js +3 -0
- package/lib/model/ITtscLintDaemonTarget.js.map +1 -0
- package/lib/model/TtscGraphLinePeer.d.ts +146 -0
- package/lib/model/TtscGraphLinePeer.js +213 -0
- package/lib/model/TtscGraphLinePeer.js.map +1 -0
- package/lib/model/TtscGraphMemory.d.ts +156 -16
- package/lib/model/TtscGraphMemory.js +228 -222
- package/lib/model/TtscGraphMemory.js.map +1 -1
- package/lib/model/TtscGraphNativeArguments.d.ts +40 -0
- package/lib/model/TtscGraphNativeArguments.js +64 -0
- package/lib/model/TtscGraphNativeArguments.js.map +1 -0
- package/lib/model/TtscGraphNodeId.d.ts +55 -3
- package/lib/model/TtscGraphNodeId.js +40 -2
- package/lib/model/TtscGraphNodeId.js.map +1 -1
- package/lib/model/TtscGraphProjection.d.ts +156 -0
- package/lib/model/TtscGraphProjection.js +390 -0
- package/lib/model/TtscGraphProjection.js.map +1 -0
- package/lib/model/TtscGraphProtocol.d.ts +28 -0
- package/lib/model/TtscGraphProtocol.js +928 -0
- package/lib/model/TtscGraphProtocol.js.map +1 -0
- package/lib/model/TtscGraphReadonly.d.ts +15 -0
- package/lib/model/TtscGraphReadonly.js +3 -0
- package/lib/model/TtscGraphReadonly.js.map +1 -0
- package/lib/model/TtscGraphRequestOptions.d.ts +16 -0
- package/lib/model/TtscGraphRequestOptions.js +3 -0
- package/lib/model/TtscGraphRequestOptions.js.map +1 -0
- package/lib/model/TtscGraphSession.d.ts +82 -36
- package/lib/model/TtscGraphSession.js +164 -1238
- package/lib/model/TtscGraphSession.js.map +1 -1
- package/lib/model/TtscGraphSessionOptions.d.ts +20 -0
- package/lib/model/TtscGraphSessionOptions.js +3 -0
- package/lib/model/TtscGraphSessionOptions.js.map +1 -0
- package/lib/model/TtscGraphSessionState.d.ts +187 -0
- package/lib/model/TtscGraphSessionState.js +417 -0
- package/lib/model/TtscGraphSessionState.js.map +1 -0
- package/lib/model/TtscGraphShardStore.d.ts +64 -3
- package/lib/model/TtscGraphShardStore.js +85 -15
- package/lib/model/TtscGraphShardStore.js.map +1 -1
- package/lib/model/TtscGraphSourceReader.d.ts +31 -8
- package/lib/model/TtscGraphSourceReader.js +37 -13
- package/lib/model/TtscGraphSourceReader.js.map +1 -1
- package/lib/model/TtscLintDaemon.d.ts +35 -17
- package/lib/model/TtscLintDaemon.js +39 -117
- package/lib/model/TtscLintDaemon.js.map +1 -1
- package/lib/model/TtscLintDaemonState.d.ts +69 -0
- package/lib/model/TtscLintDaemonState.js +166 -0
- package/lib/model/TtscLintDaemonState.js.map +1 -0
- package/lib/model/copyGraphRecords.d.ts +14 -0
- package/lib/model/copyGraphRecords.js +19 -0
- package/lib/model/copyGraphRecords.js.map +1 -0
- package/lib/model/copyGraphSnapshot.d.ts +17 -0
- package/lib/model/copyGraphSnapshot.js +33 -0
- package/lib/model/copyGraphSnapshot.js.map +1 -0
- package/lib/model/loadGraph.d.ts +19 -10
- package/lib/model/loadGraph.js +21 -12
- package/lib/model/loadGraph.js.map +1 -1
- package/lib/model/publishedArtifacts.d.ts +78 -97
- package/lib/model/publishedArtifacts.js +271 -85
- package/lib/model/publishedArtifacts.js.map +1 -1
- package/lib/nativeExecutable.d.ts +19 -8
- package/lib/nativeExecutable.js +75 -15
- package/lib/nativeExecutable.js.map +1 -1
- package/lib/reduce.d.ts +19 -45
- package/lib/reduce.js +19 -3
- package/lib/reduce.js.map +1 -1
- package/lib/resolveGraphBinary.d.ts +9 -0
- package/lib/resolveGraphBinary.js +11 -3
- package/lib/resolveGraphBinary.js.map +1 -1
- package/lib/runGraph.d.ts +21 -0
- package/lib/runGraph.js +83 -0
- package/lib/runGraph.js.map +1 -0
- package/lib/server/IResolvedGraphHandle.d.ts +22 -0
- package/lib/server/IResolvedGraphHandle.js +3 -0
- package/lib/server/IResolvedGraphHandle.js.map +1 -0
- package/lib/server/createServer.d.ts +14 -10
- package/lib/server/createServer.js +42 -39
- package/lib/server/createServer.js.map +1 -1
- package/lib/server/exportSurface.d.ts +23 -1
- package/lib/server/exportSurface.js +23 -1
- package/lib/server/exportSurface.js.map +1 -1
- package/lib/server/formatGraphFailure.d.ts +20 -0
- package/lib/server/formatGraphFailure.js +35 -0
- package/lib/server/formatGraphFailure.js.map +1 -0
- package/lib/server/pathPolicy.d.ts +70 -5
- package/lib/server/pathPolicy.js +66 -4
- package/lib/server/pathPolicy.js.map +1 -1
- package/lib/server/resolveHandle.d.ts +10 -5
- package/lib/server/resolveHandle.js +39 -7
- package/lib/server/resolveHandle.js.map +1 -1
- package/lib/server/resultAudit.d.ts +17 -14
- package/lib/server/resultAudit.js +56 -27
- package/lib/server/resultAudit.js.map +1 -1
- package/lib/server/resultNext.d.ts +26 -1
- package/lib/server/resultNext.js +14 -0
- package/lib/server/resultNext.js.map +1 -1
- package/lib/server/runDetails.d.ts +72 -15
- package/lib/server/runDetails.js +104 -69
- package/lib/server/runDetails.js.map +1 -1
- package/lib/server/runEntrypoints.d.ts +9 -0
- package/lib/server/runEntrypoints.js +9 -0
- package/lib/server/runEntrypoints.js.map +1 -1
- package/lib/server/runLookup.d.ts +13 -3
- package/lib/server/runLookup.js +19 -6
- package/lib/server/runLookup.js.map +1 -1
- package/lib/server/runOverview.d.ts +13 -4
- package/lib/server/runOverview.js +13 -4
- package/lib/server/runOverview.js.map +1 -1
- package/lib/server/runTour.d.ts +9 -0
- package/lib/server/runTour.js +33 -65
- package/lib/server/runTour.js.map +1 -1
- package/lib/server/runTrace.d.ts +25 -1
- package/lib/server/runTrace.js +133 -88
- package/lib/server/runTrace.js.map +1 -1
- package/lib/server/startServer.d.ts +16 -0
- package/lib/server/startServer.js +24 -1
- package/lib/server/startServer.js.map +1 -1
- package/lib/structures/ITtscGraphApplication.d.ts +95 -41
- package/lib/structures/ITtscGraphDecorator.d.ts +18 -0
- package/lib/structures/ITtscGraphDetails.d.ts +81 -6
- package/lib/structures/ITtscGraphDocTag.d.ts +17 -8
- package/lib/structures/ITtscGraphDump.d.ts +106 -8
- package/lib/structures/ITtscGraphEdge.d.ts +14 -2
- package/lib/structures/ITtscGraphEntrypoints.d.ts +81 -6
- package/lib/structures/ITtscGraphEscape.d.ts +24 -2
- package/lib/structures/ITtscGraphEvidence.d.ts +9 -0
- package/lib/structures/ITtscGraphLookup.d.ts +42 -9
- package/lib/structures/ITtscGraphNext.d.ts +12 -1
- package/lib/structures/ITtscGraphNode.d.ts +38 -11
- package/lib/structures/ITtscGraphOverview.d.ts +81 -6
- package/lib/structures/ITtscGraphSnapshot.d.ts +97 -2
- package/lib/structures/ITtscGraphSpan.d.ts +9 -0
- package/lib/structures/ITtscGraphTour.d.ts +83 -9
- package/lib/structures/ITtscGraphTrace.d.ts +99 -14
- package/lib/structures/RawDump.d.ts +22 -0
- package/lib/structures/RawDump.js +3 -0
- package/lib/structures/RawDump.js.map +1 -0
- package/lib/structures/RawEdge.d.ts +20 -0
- package/lib/structures/RawEdge.js +3 -0
- package/lib/structures/RawEdge.js.map +1 -0
- package/lib/structures/RawNode.d.ts +26 -0
- package/lib/structures/RawNode.js +3 -0
- package/lib/structures/RawNode.js.map +1 -0
- package/lib/structures/TtscGraphArtifactNodeKind.d.ts +9 -0
- package/lib/structures/TtscGraphArtifactNodeKind.js +9 -0
- package/lib/structures/TtscGraphArtifactNodeKind.js.map +1 -1
- package/lib/structures/TtscGraphDumpEdgeKind.d.ts +15 -1
- package/lib/structures/TtscGraphDumpNodeKind.d.ts +9 -0
- package/lib/structures/TtscGraphEdgeKind.d.ts +13 -4
- package/lib/structures/TtscGraphNodeKind.d.ts +9 -0
- package/lib/structures/TtscGraphNodeModifier.d.ts +9 -0
- package/lib/structures/ViewerLink.d.ts +20 -0
- package/lib/structures/ViewerLink.js +3 -0
- package/lib/structures/ViewerLink.js.map +1 -0
- package/lib/structures/ViewerNode.d.ts +24 -0
- package/lib/structures/ViewerNode.js +3 -0
- package/lib/structures/ViewerNode.js.map +1 -0
- package/lib/structures/ViewerPayload.d.ts +42 -0
- package/lib/structures/ViewerPayload.js +3 -0
- package/lib/structures/ViewerPayload.js.map +1 -0
- package/lib/view.d.ts +9 -0
- package/lib/view.js +203 -91
- package/lib/view.js.map +1 -1
- package/lib/viewer/viewer.js +1 -1
- package/package.json +8 -6
- package/src/CapturedProcessOutput.ts +50 -0
- package/src/ILauncherOption.ts +24 -0
- package/src/IProjectOptions.ts +19 -0
- package/src/ParsedLauncherOptions.ts +13 -0
- package/src/TtscGraphApplication.ts +38 -1
- package/src/TtscGraphLauncherArguments.ts +207 -0
- package/src/TtscGraphViewSnapshot.ts +46 -0
- package/src/index.ts +9 -160
- package/src/internal/GraphProcessTrace.ts +242 -0
- package/src/launcherArgs.ts +89 -23
- package/src/model/IArtifactDirectory.ts +27 -0
- package/src/model/IArtifactInputs.ts +21 -0
- package/src/model/IPublishedArtifacts.ts +81 -0
- package/src/model/IPublishedArtifactsResident.ts +24 -0
- package/src/model/ITtscLintDaemonTarget.ts +22 -0
- package/src/model/TtscGraphLinePeer.ts +318 -0
- package/src/model/TtscGraphMemory.ts +284 -246
- package/src/model/TtscGraphNativeArguments.ts +67 -0
- package/src/model/TtscGraphNodeId.ts +57 -3
- package/src/model/TtscGraphProjection.ts +473 -0
- package/src/model/TtscGraphProtocol.ts +90 -0
- package/src/model/TtscGraphReadonly.ts +17 -0
- package/src/model/TtscGraphRequestOptions.ts +16 -0
- package/src/model/TtscGraphSession.ts +196 -475
- package/src/model/TtscGraphSessionOptions.ts +22 -0
- package/src/model/TtscGraphSessionState.ts +558 -0
- package/src/model/TtscGraphShardStore.ts +99 -23
- package/src/model/TtscGraphSourceReader.ts +37 -15
- package/src/model/TtscLintDaemon.ts +55 -136
- package/src/model/TtscLintDaemonState.ts +174 -0
- package/src/model/copyGraphRecords.ts +17 -0
- package/src/model/copyGraphSnapshot.ts +30 -0
- package/src/model/loadGraph.ts +23 -12
- package/src/model/publishedArtifacts.ts +361 -182
- package/src/nativeExecutable.ts +93 -26
- package/src/reduce.ts +24 -51
- package/src/resolveGraphBinary.ts +13 -3
- package/src/runGraph.ts +90 -0
- package/src/server/IResolvedGraphHandle.ts +24 -0
- package/src/server/createServer.ts +16 -22
- package/src/server/exportSurface.ts +23 -1
- package/src/server/formatGraphFailure.ts +32 -0
- package/src/server/pathPolicy.ts +70 -5
- package/src/server/resolveHandle.ts +45 -14
- package/src/server/resultAudit.ts +58 -27
- package/src/server/resultNext.ts +26 -1
- package/src/server/runDetails.ts +138 -73
- package/src/server/runEntrypoints.ts +13 -1
- package/src/server/runLookup.ts +23 -7
- package/src/server/runOverview.ts +17 -5
- package/src/server/runTour.ts +44 -82
- package/src/server/runTrace.ts +175 -102
- package/src/server/startServer.ts +29 -1
- package/src/structures/ITtscGraphApplication.ts +95 -41
- package/src/structures/ITtscGraphDecorator.ts +18 -0
- package/src/structures/ITtscGraphDetails.ts +81 -6
- package/src/structures/ITtscGraphDocTag.ts +17 -8
- package/src/structures/ITtscGraphDump.ts +106 -8
- package/src/structures/ITtscGraphEdge.ts +14 -2
- package/src/structures/ITtscGraphEntrypoints.ts +81 -6
- package/src/structures/ITtscGraphEscape.ts +24 -2
- package/src/structures/ITtscGraphEvidence.ts +9 -0
- package/src/structures/ITtscGraphLookup.ts +42 -9
- package/src/structures/ITtscGraphNext.ts +12 -1
- package/src/structures/ITtscGraphNode.ts +38 -11
- package/src/structures/ITtscGraphOverview.ts +88 -6
- package/src/structures/ITtscGraphSnapshot.ts +117 -2
- package/src/structures/ITtscGraphSpan.ts +9 -0
- package/src/structures/ITtscGraphTour.ts +83 -9
- package/src/structures/ITtscGraphTrace.ts +100 -14
- package/src/structures/RawDump.ts +25 -0
- package/src/structures/RawEdge.ts +22 -0
- package/src/structures/RawNode.ts +31 -0
- package/src/structures/TtscGraphArtifactNodeKind.ts +9 -0
- package/src/structures/TtscGraphDumpEdgeKind.ts +15 -1
- package/src/structures/TtscGraphDumpNodeKind.ts +9 -0
- package/src/structures/TtscGraphEdgeKind.ts +13 -4
- package/src/structures/TtscGraphNodeKind.ts +9 -0
- package/src/structures/TtscGraphNodeModifier.ts +9 -0
- package/src/structures/ViewerLink.ts +22 -0
- package/src/structures/ViewerNode.ts +28 -0
- package/src/structures/ViewerPayload.ts +52 -0
- package/src/view.ts +208 -110
- package/src/viewer/legend.ts +119 -27
|
@@ -4,9 +4,26 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { ITtscGraphDump } from "../structures/ITtscGraphDump";
|
|
5
5
|
import { ITtscGraphSnapshot } from "../structures/ITtscGraphSnapshot";
|
|
6
6
|
import { isArtifactNodeKind } from "../structures/TtscGraphArtifactNodeKind";
|
|
7
|
+
import { TtscGraphReadonly } from "./TtscGraphReadonly";
|
|
8
|
+
import { copyGraphRecords } from "./copyGraphRecords";
|
|
9
|
+
import { copyGraphSnapshot } from "./copyGraphSnapshot";
|
|
7
10
|
import { DUMP_SCHEMA_VERSION } from "./loadGraph";
|
|
8
11
|
|
|
9
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Atomic validator and assembler for native ttscgraph shard transactions.
|
|
14
|
+
*
|
|
15
|
+
* A transaction is committed only after base coordinates, digests, ownership
|
|
16
|
+
* and the complete manifest agree. Rejected transactions preserve prior state.
|
|
17
|
+
*
|
|
18
|
+
* @evidence contracts/common.md#principled-implementation Content digests and consecutive base coordinates qualify source-owned shards before cross-shard assembly and atomic state replacement.
|
|
19
|
+
* @evidence contracts/common.md#clear-and-simple-design The store owns committed generation state; private validation and assembly helpers keep wire checks outside consumers.
|
|
20
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Stale bases or inconsistent contents are rejected rather than merged under compensating guesses.
|
|
21
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states the commit boundary and preserved prior state; public methods describe validation and canonical hashing.
|
|
22
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms apply owns staged validation and assembly algorithms; this declaration defines committed state.
|
|
23
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work apply establishes permission to reuse unchanged shard payloads.
|
|
24
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources apply owns population replacement and the session owns store retirement; the class declaration performs no independent lifecycle transition.
|
|
25
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation validates and assembles in-memory shard objects; no file, path or process.
|
|
26
|
+
*/
|
|
10
27
|
export class TtscGraphShardStore {
|
|
11
28
|
static readonly PROTOCOL_VERSION = 1;
|
|
12
29
|
|
|
@@ -16,11 +33,51 @@ export class TtscGraphShardStore {
|
|
|
16
33
|
private tsconfig: string | undefined;
|
|
17
34
|
private shards = new Map<
|
|
18
35
|
string,
|
|
19
|
-
{ digest: string; shard: ITtscGraphSnapshot.IShard }
|
|
36
|
+
{ digest: string; shard: TtscGraphReadonly<ITtscGraphSnapshot.IShard> }
|
|
20
37
|
>();
|
|
21
38
|
|
|
22
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Validate and atomically commit one complete or base-generation delta.
|
|
41
|
+
*
|
|
42
|
+
* Throws on invalid coordinates, repeated changes, content/manifest mismatch
|
|
43
|
+
* or inconsistent ownership. The prior generation remains committed until
|
|
44
|
+
* full dump assembly succeeds. Retained shard records are detached and
|
|
45
|
+
* frozen; the returned dump owns mutable copies of those records.
|
|
46
|
+
*
|
|
47
|
+
* @evidence contracts/common.md#principled-implementation A staged shard map validates changes, exact manifest and generation hash before assembly establishes node/edge/config ownership and commits state together.
|
|
48
|
+
* @evidence contracts/common.md#clear-and-simple-design One operation owns transaction staging and the final state swap; helper validators do not independently mutate committed fields.
|
|
49
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts A bad delta is not retried against an invented base or partially applied to the resident store.
|
|
50
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose documents rejection causes and the unchanged-state guarantee before the tags.
|
|
51
|
+
* @evidence contracts/performance.md#efficient-algorithms Map staging and manifest/ownership scans are linear in shards and facts; canonical hashes cost changed content bytes and deterministic dump sorting costs O(N log N + E log E).
|
|
52
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Unchanged frozen shard payloads retain their validated digest in the staged map; only upserts are rehashed, while the generation manifest validates continued membership and mutable output copies cannot change retained facts.
|
|
53
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources The store retains only the current manifest's shards and coordinates; commit drops removed payloads and rejection leaves the prior generation intact.
|
|
54
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation validates and assembles shard objects in memory and hashes JSON text with node:crypto; no file, path or process.
|
|
55
|
+
*/
|
|
23
56
|
apply(transaction: ITtscGraphSnapshot.ITransaction): ITtscGraphDump {
|
|
57
|
+
return this.applyProjection(transaction, (dump) =>
|
|
58
|
+
copyGraphRecords<ITtscGraphDump>(dump),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Validate a staged immutable population and commit only after projection.
|
|
64
|
+
*
|
|
65
|
+
* The callback borrows store-owned frozen facts, not caller DTOs. A thrown
|
|
66
|
+
* projection leaves every committed coordinate and shard unchanged.
|
|
67
|
+
*
|
|
68
|
+
* @evidence contracts/common.md#principled-implementation Full transaction/manifest/ownership validation precedes projection and the synchronous commit; callback failure cannot publish a partial generation.
|
|
69
|
+
* @evidence contracts/common.md#clear-and-simple-design Public mutable apply and resident immutable projection share the same staged validator and commit boundary.
|
|
70
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Immutable borrowing does not remove any digest, universe, owner or edge check; apply still returns a detached mutable DTO.
|
|
71
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states input ownership and callback-failure atomicity.
|
|
72
|
+
* @evidence contracts/performance.md#efficient-algorithms Changed shards are cloned/hashed once; full manifest/ownership scans and deterministic sorting remain. The resident callback avoids full nested mutable materialization.
|
|
73
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Retained frozen payload identities survive staged validation, qualifying downstream reuse independently of caller-owned transactions.
|
|
74
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Commit retains only current shards; rejected staging and callback values remain local, with prior state intact.
|
|
75
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Hashes and validates in-memory records without filesystem or process operations.
|
|
76
|
+
*/
|
|
77
|
+
applyProjection<T>(
|
|
78
|
+
transaction: ITtscGraphSnapshot.ITransaction,
|
|
79
|
+
project: (dump: TtscGraphReadonly<ITtscGraphDump>) => T,
|
|
80
|
+
): T {
|
|
24
81
|
this.assertCoordinates(transaction);
|
|
25
82
|
const next = new Map(this.shards);
|
|
26
83
|
const touched = new Set<string>();
|
|
@@ -50,7 +107,10 @@ export class TtscGraphShardStore {
|
|
|
50
107
|
`@ttsc/graph: native shard ${upsert.shard.key} digest ${upsert.digest} does not match ${digest}`,
|
|
51
108
|
);
|
|
52
109
|
}
|
|
53
|
-
next.set(upsert.shard.key, {
|
|
110
|
+
next.set(upsert.shard.key, {
|
|
111
|
+
digest,
|
|
112
|
+
shard: copyGraphSnapshot(upsert.shard),
|
|
113
|
+
});
|
|
54
114
|
}
|
|
55
115
|
|
|
56
116
|
const manifest = [...transaction.manifest];
|
|
@@ -93,15 +153,30 @@ export class TtscGraphShardStore {
|
|
|
93
153
|
}
|
|
94
154
|
|
|
95
155
|
const dump = assemble(transaction, next);
|
|
156
|
+
const projected = project(dump);
|
|
96
157
|
this.sequence = transaction.sequence;
|
|
97
158
|
this.generation = transaction.generation;
|
|
98
159
|
this.project = transaction.project;
|
|
99
160
|
this.tsconfig = transaction.tsconfig;
|
|
100
161
|
this.shards = next;
|
|
101
|
-
return
|
|
162
|
+
return projected;
|
|
102
163
|
}
|
|
103
164
|
|
|
104
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* SHA-256 over the producer's deterministic Go JSON encoding.
|
|
167
|
+
*
|
|
168
|
+
* JSON-serializable wire shards use producer field order and Go's escaping
|
|
169
|
+
* for HTML-sensitive characters and Unicode line separators.
|
|
170
|
+
*
|
|
171
|
+
* @evidence contracts/common.md#principled-implementation Canonical Go-compatible JSON escaping and UTF-8 SHA-256 reproduce the producer's content witness for the supplied wire shard.
|
|
172
|
+
* @evidence contracts/common.md#clear-and-simple-design One helper shares canonical digest logic with generation verification.
|
|
173
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Digest checks compare content rather than a size/time proxy or expected fixture hash.
|
|
174
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states serializability, ordering and escaping premises needed for cross-language digest equivalence.
|
|
175
|
+
* @evidence contracts/performance.md#efficient-algorithms Serialization preserves the wire object's insertion order, escapes Go-sensitive characters in one text pass and hashes the resulting bytes once.
|
|
176
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work This digest primitive computes one shard identity; apply coordinates continued reuse of validated payloads.
|
|
177
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The digest string transfers to its caller and the canonical buffer is local to this computation.
|
|
178
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation hashes the canonical JSON text of one shard with node:crypto; no file, path or process.
|
|
179
|
+
*/
|
|
105
180
|
static shardDigest(shard: ITtscGraphSnapshot.IShard): string {
|
|
106
181
|
return digest(shard);
|
|
107
182
|
}
|
|
@@ -165,13 +240,13 @@ function assemble(
|
|
|
165
240
|
transaction: ITtscGraphSnapshot.ITransaction,
|
|
166
241
|
committed: ReadonlyMap<
|
|
167
242
|
string,
|
|
168
|
-
{ digest: string; shard: ITtscGraphSnapshot.IShard }
|
|
243
|
+
{ digest: string; shard: TtscGraphReadonly<ITtscGraphSnapshot.IShard> }
|
|
169
244
|
>,
|
|
170
|
-
): ITtscGraphDump {
|
|
171
|
-
const nodes: ITtscGraphDump.INode[] = [];
|
|
172
|
-
const edges: ITtscGraphDump.IEdge[] = [];
|
|
173
|
-
const diagnostics: ITtscGraphDump.IDiagnostic[] = [];
|
|
174
|
-
const sources: ITtscGraphDump.ISourceDigest[] = [];
|
|
245
|
+
): TtscGraphReadonly<ITtscGraphDump> {
|
|
246
|
+
const nodes: TtscGraphReadonly<ITtscGraphDump.INode>[] = [];
|
|
247
|
+
const edges: TtscGraphReadonly<ITtscGraphDump.IEdge>[] = [];
|
|
248
|
+
const diagnostics: TtscGraphReadonly<ITtscGraphDump.IDiagnostic>[] = [];
|
|
249
|
+
const sources: TtscGraphReadonly<ITtscGraphDump.ISourceDigest>[] = [];
|
|
175
250
|
const nodeOwners = new Map<string, string>();
|
|
176
251
|
const sourceFiles = new Set<string>();
|
|
177
252
|
const configInputs = new Map<string, string>();
|
|
@@ -208,7 +283,7 @@ function assemble(
|
|
|
208
283
|
);
|
|
209
284
|
}
|
|
210
285
|
sourceFiles.add(shard.source.file);
|
|
211
|
-
sources.push(
|
|
286
|
+
sources.push(shard.source);
|
|
212
287
|
}
|
|
213
288
|
assertShardContents(key, shard);
|
|
214
289
|
for (const node of shard.nodes) {
|
|
@@ -221,8 +296,9 @@ function assemble(
|
|
|
221
296
|
nodeOwners.set(node.id, key);
|
|
222
297
|
nodes.push(node);
|
|
223
298
|
}
|
|
224
|
-
|
|
225
|
-
|
|
299
|
+
for (const entryToAppend of shard.edges) edges.push(entryToAppend);
|
|
300
|
+
for (const entryToAppend of shard.diagnostics)
|
|
301
|
+
diagnostics.push(entryToAppend);
|
|
226
302
|
}
|
|
227
303
|
for (const [key, value] of committed) {
|
|
228
304
|
for (const edge of value.shard.edges) {
|
|
@@ -269,10 +345,10 @@ function assemble(
|
|
|
269
345
|
left.code - right.code,
|
|
270
346
|
);
|
|
271
347
|
sources.sort((left, right) => compareText(left.file, right.file));
|
|
272
|
-
return {
|
|
348
|
+
return Object.freeze({
|
|
273
349
|
project: transaction.project,
|
|
274
350
|
tsconfig: transaction.tsconfig,
|
|
275
|
-
provenance: {
|
|
351
|
+
provenance: copyGraphSnapshot({
|
|
276
352
|
schemaVersion: transaction.schemaVersion,
|
|
277
353
|
capabilities: [...transaction.capabilities],
|
|
278
354
|
producer: { ...transaction.producer },
|
|
@@ -281,16 +357,16 @@ function assemble(
|
|
|
281
357
|
roots: transaction.universe.roots.map((root) => ({ ...root })),
|
|
282
358
|
},
|
|
283
359
|
sources,
|
|
284
|
-
},
|
|
285
|
-
diagnostics,
|
|
286
|
-
nodes,
|
|
287
|
-
edges,
|
|
288
|
-
};
|
|
360
|
+
}),
|
|
361
|
+
diagnostics: Object.freeze(diagnostics),
|
|
362
|
+
nodes: Object.freeze(nodes),
|
|
363
|
+
edges: Object.freeze(edges),
|
|
364
|
+
});
|
|
289
365
|
}
|
|
290
366
|
|
|
291
367
|
function assertShardContents(
|
|
292
368
|
key: string,
|
|
293
|
-
shard: ITtscGraphSnapshot.IShard
|
|
369
|
+
shard: TtscGraphReadonly<ITtscGraphSnapshot.IShard>,
|
|
294
370
|
): void {
|
|
295
371
|
if (shard.source !== undefined) {
|
|
296
372
|
for (const node of shard.nodes) {
|
|
@@ -9,12 +9,25 @@ type ReadFile = (file: string) => Buffer;
|
|
|
9
9
|
/**
|
|
10
10
|
* Immutable, provenance-gated source lines owned by one graph snapshot.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* compiler-decoded text hashes to `checkerDigest`.
|
|
16
|
-
*
|
|
17
|
-
* one immutable line array.
|
|
12
|
+
* Declaration documentation is a display fact derived from source text, but the
|
|
13
|
+
* live disk is not the snapshot the checker resolved. A file becomes readable
|
|
14
|
+
* here only after its current bytes hash to `diskDigest` and the
|
|
15
|
+
* compiler-decoded text hashes to `checkerDigest`. Manifest members cache both
|
|
16
|
+
* success and failure, so every consumer of a `TtscGraphMemory` sees one
|
|
17
|
+
* adjudication and one immutable line array. Unknown names remain uncached;
|
|
18
|
+
* they have no snapshot witness to adjudicate.
|
|
19
|
+
*
|
|
20
|
+
* Marked UTF-16 files follow the compiler's Unicode scalar decoding: valid
|
|
21
|
+
* surrogate pairs remain intact and unpaired surrogates become U+FFFD.
|
|
22
|
+
*
|
|
23
|
+
* @evidence contracts/common.md#principled-implementation Raw-byte SHA-256 establishes disk identity before compiler-equivalent BOM removal and Unicode scalar decoding; decoded-text SHA-256 then establishes checker identity.
|
|
24
|
+
* @evidence contracts/common.md#clear-and-simple-design One snapshot reader owns provenance selection, decoding and per-file adjudication rather than letting consumers read disk independently.
|
|
25
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Missing provenance or digest mismatch yields absence instead of mixing current source into old checker facts.
|
|
26
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain the dual witnesses, immutable lines and cached failure behavior.
|
|
27
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms lines owns hashing, decoding and splitting strategy through its private helpers.
|
|
28
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work lines coordinates per-generation success/absence reuse; the declaration exposes that owner.
|
|
29
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources lines controls retained per-file entries and the graph owns reader lifetime; the declaration adds no separate acquisition or release.
|
|
30
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation The type exposes a source-reading contract; lines and its helpers perform the native filesystem boundary.
|
|
18
31
|
*/
|
|
19
32
|
export class TtscGraphSourceReader {
|
|
20
33
|
private readonly project: string;
|
|
@@ -47,20 +60,27 @@ export class TtscGraphSourceReader {
|
|
|
47
60
|
|
|
48
61
|
/**
|
|
49
62
|
* Return frozen lines only when raw disk bytes and compiler-decoded text both
|
|
50
|
-
* equal the checker snapshot.
|
|
51
|
-
*
|
|
63
|
+
* equal the checker snapshot. Missing manifest entries return absence without
|
|
64
|
+
* storage or disk access. For manifest members, missing digests, read failure
|
|
65
|
+
* and digest mismatch are cached absences rather than reasons to mix current
|
|
52
66
|
* disk text into old graph facts.
|
|
67
|
+
*
|
|
68
|
+
* @evidence contracts/common.md#principled-implementation Normalized file lookup, raw hash, BOM-aware decoding with the compiler's unpaired-surrogate replacement and checker-text hash establish both byte and semantic snapshot identity before freezing lines.
|
|
69
|
+
* @evidence contracts/common.md#clear-and-simple-design One path adjudicates all source display reads, with decoding/hash helpers isolated from cache ownership.
|
|
70
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Missing witnesses return absence without disk access; changed bytes remain a cached absence rather than a fallback to unverified live text.
|
|
71
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose distinguishes uncached unknown names from cached adjudications of manifest members.
|
|
72
|
+
* @evidence contracts/portability.md#os-neutral-implementation Dump separators are normalized only for manifest lookup; Node path.resolve and fs read the project-native location without inferring case policy from OS.
|
|
73
|
+
* @evidence contracts/performance.md#efficient-algorithms A first read costs linear file bytes for hashes, BOM decoding and line splitting; later lookups use the per-file map.
|
|
74
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Manifest-member success and absence are cached by normalized path within one immutable provenance generation; unknown names need only a map lookup and a new graph creates a new reader.
|
|
75
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Only manifest members can enter the adjudication cache, bounding retained entries and line arrays by the snapshot source population; releasing the owning graph releases both maps.
|
|
53
76
|
*/
|
|
54
77
|
lines(file: string): readonly string[] | undefined {
|
|
55
78
|
const key = normalize(file);
|
|
56
79
|
if (this.cache.has(key)) return this.cache.get(key);
|
|
57
80
|
|
|
58
81
|
const expected = this.digests.get(key);
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
expected.checker === "" ||
|
|
62
|
-
expected.disk === ""
|
|
63
|
-
) {
|
|
82
|
+
if (expected === undefined) return undefined;
|
|
83
|
+
if (expected.checker === "" || expected.disk === "") {
|
|
64
84
|
this.cache.set(key, undefined);
|
|
65
85
|
return undefined;
|
|
66
86
|
}
|
|
@@ -95,12 +115,14 @@ function normalize(file: string): string {
|
|
|
95
115
|
return file.replace(/\\/g, "/");
|
|
96
116
|
}
|
|
97
117
|
|
|
118
|
+
/** Decode complete UTF-16 units to compiler scalars; ignore a trailing byte. */
|
|
98
119
|
function decodeSource(bytes: Buffer): string {
|
|
99
120
|
if (bytes.length >= 2) {
|
|
100
121
|
const body = bytes.subarray(2, bytes.length - ((bytes.length - 2) % 2));
|
|
101
|
-
if (bytes[0] === 0xff && bytes[1] === 0xfe)
|
|
122
|
+
if (bytes[0] === 0xff && bytes[1] === 0xfe)
|
|
123
|
+
return body.toString("utf16le").toWellFormed();
|
|
102
124
|
if (bytes[0] === 0xfe && bytes[1] === 0xff)
|
|
103
|
-
return Buffer.from(body).swap16().toString("utf16le");
|
|
125
|
+
return Buffer.from(body).swap16().toString("utf16le").toWellFormed();
|
|
104
126
|
}
|
|
105
127
|
if (
|
|
106
128
|
bytes.length >= 3 &&
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export interface ITtscLintDaemonTarget {
|
|
6
|
-
binary: string;
|
|
7
|
-
manifest: string;
|
|
8
|
-
projectContext?: string;
|
|
9
|
-
}
|
|
1
|
+
import type { ITtscLintDaemonTarget } from "./ITtscLintDaemonTarget";
|
|
2
|
+
import { TtscGraphLinePeer } from "./TtscGraphLinePeer";
|
|
3
|
+
import { TtscGraphNativeArguments } from "./TtscGraphNativeArguments";
|
|
4
|
+
import { TtscLintDaemonState } from "./TtscLintDaemonState";
|
|
10
5
|
|
|
11
6
|
/**
|
|
12
7
|
* A resident `@ttsc/lint` sidecar, kept open across the questions one graph
|
|
@@ -24,19 +19,38 @@ export interface ITtscLintDaemonTarget {
|
|
|
24
19
|
* at all, and a daemon can die mid-session; each of those closes this one and
|
|
25
20
|
* leaves the caller to spawn per verb, which is the behaviour that existed
|
|
26
21
|
* before this and is still correct — only slower.
|
|
22
|
+
*
|
|
23
|
+
* @evidence contracts/common.md#principled-implementation Serialized line requests match FIFO responses; unsupported or failed daemon replies return null so callers can use the same direct verb contract.
|
|
24
|
+
* @evidence contracts/common.md#clear-and-simple-design This facade owns one sidecar's launch identity; daemon state owns FIFO replies/fallback and the shared line adapter owns its native transport.
|
|
25
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts A failed daemon is retired rather than advertised as a successful empty artifact set.
|
|
26
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain resident amortization and the supported direct-command fallback rather than claiming all failures are empty results.
|
|
27
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms ask delegates FIFO processing and fallback to daemon state; this declaration describes the real sidecar launch identity.
|
|
28
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work ask owns continued process/configuration reuse and invalidation.
|
|
29
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources ask and close own acquisition, failure and shutdown transitions; the declaration adds no independent lifecycle operation.
|
|
30
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Native invocation and termination decisions are acknowledged on ask and close together with private helpers.
|
|
27
31
|
*/
|
|
28
32
|
export class TtscLintDaemon {
|
|
29
|
-
private
|
|
30
|
-
private lines: readline.Interface | undefined;
|
|
31
|
-
private readonly pending: ((reply: IReply | null) => void)[] = [];
|
|
32
|
-
private queue: Promise<unknown> = Promise.resolve();
|
|
33
|
-
private failed = false;
|
|
33
|
+
private readonly state: TtscLintDaemonState;
|
|
34
34
|
|
|
35
35
|
public constructor(
|
|
36
36
|
private readonly target: ITtscLintDaemonTarget,
|
|
37
37
|
private readonly cwd: string,
|
|
38
38
|
private readonly tsconfig: string,
|
|
39
|
-
) {
|
|
39
|
+
) {
|
|
40
|
+
this.state = new TtscLintDaemonState((events) =>
|
|
41
|
+
TtscGraphLinePeer.open(
|
|
42
|
+
this.target.binary,
|
|
43
|
+
TtscGraphNativeArguments.lint(
|
|
44
|
+
this.cwd,
|
|
45
|
+
this.tsconfig,
|
|
46
|
+
this.target.manifest,
|
|
47
|
+
this.target.projectContext,
|
|
48
|
+
),
|
|
49
|
+
events,
|
|
50
|
+
{ cwd: this.cwd, stderr: "drain" },
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
40
54
|
|
|
41
55
|
/**
|
|
42
56
|
* Ask one verb and return its raw JSON, or `null` when this daemon cannot
|
|
@@ -50,130 +64,35 @@ export class TtscLintDaemon {
|
|
|
50
64
|
* Requests are serialized. The daemon answers one line per request in order,
|
|
51
65
|
* with nothing in the reply to address it by, so a second request in flight
|
|
52
66
|
* would be matched against the first one's answer.
|
|
67
|
+
*
|
|
68
|
+
* @evidence contracts/common.md#principled-implementation A promise queue admits one request at a time because the protocol orders responses without request ids; result text preserves arbitrary JSON values.
|
|
69
|
+
* @evidence contracts/common.md#clear-and-simple-design ask delegates queue admission and reply decoding to daemon state; its configured opener supplies actual native transport and supported sidecar argv.
|
|
70
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Null explicitly requests a direct fallback; it cannot silently masquerade as no published artifacts.
|
|
71
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain null meaning and why requests must be serialized.
|
|
72
|
+
* @evidence contracts/performance.md#efficient-algorithms Each request writes and decodes one JSON frame; queue work is linear in serialized frame bytes with one active reply.
|
|
73
|
+
* @evidence contracts/performance.md#reuse-equivalent-work The same target/project sidecar retains process, plugin load and configuration across verbs; invalidate explicitly retires warm Program facts when input generations change.
|
|
74
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources The daemon owns one child and reader; close settles pending replies and kills the child, while queued promises remain proportional to submitted caller demand.
|
|
75
|
+
* @evidence contracts/portability.md#os-neutral-implementation spawn passes executable and an argv vector directly with windowsHide, preserving spaces and native executable semantics without shell quoting.
|
|
53
76
|
*/
|
|
54
77
|
public ask(verb: string, invalidate: boolean): Promise<string | null> {
|
|
55
|
-
|
|
56
|
-
this.queue = run.catch(() => undefined);
|
|
57
|
-
return run;
|
|
78
|
+
return this.state.ask(verb, invalidate);
|
|
58
79
|
}
|
|
59
80
|
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
): Promise<string | null> {
|
|
77
|
-
if (this.failed) return null;
|
|
78
|
-
const child = this.start();
|
|
79
|
-
if (child === undefined) return null;
|
|
80
|
-
const reply = await new Promise<IReply | null>((resolve) => {
|
|
81
|
-
this.pending.push(resolve);
|
|
82
|
-
child.stdin.write(
|
|
83
|
-
`${JSON.stringify({ invalidate, verb })}\n`,
|
|
84
|
-
(error) => {
|
|
85
|
-
if (error === null || error === undefined) return;
|
|
86
|
-
this.fail();
|
|
87
|
-
},
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
if (reply === null || reply.code !== 0) {
|
|
91
|
-
// A nonzero code is the sidecar declining, and this client cannot tell
|
|
92
|
-
// "unknown verb" from "the rule failed". Closing rather than retrying
|
|
93
|
-
// through the daemon is what makes the caller fall back to the direct
|
|
94
|
-
// command, where a real failure surfaces the same way it always did.
|
|
95
|
-
this.close();
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
return reply.result;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private start(): ChildProcessWithoutNullStreams | undefined {
|
|
102
|
-
if (this.child !== undefined) return this.child;
|
|
103
|
-
if (this.failed) return undefined;
|
|
104
|
-
let child: ChildProcessWithoutNullStreams;
|
|
105
|
-
try {
|
|
106
|
-
child = spawn(
|
|
107
|
-
this.target.binary,
|
|
108
|
-
[
|
|
109
|
-
"lsp-serve",
|
|
110
|
-
`--cwd=${this.cwd}`,
|
|
111
|
-
`--tsconfig=${this.tsconfig}`,
|
|
112
|
-
`--plugins-json=${this.target.manifest}`,
|
|
113
|
-
...(this.target.projectContext === undefined
|
|
114
|
-
? []
|
|
115
|
-
: [`--project-context-json=${this.target.projectContext}`]),
|
|
116
|
-
],
|
|
117
|
-
{ cwd: this.cwd, stdio: ["pipe", "pipe", "pipe"], windowsHide: true },
|
|
118
|
-
);
|
|
119
|
-
} catch {
|
|
120
|
-
this.failed = true;
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
this.child = child;
|
|
124
|
-
// The sidecar's stderr is its own diagnostic channel and is not this
|
|
125
|
-
// client's to interpret; draining it keeps a chatty plugin from filling the
|
|
126
|
-
// pipe and stalling the daemon it is talking through.
|
|
127
|
-
child.stderr.resume();
|
|
128
|
-
child.on("error", () => this.fail());
|
|
129
|
-
child.on("exit", () => this.fail());
|
|
130
|
-
this.lines = readline.createInterface({ input: child.stdout });
|
|
131
|
-
this.lines.on("line", (line) => this.onLine(line));
|
|
132
|
-
return child;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private onLine(line: string): void {
|
|
136
|
-
const settle = this.pending.shift();
|
|
137
|
-
if (settle === undefined) return;
|
|
138
|
-
let reply: IReply;
|
|
139
|
-
try {
|
|
140
|
-
reply = JSON.parse(line) as IReply;
|
|
141
|
-
} catch {
|
|
142
|
-
settle(null);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
settle(
|
|
146
|
-
typeof reply.code === "number"
|
|
147
|
-
? { code: reply.code, result: rawResult(line) }
|
|
148
|
-
: null,
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private fail(): void {
|
|
153
|
-
this.close();
|
|
81
|
+
/**
|
|
82
|
+
* Stop the sidecar. Safe to call more than once, and after a failure.
|
|
83
|
+
*
|
|
84
|
+
* Pending replies settle null so callers can switch to the direct command.
|
|
85
|
+
*
|
|
86
|
+
* @evidence contracts/common.md#principled-implementation Marking failed before settling requests prevents new daemon work while reader and child ownership are cleared.
|
|
87
|
+
* @evidence contracts/common.md#clear-and-simple-design One shutdown operation is shared by explicit disposal and transport failure.
|
|
88
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Shutdown does not invent a successful reply to unblock a request.
|
|
89
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states idempotence, failure safety and the meaning of settled null replies.
|
|
90
|
+
* @evidence contracts/performance.md#efficient-algorithms Closing visits outstanding reply callbacks once and clears the reader/child references.
|
|
91
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Closure retires this target's reusable process permanently; later calls return the supported direct-fallback indication.
|
|
92
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Both normal and failed shutdown settle pending callbacks, close readline and end/kill the owned process; the returned completion still joins stdio after process exit and rejects unknown or forced termination.
|
|
93
|
+
* @evidence contracts/portability.md#os-neutral-implementation Node stream closure and child.kill own native termination rather than platform shell commands.
|
|
94
|
+
*/
|
|
95
|
+
public close(): Promise<void> {
|
|
96
|
+
return this.state.close();
|
|
154
97
|
}
|
|
155
98
|
}
|
|
156
|
-
|
|
157
|
-
/** One `lsp-serve` reply: a verb result and the code that qualifies it. */
|
|
158
|
-
interface IReply {
|
|
159
|
-
code: number;
|
|
160
|
-
result: string;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* The `result` member, as the JSON text this daemon's callers parse.
|
|
165
|
-
*
|
|
166
|
-
* A verb's result is arbitrary JSON that the caller decodes itself, so it is
|
|
167
|
-
* handed back as text rather than as a value — which is what the direct command
|
|
168
|
-
* hands over, and what keeps the two paths interchangeable. The text is
|
|
169
|
-
* re-serialized rather than sliced out of the line: the bytes are not identical
|
|
170
|
-
* to the sidecar's own, but the value they decode to is, and no caller here
|
|
171
|
-
* reads anything else.
|
|
172
|
-
*
|
|
173
|
-
* A reply with no `result` is `"null"`, so a caller parses a value either way
|
|
174
|
-
* instead of being handed the empty string.
|
|
175
|
-
*/
|
|
176
|
-
function rawResult(line: string): string {
|
|
177
|
-
const parsed = JSON.parse(line) as { result?: unknown };
|
|
178
|
-
return parsed.result === undefined ? "null" : JSON.stringify(parsed.result);
|
|
179
|
-
}
|