ttsc 0.31.6 → 0.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/cmd/ttsc/compiler_argument_cwd_test.go +38 -38
  2. package/cmd/ttscserver/main.go +2 -12
  3. package/cmd/ttscserver/resolve_launch_uses_native_flag_authority_test.go +7 -7
  4. package/driver/lsp.go +4 -43
  5. package/driver/rewrite.go +1 -1
  6. package/internal/e2etrace/completion_hints.go +3 -3
  7. package/internal/e2etrace/completion_hints_test.go +26 -12
  8. package/internal/graph/build.go +5 -6
  9. package/internal/graph/graph.go +3 -3
  10. package/internal/graph/line_model.go +2 -35
  11. package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +13 -36
  12. package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +9 -3
  13. package/internal/lspserver/lsp_plugin.go +19 -0
  14. package/internal/lspserver/lsp_proxy.go +9 -129
  15. package/internal/lspserver/lsp_server.go +0 -8
  16. package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +6 -36
  17. package/internal/sourceprocess/windows_target_owns_suspended_admission_test.go +5 -5
  18. package/lib/TtscCompiler.js +1 -1
  19. package/lib/TtscCompiler.js.map +1 -1
  20. package/lib/compiler/internal/ResidentCheckProcess.d.ts +74 -96
  21. package/lib/compiler/internal/ResidentCheckProcess.js +150 -335
  22. package/lib/compiler/internal/ResidentCheckProcess.js.map +1 -1
  23. package/lib/compiler/internal/ResidentCheckProcessHost.d.ts +127 -0
  24. package/lib/compiler/internal/ResidentCheckProcessHost.js +447 -0
  25. package/lib/compiler/internal/ResidentCheckProcessHost.js.map +1 -0
  26. package/lib/compiler/internal/ResidentCheckProcessProtocol.d.ts +64 -0
  27. package/lib/compiler/internal/ResidentCheckProcessProtocol.js +3 -0
  28. package/lib/compiler/internal/ResidentCheckProcessProtocol.js.map +1 -0
  29. package/lib/compiler/internal/build/BuildExecution.d.ts +103 -1
  30. package/lib/compiler/internal/build/BuildExecution.js +92 -12
  31. package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
  32. package/lib/compiler/internal/build/ResidentCheckWatchSession.d.ts +16 -11
  33. package/lib/compiler/internal/build/ResidentCheckWatchSession.js +37 -21
  34. package/lib/compiler/internal/build/ResidentCheckWatchSession.js.map +1 -1
  35. package/lib/compiler/internal/build/RunBuildOptions.d.ts +6 -5
  36. package/lib/compiler/internal/build/TsgoArguments.js.map +1 -1
  37. package/lib/compiler/internal/build/WatchInputPublication.d.ts +59 -0
  38. package/lib/compiler/internal/build/WatchInputPublication.js +93 -0
  39. package/lib/compiler/internal/build/WatchInputPublication.js.map +1 -0
  40. package/lib/compiler/internal/build/runBuild.d.ts +29 -2
  41. package/lib/compiler/internal/build/runBuild.js +45 -19
  42. package/lib/compiler/internal/build/runBuild.js.map +1 -1
  43. package/lib/compiler/internal/buildNativeCompiler.d.ts +2 -0
  44. package/lib/compiler/internal/buildNativeCompiler.js +1 -0
  45. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  46. package/lib/compiler/internal/compileProjectInMemory.js +4 -2
  47. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  48. package/lib/compiler/internal/project/CompilerProjectSelection.d.ts +9 -8
  49. package/lib/compiler/internal/project/CompilerProjectSelection.js +11 -9
  50. package/lib/compiler/internal/project/CompilerProjectSelection.js.map +1 -1
  51. package/lib/compiler/internal/project/resolveOwningProjectConfig.d.ts +25 -8
  52. package/lib/compiler/internal/project/resolveOwningProjectConfig.js +26 -14
  53. package/lib/compiler/internal/project/resolveOwningProjectConfig.js.map +1 -1
  54. package/lib/compiler/internal/readEffectiveCompilerOptions.js +1 -1
  55. package/lib/compiler/internal/readEffectiveCompilerOptions.js.map +1 -1
  56. package/lib/compiler/internal/resolveBinary.d.ts +1 -0
  57. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  58. package/lib/compiler/internal/resolveTsgo.d.ts +6 -0
  59. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  60. package/lib/compiler/internal/runSingleFileEmit.d.ts +32 -4
  61. package/lib/compiler/internal/runSingleFileEmit.js +124 -89
  62. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  63. package/lib/compiler/internal/startResidentTransform.js +8 -2
  64. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  65. package/lib/compiler/internal/transformProjectInMemory.js +11 -3
  66. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  67. package/lib/compiler/internal/transformProjectInWorker.d.ts +1 -1
  68. package/lib/compiler/internal/transformProjectInWorker.js +3 -2
  69. package/lib/compiler/internal/transformProjectInWorker.js.map +1 -1
  70. package/lib/flags/ParseOptions.d.ts +2 -2
  71. package/lib/internal/E2ETrace.js.map +1 -1
  72. package/lib/internal/OwnedNativeProcess.d.ts +2 -2
  73. package/lib/internal/OwnedNativeProcess.js +2 -2
  74. package/lib/internal/SourceNativeRetirement.d.ts +8 -7
  75. package/lib/internal/SourceNativeRetirement.js +19 -10
  76. package/lib/internal/SourceNativeRetirement.js.map +1 -1
  77. package/lib/internal/TtsxNodeOptions.d.ts +79 -0
  78. package/lib/internal/TtsxNodeOptions.js +121 -0
  79. package/lib/internal/TtsxNodeOptions.js.map +1 -0
  80. package/lib/internal/javascriptRuntimeCapabilities.js +4 -1
  81. package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
  82. package/lib/internal/parseWindowsDirectoryCaseSensitivity.d.ts +5 -5
  83. package/lib/internal/parseWindowsDirectoryCaseSensitivity.js +5 -5
  84. package/lib/internal/pathIdentity/createCaseSensitivityObservations.d.ts +35 -0
  85. package/lib/internal/pathIdentity/createCaseSensitivityObservations.js +209 -0
  86. package/lib/internal/pathIdentity/createCaseSensitivityObservations.js.map +1 -0
  87. package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js +2 -143
  88. package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js.map +1 -1
  89. package/lib/internal/projectInputPathIdentity.d.ts +1 -0
  90. package/lib/internal/projectInputPathIdentity.js +1 -0
  91. package/lib/internal/projectInputPathIdentity.js.map +1 -1
  92. package/lib/internal/workerModuleUrl.d.ts +28 -0
  93. package/lib/internal/workerModuleUrl.js +40 -0
  94. package/lib/internal/workerModuleUrl.js.map +1 -0
  95. package/lib/launcher/internal/buildSingleRootProject.js +2 -1
  96. package/lib/launcher/internal/buildSingleRootProject.js.map +1 -1
  97. package/lib/launcher/internal/getCompilerVersionText.js +3 -2
  98. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  99. package/lib/launcher/internal/parseTtsxCLI.d.ts +5 -4
  100. package/lib/launcher/internal/parseTtsxCLI.js +5 -4
  101. package/lib/launcher/internal/parseTtsxCLI.js.map +1 -1
  102. package/lib/launcher/internal/prepareExecution.js +2 -1
  103. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  104. package/lib/launcher/internal/resolveSingleFileOutput.d.ts +8 -9
  105. package/lib/launcher/internal/resolveSingleFileOutput.js +8 -9
  106. package/lib/launcher/internal/resolveSingleFileOutput.js.map +1 -1
  107. package/lib/launcher/internal/runTtsc.js +91 -41
  108. package/lib/launcher/internal/runTtsc.js.map +1 -1
  109. package/lib/launcher/internal/runTtsx.js +5 -2
  110. package/lib/launcher/internal/runTtsx.js.map +1 -1
  111. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.d.ts +10 -6
  112. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js +11 -9
  113. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js.map +1 -1
  114. package/lib/launcher/internal/runtime/RuntimeEntrySources.d.ts +40 -0
  115. package/lib/launcher/internal/runtime/RuntimeEntrySources.js +57 -0
  116. package/lib/launcher/internal/runtime/RuntimeEntrySources.js.map +1 -0
  117. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.d.ts +5 -4
  118. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js +8 -6
  119. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js.map +1 -1
  120. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.d.ts +20 -0
  121. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js +27 -0
  122. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js.map +1 -0
  123. package/lib/launcher/internal/runtime/commonJsExportNames.d.ts +2 -1
  124. package/lib/launcher/internal/runtime/commonJsExportNames.js +2 -1
  125. package/lib/launcher/internal/runtime/commonJsExportNames.js.map +1 -1
  126. package/lib/launcher/internal/runtime/installRuntimeHooks.d.ts +5 -3
  127. package/lib/launcher/internal/runtime/installRuntimeHooks.js +42 -12
  128. package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
  129. package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js +3 -1
  130. package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js.map +1 -1
  131. package/lib/launcher/internal/watch/AsyncWatchTopology.d.ts +113 -0
  132. package/lib/launcher/internal/watch/AsyncWatchTopology.js +232 -0
  133. package/lib/launcher/internal/watch/AsyncWatchTopology.js.map +1 -0
  134. package/lib/launcher/internal/watch/WatchSettlement.d.ts +49 -0
  135. package/lib/launcher/internal/watch/WatchSettlement.js +70 -0
  136. package/lib/launcher/internal/watch/WatchSettlement.js.map +1 -0
  137. package/lib/launcher/internal/watch/WatchTopology.d.ts +103 -13
  138. package/lib/launcher/internal/watch/WatchTopology.js +380 -95
  139. package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
  140. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.d.ts +121 -0
  141. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js +3 -0
  142. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js.map +1 -0
  143. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.d.ts +4 -1
  144. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js +3 -2
  145. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js.map +1 -1
  146. package/lib/launcher/internal/watch/serveWatchTopologyWorker.d.ts +19 -0
  147. package/lib/launcher/internal/watch/serveWatchTopologyWorker.js +62 -0
  148. package/lib/launcher/internal/watch/serveWatchTopologyWorker.js.map +1 -0
  149. package/lib/launcher/internal/watch/watchTopologyWorker.d.ts +1 -0
  150. package/lib/launcher/internal/watch/watchTopologyWorker.js +22 -0
  151. package/lib/launcher/internal/watch/watchTopologyWorker.js.map +1 -0
  152. package/lib/plugin/CapabilityPluginResolver.d.ts +1 -1
  153. package/lib/plugin/CapabilityPluginResolver.js +3 -2
  154. package/lib/plugin/CapabilityPluginResolver.js.map +1 -1
  155. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +15 -12
  156. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +13 -11
  157. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
  158. package/lib/plugin/internal/load/PluginPackageResolution.d.ts +2 -2
  159. package/lib/plugin/internal/load/PluginPackageResolution.js +13 -7
  160. package/lib/plugin/internal/load/PluginPackageResolution.js.map +1 -1
  161. package/lib/plugin/internal/load/ProjectPluginEntries.d.ts +23 -7
  162. package/lib/plugin/internal/load/ProjectPluginEntries.js +36 -14
  163. package/lib/plugin/internal/load/ProjectPluginEntries.js.map +1 -1
  164. package/lib/plugin/internal/load/collectProjectHostInputs.d.ts +3 -1
  165. package/lib/plugin/internal/load/collectProjectHostInputs.js +5 -2
  166. package/lib/plugin/internal/load/collectProjectHostInputs.js.map +1 -1
  167. package/lib/plugin/internal/load/hasProjectPluginEntries.d.ts +3 -1
  168. package/lib/plugin/internal/load/hasProjectPluginEntries.js +4 -2
  169. package/lib/plugin/internal/load/hasProjectPluginEntries.js.map +1 -1
  170. package/lib/plugin/internal/load/loadProjectPlugins.d.ts +36 -5
  171. package/lib/plugin/internal/load/loadProjectPlugins.js +477 -421
  172. package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
  173. package/lib/plugin/internal/load/pluginResolutionFailure.d.ts +18 -0
  174. package/lib/plugin/internal/load/pluginResolutionFailure.js +32 -0
  175. package/lib/plugin/internal/load/pluginResolutionFailure.js.map +1 -0
  176. package/lib/plugin/internal/load/resolveNativeSource.js +2 -3
  177. package/lib/plugin/internal/load/resolveNativeSource.js.map +1 -1
  178. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +1 -1
  179. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +3 -2
  180. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
  181. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.d.ts +4 -1
  182. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +9 -2
  183. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js.map +1 -1
  184. package/lib/structures/ITtscCompilerContext.d.ts +12 -12
  185. package/lib/structures/ITtscPlugin.d.ts +7 -5
  186. package/lib/structures/ITtscPluginFactoryContext.d.ts +9 -8
  187. package/lib/structures/internal/TtscCommonOptions.d.ts +2 -2
  188. package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +12 -6
  189. package/package.json +9 -9
  190. package/src/TtscCompiler.ts +1 -1
  191. package/src/compiler/internal/ResidentCheckProcess.ts +159 -374
  192. package/src/compiler/internal/ResidentCheckProcessHost.ts +502 -0
  193. package/src/compiler/internal/ResidentCheckProcessProtocol.ts +53 -0
  194. package/src/compiler/internal/build/BuildExecution.ts +123 -11
  195. package/src/compiler/internal/build/ResidentCheckWatchSession.ts +41 -24
  196. package/src/compiler/internal/build/RunBuildOptions.ts +9 -5
  197. package/src/compiler/internal/build/TsgoArguments.ts +4 -1
  198. package/src/compiler/internal/build/WatchInputPublication.ts +106 -0
  199. package/src/compiler/internal/build/runBuild.ts +58 -34
  200. package/src/compiler/internal/buildNativeCompiler.ts +4 -0
  201. package/src/compiler/internal/compileProjectInMemory.ts +8 -2
  202. package/src/compiler/internal/project/CompilerProjectSelection.ts +14 -14
  203. package/src/compiler/internal/project/resolveOwningProjectConfig.ts +42 -16
  204. package/src/compiler/internal/readEffectiveCompilerOptions.ts +1 -1
  205. package/src/compiler/internal/resolveBinary.ts +4 -1
  206. package/src/compiler/internal/resolveTsgo.ts +7 -0
  207. package/src/compiler/internal/runSingleFileEmit.ts +145 -101
  208. package/src/compiler/internal/startResidentTransform.ts +9 -2
  209. package/src/compiler/internal/transformProjectInMemory.ts +12 -3
  210. package/src/compiler/internal/transformProjectInWorker.ts +10 -2
  211. package/src/flags/ParseOptions.ts +2 -2
  212. package/src/internal/E2ETrace.ts +3 -1
  213. package/src/internal/OwnedNativeProcess.ts +2 -2
  214. package/src/internal/SourceNativeRetirement.ts +42 -19
  215. package/src/internal/TtsxNodeOptions.ts +124 -0
  216. package/src/internal/javascriptRuntimeCapabilities.ts +6 -1
  217. package/src/internal/parseWindowsDirectoryCaseSensitivity.ts +5 -5
  218. package/src/internal/pathIdentity/createCaseSensitivityObservations.ts +242 -0
  219. package/src/internal/pathIdentity/createFilesystemPathIdentityContext.ts +2 -169
  220. package/src/internal/projectInputPathIdentity.ts +1 -0
  221. package/src/internal/workerModuleUrl.ts +34 -0
  222. package/src/launcher/internal/buildSingleRootProject.ts +2 -0
  223. package/src/launcher/internal/getCompilerVersionText.ts +3 -2
  224. package/src/launcher/internal/parseTtsxCLI.ts +5 -4
  225. package/src/launcher/internal/prepareExecution.ts +6 -1
  226. package/src/launcher/internal/resolveSingleFileOutput.ts +8 -9
  227. package/src/launcher/internal/runTtsc.ts +127 -49
  228. package/src/launcher/internal/runTtsx.ts +17 -8
  229. package/src/launcher/internal/runtime/CommonJsRuntimeSource.ts +11 -9
  230. package/src/launcher/internal/runtime/RuntimeEntrySources.ts +57 -0
  231. package/src/launcher/internal/runtime/RuntimeProjectOwnership.ts +23 -10
  232. package/src/launcher/internal/runtime/advertiseTypeScriptExtensions.ts +25 -0
  233. package/src/launcher/internal/runtime/commonJsExportNames.ts +11 -4
  234. package/src/launcher/internal/runtime/installRuntimeHooks.ts +62 -16
  235. package/src/launcher/internal/runtime/resolveRuntimeCleanTargets.ts +17 -5
  236. package/src/launcher/internal/watch/AsyncWatchTopology.ts +267 -0
  237. package/src/launcher/internal/watch/WatchSettlement.ts +67 -0
  238. package/src/launcher/internal/watch/WatchTopology.ts +461 -102
  239. package/src/launcher/internal/watch/WatchTopologyWorkerProtocol.ts +106 -0
  240. package/src/launcher/internal/watch/projectInputReloadEventShouldNotify.ts +14 -9
  241. package/src/launcher/internal/watch/serveWatchTopologyWorker.ts +75 -0
  242. package/src/launcher/internal/watch/watchTopologyWorker.ts +29 -0
  243. package/src/plugin/CapabilityPluginResolver.ts +9 -2
  244. package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +17 -12
  245. package/src/plugin/internal/load/PluginPackageResolution.ts +14 -9
  246. package/src/plugin/internal/load/ProjectPluginEntries.ts +40 -15
  247. package/src/plugin/internal/load/collectProjectHostInputs.ts +7 -1
  248. package/src/plugin/internal/load/hasProjectPluginEntries.ts +8 -3
  249. package/src/plugin/internal/load/loadProjectPlugins.ts +683 -593
  250. package/src/plugin/internal/load/pluginResolutionFailure.ts +37 -0
  251. package/src/plugin/internal/load/resolveNativeSource.ts +10 -4
  252. package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +8 -2
  253. package/src/plugin/internal/source/pluginModuleReplaceDirectories.ts +10 -1
  254. package/src/structures/ITtscCompilerContext.ts +12 -12
  255. package/src/structures/ITtscPlugin.ts +7 -5
  256. package/src/structures/ITtscPluginFactoryContext.ts +9 -8
  257. package/src/structures/internal/TtscCommonOptions.ts +2 -2
  258. package/src/structures/internal/TtscSingleFileEmitOptions.ts +22 -11
  259. package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +0 -78
  260. package/internal/graphsymbols/provider.go +0 -650
  261. package/internal/lspserver/lsp_symbols.go +0 -212
@@ -0,0 +1,267 @@
1
+ import path from "node:path";
2
+ import { Worker } from "node:worker_threads";
3
+
4
+ import { SidecarEnvironment } from "../../../compiler/internal/sharedHost/SidecarEnvironment";
5
+ import { workerModuleUrl } from "../../../internal/workerModuleUrl";
6
+ import type { PluginContentIdentities } from "../../../plugin/internal/source/PluginContentIdentities";
7
+ import type { ITtscProjectInputSnapshot } from "../../../structures/internal/ITtscProjectInputSnapshot";
8
+ import type { WatchTopologyWorkerProtocol } from "./WatchTopologyWorkerProtocol";
9
+
10
+ /**
11
+ * Keep one watch topology and its native subscriptions on a worker thread.
12
+ *
13
+ * Case-policy probes, content digests and compiler listings must retain their
14
+ * actual observations. Replacing them with path-only case caches or size/mtime
15
+ * digests can miss directory replacements and source edits. They instead run
16
+ * off the launcher's loop so their cost cannot delay resident-host retirement
17
+ * or queued build decisions (#1758). This moves the cost; it does not remove
18
+ * it. Options and publications are cloned, while callbacks stay on the caller.
19
+ *
20
+ * Await a publication before the build reads its inputs. Moving that baseline
21
+ * past the build could absorb an intervening edit (#1728). Callback messages
22
+ * and completion replies share one FIFO channel, so whenIdle completes after
23
+ * the notifications its barrier observed have reached the launcher (#1756).
24
+ *
25
+ * @evidence contracts/common.md#principled-implementation A dedicated supported worker executes the unchanged topology algorithm on selected cloneable data while callbacks remain caller-owned; worker events and correlated replies settle actual operations, and one FIFO channel orders callbacks before completion.
26
+ * @evidence contracts/common.md#clear-and-simple-design The facade separates option capture, reply dispatch and worker lifetime; the synchronous core retains filesystem policy and watcher ownership.
27
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Existing native case observations, compiler membership and content proofs remain authoritative. The explicit worker factory supplies transport without replacing globals, and the default starts the actual packaged CommonJS entry without a synchronous barrier or metadata shortcut.
28
+ * @evidence contracts/common.md#meaningful-documentation Native prose explains off-thread cost, baseline-before-build ordering, FIFO replies and lifetime failures; constructor prose identifies data capture, callback ownership and the optional factory.
29
+ * @evidence contracts/portability.md#os-neutral-implementation Native joins locate the packaged CommonJS entry, and the supported module bootstrap loads it through createRequire while retaining Node-owned inherited execution options and loader hooks. Structured cloning preserves maps and bigint device references; SidecarEnvironment merges native environment names before startup.
30
+ * @evidence contracts/performance.md#efficient-algorithms One thread serves the topology lifetime. Construction copies fixed option references before cloning, with costs for option/environment bytes and module initialization; each operation clones its publication bytes and adds one pending entry while the core retains native costs.
31
+ * @evidence contracts/performance.md#reuse-equivalent-work Construction starts a fresh worker lifetime; that worker retains current registrations, listing premises and baselines across requests, but completion grants no reuse for another filesystem generation.
32
+ * @evidence contracts/performance.md#bound-retention-and-release-resources One referenced worker, listeners, exit promise and uncapped pending map belong to the facade; creation failure precedes ownership transfer. Replies prune entries and failure rejects all entries. Close joins worker exit after native subscription close; failed close terminates and joins the thread while retaining error. Requests have no implicit deadline.
33
+ */
34
+ export class AsyncWatchTopology {
35
+ private readonly worker: Worker;
36
+ private readonly exited: Promise<void>;
37
+ private readonly pending = new Map<
38
+ number,
39
+ { resolve(): void; reject(error: unknown): void }
40
+ >();
41
+ private sequence = 0;
42
+ private failure: unknown;
43
+ private failed = false;
44
+ private terminating = false;
45
+ private closing: Promise<void> | undefined;
46
+
47
+ /**
48
+ * Capture data options and start the worker; callbacks remain caller-owned.
49
+ * The optional factory observes worker transport without replacing globals.
50
+ */
51
+ public constructor(
52
+ options: WatchTopologyWorkerProtocol.Options,
53
+ private readonly callbacks: WatchTopologyWorkerProtocol.Callbacks,
54
+ createWorker: (options: WatchTopologyWorkerProtocol.Options) => Worker = (
55
+ selected,
56
+ ) =>
57
+ new Worker(
58
+ workerModuleUrl(
59
+ path.join(
60
+ __dirname,
61
+ `watchTopologyWorker${path.extname(__filename)}`,
62
+ ),
63
+ ),
64
+ {
65
+ env: SidecarEnvironment.merge(process.env, selected.env),
66
+ workerData: selected,
67
+ },
68
+ ),
69
+ ) {
70
+ // Callers also carry build callbacks. Select only the core's data options:
71
+ // sending the invocation wholesale would attempt to clone those functions.
72
+ const {
73
+ binary,
74
+ compilerArgsCwd,
75
+ compilerProjectSelections,
76
+ cwd,
77
+ emit,
78
+ env,
79
+ files,
80
+ outDir,
81
+ passthrough,
82
+ pinCompilerProject,
83
+ projectRoot,
84
+ tsconfig,
85
+ } = options;
86
+ this.worker = createWorker({
87
+ binary,
88
+ compilerArgsCwd,
89
+ compilerProjectSelections,
90
+ cwd,
91
+ emit,
92
+ env,
93
+ files,
94
+ outDir,
95
+ passthrough,
96
+ pinCompilerProject,
97
+ projectRoot,
98
+ tsconfig,
99
+ });
100
+ this.worker.on("message", (reply: WatchTopologyWorkerProtocol.Reply) => {
101
+ if ("id" in reply) {
102
+ const waiting = this.pending.get(reply.id);
103
+ if (waiting === undefined) return;
104
+ this.pending.delete(reply.id);
105
+ if (reply.ok) waiting.resolve();
106
+ else waiting.reject(reply.error);
107
+ return;
108
+ }
109
+ switch (reply.event) {
110
+ case "input":
111
+ this.callbacks.onInputChange(reply.change);
112
+ break;
113
+ case "topology":
114
+ this.callbacks.onTopologyChange();
115
+ break;
116
+ case "error":
117
+ this.callbacks.onError(reply.location, reply.error);
118
+ break;
119
+ case "roots":
120
+ this.callbacks.onProjectInputWatchRoots?.(reply.roots);
121
+ break;
122
+ case "unavailable":
123
+ this.callbacks.onProjectInputWatchUnavailable?.(reply.roots);
124
+ break;
125
+ }
126
+ });
127
+ this.worker.on("error", (error) => this.fail(error));
128
+ this.exited = new Promise<void>((resolve) => {
129
+ this.worker.once("exit", (code) => {
130
+ if (
131
+ this.closing === undefined ||
132
+ this.pending.size !== 0 ||
133
+ (code !== 0 && !this.terminating)
134
+ )
135
+ this.fail(
136
+ new Error(`ttsc: watch topology worker exited with code ${code}`),
137
+ );
138
+ resolve();
139
+ });
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Reconcile actual compiler membership and wait for its publication.
145
+ *
146
+ * @evidence contracts/common.md#principled-implementation The worker executes the core's actual refresh before replying, so completion does not stand for a predicted membership.
147
+ * @evidence contracts/common.md#clear-and-simple-design This operation frames one refresh and delegates transport correlation to request.
148
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Membership still comes from the core's native compiler-input reader.
149
+ * @evidence contracts/common.md#meaningful-documentation Prose states the membership and publication boundary.
150
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The method frames a boolean; the worker and core own native execution.
151
+ * @evidence contracts/performance.md#efficient-algorithms Fixed-size framing adds one pending entry; the core retains listing, traversal and hashing costs off-thread.
152
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Every requested refresh executes; listing reuse remains the core's decision.
153
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Correlation remains live until reply or worker failure; no deadline is imposed.
154
+ */
155
+ public refresh(notify: boolean): Promise<void> {
156
+ return this.request({ method: "refresh", notify });
157
+ }
158
+
159
+ /**
160
+ * Capture plugin baselines before the caller starts reading or building.
161
+ *
162
+ * @evidence contracts/common.md#principled-implementation Awaited completion follows the core's baseline capture and preserves the supplied record store's separability references through structured cloning.
163
+ * @evidence contracts/common.md#clear-and-simple-design One publication carries inputs and their existing proof store together.
164
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The actual content identity owner still judges every record; transport invents no digest or metadata equivalence.
165
+ * @evidence contracts/common.md#meaningful-documentation Prose states the required pre-build ordering.
166
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Paths and bigint maps are cloned as data; the core owns native interpretation.
167
+ * @evidence contracts/performance.md#efficient-algorithms Framing is fixed structural work and cloning visits input paths and record references; digest and registration work stays with the core.
168
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each publication reaches the core, which owns baseline and listing reuse premises.
169
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Publication data and one pending correlation remain live through completion or worker failure without a size or deadline cap.
170
+ */
171
+ public setExtraInputs(
172
+ inputs: readonly string[],
173
+ identities?: PluginContentIdentities.Store,
174
+ ): Promise<void> {
175
+ return this.request({ method: "setExtraInputs", inputs, identities });
176
+ }
177
+
178
+ /**
179
+ * Capture rule-input baselines before the caller consumes those inputs.
180
+ *
181
+ * @evidence contracts/common.md#principled-implementation Completion follows actual declaration normalization, population capture and subscription reconciliation in the core.
182
+ * @evidence contracts/common.md#clear-and-simple-design One operation carries the complete declared snapshot without splitting population and baseline ownership.
183
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The core retains missing-path, alias and empty-population semantics; transport filters no inputs.
184
+ * @evidence contracts/common.md#meaningful-documentation Prose states capture timing relative to consumption.
185
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The snapshot crosses as data; the core owns filesystem identity and case policy.
186
+ * @evidence contracts/performance.md#efficient-algorithms Cloning visits the snapshot's path text; matching, fingerprinting and registrations keep their owning core costs.
187
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work The core decides whether a publication is equivalent; transport reuses no earlier result.
188
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Snapshot bytes and one pending correlation remain live until reply or worker failure without a population cap.
189
+ */
190
+ public setProjectInputs(inputs: ITtscProjectInputSnapshot): Promise<void> {
191
+ return this.request({ method: "setProjectInputs", inputs });
192
+ }
193
+
194
+ /**
195
+ * Wait for delivered topology work and its preceding callback messages.
196
+ *
197
+ * @evidence contracts/common.md#principled-implementation The worker's core barrier observes its scheduled owners, and callbacks sent before the completion reply arrive first on the same FIFO channel.
198
+ * @evidence contracts/common.md#clear-and-simple-design One request exposes the core's complete queue ownership instead of duplicating queue flags here.
199
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Actual queue completion replaces elapsed quiet-time estimates.
200
+ * @evidence contracts/common.md#meaningful-documentation Prose identifies delivered work and callback ordering.
201
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The operation uses portable MessagePort ordering without a native path or process call.
202
+ * @evidence contracts/performance.md#efficient-algorithms Fixed-size framing adds one correlation; the core checks flags per yielded turn and still pays its pending operations' costs.
203
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each barrier observes current pending work rather than a previous receipt.
204
+ * @evidence contracts/performance.md#bound-retention-and-release-resources One request remains live until idle, close or failure; continuous new work can keep it pending without an implicit deadline.
205
+ */
206
+ public whenIdle(): Promise<void> {
207
+ return this.request({ method: "whenIdle" });
208
+ }
209
+
210
+ /**
211
+ * Close subscriptions and join the thread. Repeated calls share retirement;
212
+ * close or worker failures remain rejection even after thread termination.
213
+ *
214
+ * @evidence contracts/common.md#principled-implementation Successful close follows core subscription release, outstanding barrier answers and actual worker exit; failed close terminates and joins the thread while preserving rejection.
215
+ * @evidence contracts/common.md#clear-and-simple-design One retained promise owns idempotent shutdown and finishClose owns its two retirement paths.
216
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Shutdown joins actual thread exit rather than treating a posted close as completion.
217
+ * @evidence contracts/common.md#meaningful-documentation Prose states joined completion, repeated-call sharing and failure retention.
218
+ * @evidence contracts/portability.md#os-neutral-implementation Node's supported worker exit and termination APIs own thread retirement; the core owns backend subscription closure.
219
+ * @evidence contracts/performance.md#efficient-algorithms One close message and shared promise add fixed structural work; subscription traversal, outstanding replies and thread retirement retain delegated costs.
220
+ * @evidence contracts/performance.md#reuse-equivalent-work Every repeated close shares this lifetime's single retirement result, including rejection.
221
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Closure rejects new requests and joins the worker; pending requests settle by replies or terminal failure. Native FSEvents stop errors are suppressed by its existing backend, so thread joining alone cannot certify that optional binding's physical cleanup.
222
+ */
223
+ public close(): Promise<void> {
224
+ this.closing ??= this.finishClose();
225
+ return this.closing;
226
+ }
227
+
228
+ private async finishClose(): Promise<void> {
229
+ try {
230
+ await this.request({ method: "close" });
231
+ } catch (error) {
232
+ this.terminating = true;
233
+ await this.worker.terminate();
234
+ await this.exited;
235
+ throw error;
236
+ }
237
+ await this.exited;
238
+ if (this.failed) throw this.failure;
239
+ }
240
+
241
+ private request(
242
+ operation: WatchTopologyWorkerProtocol.Operation,
243
+ ): Promise<void> {
244
+ if (this.failed) return Promise.reject(this.failure);
245
+ if (this.closing !== undefined)
246
+ return Promise.reject(new Error("ttsc: watch topology is closing"));
247
+ const id = ++this.sequence;
248
+ return new Promise<void>((resolve, reject) => {
249
+ this.pending.set(id, { reject, resolve });
250
+ try {
251
+ this.worker.postMessage({ ...operation, id });
252
+ } catch (error) {
253
+ this.pending.delete(id);
254
+ reject(error);
255
+ }
256
+ });
257
+ }
258
+
259
+ private fail(error: unknown): void {
260
+ if (this.failed) return;
261
+ this.failed = true;
262
+ this.failure = error;
263
+ for (const waiting of this.pending.values()) waiting.reject(error);
264
+ this.pending.clear();
265
+ this.callbacks.onFailure(error);
266
+ }
267
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Waits for an owner's idle observation without losing changes during that
3
+ * observation. The owner announces every scheduling and terminal transition.
4
+ *
5
+ * Elapsed silence cannot certify idle: a queued build or an asynchronous input
6
+ * decision can remain silent until it starts. A changed revision therefore
7
+ * requires another observation, even when the earlier one reported idle.
8
+ *
9
+ * @evidence contracts/common.md#principled-implementation A monotonic revision detects transitions across an asynchronous idle query; wait registration and the final revision comparison share one JavaScript turn, preventing a lost wake.
10
+ * @evidence contracts/common.md#clear-and-simple-design The owner supplies its actual idle predicate and announces state changes; this class owns only revision validation and waiting callbacks.
11
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No timeout, retry limit, process hook or consumer-specific decision substitutes for the owner's state.
12
+ * @evidence contracts/common.md#meaningful-documentation Prose defines idle-query ownership and why silent queued work and intervening changes require a fresh observation; methods describe notification and failure effects.
13
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Revisions and promises have no native path, filesystem or process boundary.
14
+ * @evidence contracts/performance.md#efficient-algorithms Each transition visits current waiters once; each observation is constant bookkeeping plus the supplied owner's work, without polling.
15
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each wait observes mutable owner state independently; no computed result is cached across transitions.
16
+ * @evidence contracts/performance.md#bound-retention-and-release-resources A non-idle wait registers one callback until the next transition; an in-flight idle query or publication retains its call closure until settlement. Errors reject directly. The owner must announce terminal transitions so registered callbacks can recheck and reject. Concurrent caller population has no fixed cap.
17
+ */
18
+ export class WatchSettlement {
19
+ private revision = 0;
20
+ private readonly waiters = new Set<() => void>();
21
+
22
+ /**
23
+ * Announces a scheduling or terminal transition and releases current waits.
24
+ *
25
+ * @evidence contracts/common.md#principled-implementation Advancing the revision invalidates queries already in flight, and releasing the current callback population permits each pending wait to query again.
26
+ * @evidence contracts/common.md#clear-and-simple-design One notification handles both ordinary scheduling and terminal transitions without interpreting the owner's state.
27
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Callbacks observe a real owner transition without polling or replacing owner operations.
28
+ * @evidence contracts/common.md#meaningful-documentation The prose states which transitions callers announce and that current waits are released.
29
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation This in-memory notification has no native boundary.
30
+ * @evidence contracts/performance.md#efficient-algorithms Copying and waking current callbacks costs linear time and temporary space in pending waits.
31
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work This effectful transition does not compute a reusable result.
32
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Clearing the set releases its references before callbacks run; each resumed wait owns any later registration.
33
+ */
34
+ public changed(): void {
35
+ ++this.revision;
36
+ const waiters = [...this.waiters];
37
+ this.waiters.clear();
38
+ for (const wake of waiters) wake();
39
+ }
40
+
41
+ /**
42
+ * Publishes and resolves after an idle query spans no transition; errors
43
+ * reject. The owner must reject its query after shutdown and announce that
44
+ * shutdown. Publication starts in the same turn as the final revision check.
45
+ *
46
+ * @evidence contracts/common.md#principled-implementation The revision comparison validates the complete awaited observation; publication starts synchronously after that comparison, and a non-idle result registers its wake in the same turn, so neither path loses an intervening transition.
47
+ * @evidence contracts/common.md#clear-and-simple-design The loop retries only after a real transition and delegates all idle and terminal meaning to the supplied owner query.
48
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No elapsed silence, deadline or fixed retry count stands for the owner's actual pending work.
49
+ * @evidence contracts/common.md#meaningful-documentation The prose describes successful observation, same-turn publication, error propagation and the terminal notification obligation.
50
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Awaiting an owner query imposes no native path or process policy.
51
+ * @evidence contracts/performance.md#efficient-algorithms Each query adds constant revision bookkeeping and at most one callback; query work belongs to the supplied owner, and no timer polls state.
52
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Queries observe mutable state and are not cached across transitions.
53
+ * @evidence contracts/performance.md#bound-retention-and-release-resources A non-idle observation retains one resolver until changed clears it; successful and failed queries retain no registered callback, while an in-flight query or publication retains this call until settlement. Terminal wake and rejection are the owner's obligation.
54
+ */
55
+ public async wait(
56
+ idle: () => Promise<boolean>,
57
+ publish: () => void | Promise<void> = () => {},
58
+ ): Promise<void> {
59
+ for (;;) {
60
+ const revision = this.revision;
61
+ const observed = await idle();
62
+ if (revision !== this.revision) continue;
63
+ if (observed) return await publish();
64
+ await new Promise<void>((resolve) => this.waiters.add(resolve));
65
+ }
66
+ }
67
+ }