@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.
Files changed (292) hide show
  1. package/README.md +122 -30
  2. package/lib/CapturedProcessOutput.d.ts +47 -0
  3. package/lib/CapturedProcessOutput.js +3 -0
  4. package/lib/CapturedProcessOutput.js.map +1 -0
  5. package/lib/ILauncherOption.d.ts +22 -0
  6. package/lib/ILauncherOption.js +3 -0
  7. package/lib/ILauncherOption.js.map +1 -0
  8. package/lib/IProjectOptions.d.ts +18 -0
  9. package/lib/IProjectOptions.js +3 -0
  10. package/lib/IProjectOptions.js.map +1 -0
  11. package/lib/ParsedLauncherOptions.d.ts +13 -0
  12. package/lib/ParsedLauncherOptions.js +3 -0
  13. package/lib/ParsedLauncherOptions.js.map +1 -0
  14. package/lib/TtscGraphApplication.d.ts +38 -1
  15. package/lib/TtscGraphApplication.js +26 -1
  16. package/lib/TtscGraphApplication.js.map +1 -1
  17. package/lib/TtscGraphLauncherArguments.d.ts +115 -0
  18. package/lib/TtscGraphLauncherArguments.js +181 -0
  19. package/lib/TtscGraphLauncherArguments.js.map +1 -0
  20. package/lib/TtscGraphViewSnapshot.d.ts +35 -0
  21. package/lib/TtscGraphViewSnapshot.js +48 -0
  22. package/lib/TtscGraphViewSnapshot.js.map +1 -0
  23. package/lib/index.d.ts +9 -24
  24. package/lib/index.js +6 -128
  25. package/lib/index.js.map +1 -1
  26. package/lib/internal/GraphProcessTrace.d.ts +1 -0
  27. package/lib/internal/GraphProcessTrace.js +235 -0
  28. package/lib/internal/GraphProcessTrace.js.map +1 -0
  29. package/lib/launcherArgs.d.ts +75 -16
  30. package/lib/launcherArgs.js +77 -8
  31. package/lib/launcherArgs.js.map +1 -1
  32. package/lib/model/IArtifactDirectory.d.ts +26 -0
  33. package/lib/model/IArtifactDirectory.js +3 -0
  34. package/lib/model/IArtifactDirectory.js.map +1 -0
  35. package/lib/model/IArtifactInputs.d.ts +19 -0
  36. package/lib/model/IArtifactInputs.js +3 -0
  37. package/lib/model/IArtifactInputs.js.map +1 -0
  38. package/lib/model/IPublishedArtifacts.d.ts +76 -0
  39. package/lib/model/IPublishedArtifacts.js +3 -0
  40. package/lib/model/IPublishedArtifacts.js.map +1 -0
  41. package/lib/model/IPublishedArtifactsResident.d.ts +19 -0
  42. package/lib/model/IPublishedArtifactsResident.js +3 -0
  43. package/lib/model/IPublishedArtifactsResident.js.map +1 -0
  44. package/lib/model/ITtscLintDaemonTarget.d.ts +20 -0
  45. package/lib/model/ITtscLintDaemonTarget.js +3 -0
  46. package/lib/model/ITtscLintDaemonTarget.js.map +1 -0
  47. package/lib/model/TtscGraphLinePeer.d.ts +146 -0
  48. package/lib/model/TtscGraphLinePeer.js +213 -0
  49. package/lib/model/TtscGraphLinePeer.js.map +1 -0
  50. package/lib/model/TtscGraphMemory.d.ts +156 -16
  51. package/lib/model/TtscGraphMemory.js +228 -222
  52. package/lib/model/TtscGraphMemory.js.map +1 -1
  53. package/lib/model/TtscGraphNativeArguments.d.ts +40 -0
  54. package/lib/model/TtscGraphNativeArguments.js +64 -0
  55. package/lib/model/TtscGraphNativeArguments.js.map +1 -0
  56. package/lib/model/TtscGraphNodeId.d.ts +55 -3
  57. package/lib/model/TtscGraphNodeId.js +40 -2
  58. package/lib/model/TtscGraphNodeId.js.map +1 -1
  59. package/lib/model/TtscGraphProjection.d.ts +156 -0
  60. package/lib/model/TtscGraphProjection.js +390 -0
  61. package/lib/model/TtscGraphProjection.js.map +1 -0
  62. package/lib/model/TtscGraphProtocol.d.ts +28 -0
  63. package/lib/model/TtscGraphProtocol.js +928 -0
  64. package/lib/model/TtscGraphProtocol.js.map +1 -0
  65. package/lib/model/TtscGraphReadonly.d.ts +15 -0
  66. package/lib/model/TtscGraphReadonly.js +3 -0
  67. package/lib/model/TtscGraphReadonly.js.map +1 -0
  68. package/lib/model/TtscGraphRequestOptions.d.ts +16 -0
  69. package/lib/model/TtscGraphRequestOptions.js +3 -0
  70. package/lib/model/TtscGraphRequestOptions.js.map +1 -0
  71. package/lib/model/TtscGraphSession.d.ts +82 -36
  72. package/lib/model/TtscGraphSession.js +164 -1238
  73. package/lib/model/TtscGraphSession.js.map +1 -1
  74. package/lib/model/TtscGraphSessionOptions.d.ts +20 -0
  75. package/lib/model/TtscGraphSessionOptions.js +3 -0
  76. package/lib/model/TtscGraphSessionOptions.js.map +1 -0
  77. package/lib/model/TtscGraphSessionState.d.ts +187 -0
  78. package/lib/model/TtscGraphSessionState.js +417 -0
  79. package/lib/model/TtscGraphSessionState.js.map +1 -0
  80. package/lib/model/TtscGraphShardStore.d.ts +64 -3
  81. package/lib/model/TtscGraphShardStore.js +85 -15
  82. package/lib/model/TtscGraphShardStore.js.map +1 -1
  83. package/lib/model/TtscGraphSourceReader.d.ts +31 -8
  84. package/lib/model/TtscGraphSourceReader.js +37 -13
  85. package/lib/model/TtscGraphSourceReader.js.map +1 -1
  86. package/lib/model/TtscLintDaemon.d.ts +35 -17
  87. package/lib/model/TtscLintDaemon.js +39 -117
  88. package/lib/model/TtscLintDaemon.js.map +1 -1
  89. package/lib/model/TtscLintDaemonState.d.ts +69 -0
  90. package/lib/model/TtscLintDaemonState.js +166 -0
  91. package/lib/model/TtscLintDaemonState.js.map +1 -0
  92. package/lib/model/copyGraphRecords.d.ts +14 -0
  93. package/lib/model/copyGraphRecords.js +19 -0
  94. package/lib/model/copyGraphRecords.js.map +1 -0
  95. package/lib/model/copyGraphSnapshot.d.ts +17 -0
  96. package/lib/model/copyGraphSnapshot.js +33 -0
  97. package/lib/model/copyGraphSnapshot.js.map +1 -0
  98. package/lib/model/loadGraph.d.ts +19 -10
  99. package/lib/model/loadGraph.js +21 -12
  100. package/lib/model/loadGraph.js.map +1 -1
  101. package/lib/model/publishedArtifacts.d.ts +78 -97
  102. package/lib/model/publishedArtifacts.js +271 -85
  103. package/lib/model/publishedArtifacts.js.map +1 -1
  104. package/lib/nativeExecutable.d.ts +19 -8
  105. package/lib/nativeExecutable.js +75 -15
  106. package/lib/nativeExecutable.js.map +1 -1
  107. package/lib/reduce.d.ts +19 -45
  108. package/lib/reduce.js +19 -3
  109. package/lib/reduce.js.map +1 -1
  110. package/lib/resolveGraphBinary.d.ts +9 -0
  111. package/lib/resolveGraphBinary.js +11 -3
  112. package/lib/resolveGraphBinary.js.map +1 -1
  113. package/lib/runGraph.d.ts +21 -0
  114. package/lib/runGraph.js +83 -0
  115. package/lib/runGraph.js.map +1 -0
  116. package/lib/server/IResolvedGraphHandle.d.ts +22 -0
  117. package/lib/server/IResolvedGraphHandle.js +3 -0
  118. package/lib/server/IResolvedGraphHandle.js.map +1 -0
  119. package/lib/server/createServer.d.ts +14 -10
  120. package/lib/server/createServer.js +42 -39
  121. package/lib/server/createServer.js.map +1 -1
  122. package/lib/server/exportSurface.d.ts +23 -1
  123. package/lib/server/exportSurface.js +23 -1
  124. package/lib/server/exportSurface.js.map +1 -1
  125. package/lib/server/formatGraphFailure.d.ts +20 -0
  126. package/lib/server/formatGraphFailure.js +35 -0
  127. package/lib/server/formatGraphFailure.js.map +1 -0
  128. package/lib/server/pathPolicy.d.ts +70 -5
  129. package/lib/server/pathPolicy.js +66 -4
  130. package/lib/server/pathPolicy.js.map +1 -1
  131. package/lib/server/resolveHandle.d.ts +10 -5
  132. package/lib/server/resolveHandle.js +39 -7
  133. package/lib/server/resolveHandle.js.map +1 -1
  134. package/lib/server/resultAudit.d.ts +17 -14
  135. package/lib/server/resultAudit.js +56 -27
  136. package/lib/server/resultAudit.js.map +1 -1
  137. package/lib/server/resultNext.d.ts +26 -1
  138. package/lib/server/resultNext.js +14 -0
  139. package/lib/server/resultNext.js.map +1 -1
  140. package/lib/server/runDetails.d.ts +72 -15
  141. package/lib/server/runDetails.js +104 -69
  142. package/lib/server/runDetails.js.map +1 -1
  143. package/lib/server/runEntrypoints.d.ts +9 -0
  144. package/lib/server/runEntrypoints.js +9 -0
  145. package/lib/server/runEntrypoints.js.map +1 -1
  146. package/lib/server/runLookup.d.ts +13 -3
  147. package/lib/server/runLookup.js +19 -6
  148. package/lib/server/runLookup.js.map +1 -1
  149. package/lib/server/runOverview.d.ts +13 -4
  150. package/lib/server/runOverview.js +13 -4
  151. package/lib/server/runOverview.js.map +1 -1
  152. package/lib/server/runTour.d.ts +9 -0
  153. package/lib/server/runTour.js +33 -65
  154. package/lib/server/runTour.js.map +1 -1
  155. package/lib/server/runTrace.d.ts +25 -1
  156. package/lib/server/runTrace.js +133 -88
  157. package/lib/server/runTrace.js.map +1 -1
  158. package/lib/server/startServer.d.ts +16 -0
  159. package/lib/server/startServer.js +24 -1
  160. package/lib/server/startServer.js.map +1 -1
  161. package/lib/structures/ITtscGraphApplication.d.ts +95 -41
  162. package/lib/structures/ITtscGraphDecorator.d.ts +18 -0
  163. package/lib/structures/ITtscGraphDetails.d.ts +81 -6
  164. package/lib/structures/ITtscGraphDocTag.d.ts +17 -8
  165. package/lib/structures/ITtscGraphDump.d.ts +106 -8
  166. package/lib/structures/ITtscGraphEdge.d.ts +14 -2
  167. package/lib/structures/ITtscGraphEntrypoints.d.ts +81 -6
  168. package/lib/structures/ITtscGraphEscape.d.ts +24 -2
  169. package/lib/structures/ITtscGraphEvidence.d.ts +9 -0
  170. package/lib/structures/ITtscGraphLookup.d.ts +42 -9
  171. package/lib/structures/ITtscGraphNext.d.ts +12 -1
  172. package/lib/structures/ITtscGraphNode.d.ts +38 -11
  173. package/lib/structures/ITtscGraphOverview.d.ts +81 -6
  174. package/lib/structures/ITtscGraphSnapshot.d.ts +97 -2
  175. package/lib/structures/ITtscGraphSpan.d.ts +9 -0
  176. package/lib/structures/ITtscGraphTour.d.ts +83 -9
  177. package/lib/structures/ITtscGraphTrace.d.ts +99 -14
  178. package/lib/structures/RawDump.d.ts +22 -0
  179. package/lib/structures/RawDump.js +3 -0
  180. package/lib/structures/RawDump.js.map +1 -0
  181. package/lib/structures/RawEdge.d.ts +20 -0
  182. package/lib/structures/RawEdge.js +3 -0
  183. package/lib/structures/RawEdge.js.map +1 -0
  184. package/lib/structures/RawNode.d.ts +26 -0
  185. package/lib/structures/RawNode.js +3 -0
  186. package/lib/structures/RawNode.js.map +1 -0
  187. package/lib/structures/TtscGraphArtifactNodeKind.d.ts +9 -0
  188. package/lib/structures/TtscGraphArtifactNodeKind.js +9 -0
  189. package/lib/structures/TtscGraphArtifactNodeKind.js.map +1 -1
  190. package/lib/structures/TtscGraphDumpEdgeKind.d.ts +15 -1
  191. package/lib/structures/TtscGraphDumpNodeKind.d.ts +9 -0
  192. package/lib/structures/TtscGraphEdgeKind.d.ts +13 -4
  193. package/lib/structures/TtscGraphNodeKind.d.ts +9 -0
  194. package/lib/structures/TtscGraphNodeModifier.d.ts +9 -0
  195. package/lib/structures/ViewerLink.d.ts +20 -0
  196. package/lib/structures/ViewerLink.js +3 -0
  197. package/lib/structures/ViewerLink.js.map +1 -0
  198. package/lib/structures/ViewerNode.d.ts +24 -0
  199. package/lib/structures/ViewerNode.js +3 -0
  200. package/lib/structures/ViewerNode.js.map +1 -0
  201. package/lib/structures/ViewerPayload.d.ts +42 -0
  202. package/lib/structures/ViewerPayload.js +3 -0
  203. package/lib/structures/ViewerPayload.js.map +1 -0
  204. package/lib/view.d.ts +9 -0
  205. package/lib/view.js +203 -91
  206. package/lib/view.js.map +1 -1
  207. package/lib/viewer/viewer.js +1 -1
  208. package/package.json +8 -6
  209. package/src/CapturedProcessOutput.ts +50 -0
  210. package/src/ILauncherOption.ts +24 -0
  211. package/src/IProjectOptions.ts +19 -0
  212. package/src/ParsedLauncherOptions.ts +13 -0
  213. package/src/TtscGraphApplication.ts +38 -1
  214. package/src/TtscGraphLauncherArguments.ts +207 -0
  215. package/src/TtscGraphViewSnapshot.ts +46 -0
  216. package/src/index.ts +9 -160
  217. package/src/internal/GraphProcessTrace.ts +242 -0
  218. package/src/launcherArgs.ts +89 -23
  219. package/src/model/IArtifactDirectory.ts +27 -0
  220. package/src/model/IArtifactInputs.ts +21 -0
  221. package/src/model/IPublishedArtifacts.ts +81 -0
  222. package/src/model/IPublishedArtifactsResident.ts +24 -0
  223. package/src/model/ITtscLintDaemonTarget.ts +22 -0
  224. package/src/model/TtscGraphLinePeer.ts +318 -0
  225. package/src/model/TtscGraphMemory.ts +284 -246
  226. package/src/model/TtscGraphNativeArguments.ts +67 -0
  227. package/src/model/TtscGraphNodeId.ts +57 -3
  228. package/src/model/TtscGraphProjection.ts +473 -0
  229. package/src/model/TtscGraphProtocol.ts +90 -0
  230. package/src/model/TtscGraphReadonly.ts +17 -0
  231. package/src/model/TtscGraphRequestOptions.ts +16 -0
  232. package/src/model/TtscGraphSession.ts +196 -475
  233. package/src/model/TtscGraphSessionOptions.ts +22 -0
  234. package/src/model/TtscGraphSessionState.ts +558 -0
  235. package/src/model/TtscGraphShardStore.ts +99 -23
  236. package/src/model/TtscGraphSourceReader.ts +37 -15
  237. package/src/model/TtscLintDaemon.ts +55 -136
  238. package/src/model/TtscLintDaemonState.ts +174 -0
  239. package/src/model/copyGraphRecords.ts +17 -0
  240. package/src/model/copyGraphSnapshot.ts +30 -0
  241. package/src/model/loadGraph.ts +23 -12
  242. package/src/model/publishedArtifacts.ts +361 -182
  243. package/src/nativeExecutable.ts +93 -26
  244. package/src/reduce.ts +24 -51
  245. package/src/resolveGraphBinary.ts +13 -3
  246. package/src/runGraph.ts +90 -0
  247. package/src/server/IResolvedGraphHandle.ts +24 -0
  248. package/src/server/createServer.ts +16 -22
  249. package/src/server/exportSurface.ts +23 -1
  250. package/src/server/formatGraphFailure.ts +32 -0
  251. package/src/server/pathPolicy.ts +70 -5
  252. package/src/server/resolveHandle.ts +45 -14
  253. package/src/server/resultAudit.ts +58 -27
  254. package/src/server/resultNext.ts +26 -1
  255. package/src/server/runDetails.ts +138 -73
  256. package/src/server/runEntrypoints.ts +13 -1
  257. package/src/server/runLookup.ts +23 -7
  258. package/src/server/runOverview.ts +17 -5
  259. package/src/server/runTour.ts +44 -82
  260. package/src/server/runTrace.ts +175 -102
  261. package/src/server/startServer.ts +29 -1
  262. package/src/structures/ITtscGraphApplication.ts +95 -41
  263. package/src/structures/ITtscGraphDecorator.ts +18 -0
  264. package/src/structures/ITtscGraphDetails.ts +81 -6
  265. package/src/structures/ITtscGraphDocTag.ts +17 -8
  266. package/src/structures/ITtscGraphDump.ts +106 -8
  267. package/src/structures/ITtscGraphEdge.ts +14 -2
  268. package/src/structures/ITtscGraphEntrypoints.ts +81 -6
  269. package/src/structures/ITtscGraphEscape.ts +24 -2
  270. package/src/structures/ITtscGraphEvidence.ts +9 -0
  271. package/src/structures/ITtscGraphLookup.ts +42 -9
  272. package/src/structures/ITtscGraphNext.ts +12 -1
  273. package/src/structures/ITtscGraphNode.ts +38 -11
  274. package/src/structures/ITtscGraphOverview.ts +88 -6
  275. package/src/structures/ITtscGraphSnapshot.ts +117 -2
  276. package/src/structures/ITtscGraphSpan.ts +9 -0
  277. package/src/structures/ITtscGraphTour.ts +83 -9
  278. package/src/structures/ITtscGraphTrace.ts +100 -14
  279. package/src/structures/RawDump.ts +25 -0
  280. package/src/structures/RawEdge.ts +22 -0
  281. package/src/structures/RawNode.ts +31 -0
  282. package/src/structures/TtscGraphArtifactNodeKind.ts +9 -0
  283. package/src/structures/TtscGraphDumpEdgeKind.ts +15 -1
  284. package/src/structures/TtscGraphDumpNodeKind.ts +9 -0
  285. package/src/structures/TtscGraphEdgeKind.ts +13 -4
  286. package/src/structures/TtscGraphNodeKind.ts +9 -0
  287. package/src/structures/TtscGraphNodeModifier.ts +9 -0
  288. package/src/structures/ViewerLink.ts +22 -0
  289. package/src/structures/ViewerNode.ts +28 -0
  290. package/src/structures/ViewerPayload.ts +52 -0
  291. package/src/view.ts +208 -110
  292. 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
- /** Atomic validator and assembler for native `ttscgraph` shard transactions. */
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
- /** Validate and atomically commit one complete or base-generation delta. */
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, { digest, shard: upsert.shard });
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 dump;
162
+ return projected;
102
163
  }
103
164
 
104
- /** SHA-256 over the producer's deterministic Go JSON encoding. */
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({ ...shard.source });
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
- edges.push(...shard.edges);
225
- diagnostics.push(...shard.diagnostics);
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
- * Signatures and declaration docs are display facts derived from source text,
13
- * but the live disk is not the snapshot the checker resolved. A file becomes
14
- * readable here only after its current bytes hash to `diskDigest` and the
15
- * compiler-decoded text hashes to `checkerDigest`. Both success and failure are
16
- * cached, so every consumer of a `TtscGraphMemory` sees one adjudication and
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. A missing manifest entry, read failure, or
51
- * digest mismatch is a cached absence rather than a reason to mix current
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
- expected === undefined ||
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) return body.toString("utf16le");
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 { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
2
- import readline from "node:readline";
3
-
4
- /** One plugin sidecar this daemon can be opened against. */
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 child: ChildProcessWithoutNullStreams | undefined;
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
- const run = this.queue.then(() => this.send(verb, invalidate));
56
- this.queue = run.catch(() => undefined);
57
- return run;
78
+ return this.state.ask(verb, invalidate);
58
79
  }
59
80
 
60
- /** Stop the sidecar. Safe to call more than once, and after a failure. */
61
- public close(): void {
62
- this.failed = true;
63
- for (const settle of this.pending.splice(0)) settle(null);
64
- this.lines?.close();
65
- this.lines = undefined;
66
- const child = this.child;
67
- this.child = undefined;
68
- if (child === undefined) return;
69
- child.stdin.end();
70
- child.kill();
71
- }
72
-
73
- private async send(
74
- verb: string,
75
- invalidate: boolean,
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
- }