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
@@ -68,11 +68,13 @@ export function compileProjectInMemory(options: ITtscCompilerContext): {
68
68
  return compileProjectWithPlugins(options, cwd, project);
69
69
  }
70
70
  const tsconfig = project.path;
71
+ const env = inheritedSidecarEnv(options.env, options.binary);
71
72
  const binary = buildNativeCompiler({
72
73
  cacheBaseDir: project.root,
73
74
  cacheDir:
74
75
  options.cacheDir ??
75
76
  SidecarEnvironment.read(options.env, "TTSC_CACHE_DIR"),
77
+ env,
76
78
  packageRoot: packageRootDir(),
77
79
  });
78
80
  const res = spawnNative(
@@ -80,7 +82,7 @@ export function compileProjectInMemory(options: ITtscCompilerContext): {
80
82
  ["api-compile", "--cwd", project.root, "--tsconfig", tsconfig],
81
83
  {
82
84
  cwd: project.root,
83
- env: inheritedSidecarEnv(options.env, options.binary),
85
+ env,
84
86
  },
85
87
  );
86
88
  if (res.error) {
@@ -110,7 +112,11 @@ function hasConfiguredPlugins(
110
112
  options: ITtscCompilerContext,
111
113
  project: ITtscParsedProjectConfig,
112
114
  ): boolean {
113
- return hasProjectPluginEntries(project, options.plugins);
115
+ return hasProjectPluginEntries(
116
+ project,
117
+ options.plugins,
118
+ options.pluginConfigDir,
119
+ );
114
120
  }
115
121
 
116
122
  /**
@@ -24,11 +24,11 @@ export namespace CompilerProjectSelection {
24
24
  * Resolve one compiler project without rebasing its original argument frame.
25
25
  *
26
26
  * Validated visible selectors retain their positions beside original response
27
- * tokens. The existing observed inspector projects those frames for selection;
28
- * the native producer still receives their original bytes and owns validation.
29
- * Positional discovery starts at the supplied file until an observed project
30
- * assignment selects another config. Explicit resolvedProject callers retain
31
- * their independent API authority.
27
+ * tokens. The existing observed inspector projects those frames for
28
+ * selection; the native producer still receives their original bytes and owns
29
+ * validation. Positional discovery starts at the supplied file until an
30
+ * observed project assignment selects another config. Explicit
31
+ * resolvedProject callers retain their independent API authority.
32
32
  * Observations are sequential and do not pin bytes across a later producer.
33
33
  *
34
34
  * @evidence contracts/common.md#principled-implementation Existing schema occurrence metadata and observed response inspection establish ordered canonical project assignments; one selected config is returned separately from the retained compiler argument cwd.
@@ -49,7 +49,8 @@ export namespace CompilerProjectSelection {
49
49
  },
50
50
  ) {
51
51
  const cwd = path.resolve(options.cwd ?? process.cwd());
52
- const file = options.file ??
52
+ const file =
53
+ options.file ??
53
54
  (options.files?.length === 1 ? options.files[0] : undefined);
54
55
  let initial = options.resolvedProject?.identity;
55
56
  let initialError: unknown;
@@ -117,7 +118,8 @@ export namespace CompilerProjectSelection {
117
118
  if (responseFrames.length !== 0 && inspectionError === undefined) {
118
119
  projectGuard = ["-p", project.path];
119
120
  } else if (
120
- options.resolvedProject !== undefined && assignments.has("project")
121
+ options.resolvedProject !== undefined &&
122
+ assignments.has("project")
121
123
  ) {
122
124
  // A reporting/selection guard must not become a missing top-level scalar
123
125
  // operand. An unsafe native request retains its original diagnostic frame.
@@ -141,8 +143,8 @@ export namespace CompilerProjectSelection {
141
143
  }
142
144
 
143
145
  /**
144
- * Reject a changed response generation before a consumer publishes or runs it.
145
- * This is a sequential reobservation, not an atomic producer snapshot.
146
+ * Reject a changed response generation before a consumer publishes or runs
147
+ * it. This is a sequential reobservation, not an atomic producer snapshot.
146
148
  *
147
149
  * @evidence contracts/common.md#principled-implementation Current physical/content observations must match every response input admitted by this selection before its consumer proceeds.
148
150
  * @evidence contracts/common.md#clear-and-simple-design One loop delegates actual file observation to the existing owner and propagates read failures.
@@ -154,10 +156,7 @@ export namespace CompilerProjectSelection {
154
156
  * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Synchronous observation retains no handle or history; the selection belongs to its caller.
155
157
  */
156
158
  export function assertCurrent(
157
- selection: Pick<
158
- ReturnType<typeof read>,
159
- "observations"
160
- >,
159
+ selection: Pick<ReturnType<typeof read>, "observations">,
161
160
  ): void {
162
161
  for (const [file, observation] of selection.observations)
163
162
  if (CompilerArgumentsInspection.observeInputFile(file) !== observation)
@@ -169,7 +168,8 @@ export namespace CompilerProjectSelection {
169
168
  /**
170
169
  * Pin an established project only after the current forwarded frame is safe.
171
170
  * A phase may filter or extend argv after context acquisition; it must retain
172
- * that live payload without letting a final flag satisfy an incomplete scalar.
171
+ * that live payload without letting a final flag satisfy an incomplete
172
+ * scalar.
173
173
  *
174
174
  * @evidence contracts/common.md#principled-implementation An unchanged observed frame reuses its admitted guard; changed phase arguments receive the existing conservative inspection before the selected project can be pinned.
175
175
  * @evidence contracts/common.md#clear-and-simple-design One guard reader preserves live argv and the context's selected project without duplicating option arity or response grammar.
@@ -39,16 +39,21 @@ import { selectReferencedProject } from "./selectReferencedProject";
39
39
  * realpaths and case capabilities, retaining best-effort spelling when native
40
40
  * identity lookup is unavailable.
41
41
  *
42
- * Graph observations are shared only within this lookup. Later requests can see
43
- * changed configs, inherited options, directory membership or compiler inputs;
44
- * a config pathname alone cannot establish that the expansion is still valid.
45
- * The callback observes direct config reads, not every inherited config or
46
- * compiler input consulted by expansion. Synchronous compiler work has no
47
- * explicit execution deadline in this operation.
42
+ * Graph observations are shared only within this lookup, unless the caller
43
+ * hands in `expansions`, which keeps each config's expanded roots for as long
44
+ * as the caller keeps the map. Later requests can otherwise see changed
45
+ * configs, inherited options, directory membership or compiler inputs; a config
46
+ * pathname alone cannot establish that the expansion is still valid. A file
47
+ * created after a kept expansion was taken is still expanded for afresh. The
48
+ * callback observes direct config reads, not every inherited config or compiler
49
+ * input consulted by expansion. Synchronous compiler work has no explicit
50
+ * execution deadline in this operation.
48
51
  *
49
52
  * @param props.tsconfig - The config project discovery found for the file.
50
53
  * @param props.file - The file whose project is asked for.
51
54
  * @param props.binary - An explicit TypeScript-Go binary, when one was given.
55
+ * @param props.env - Complete environment shared by compiler selection and
56
+ * root-file expansion; omission inherits the current process environment.
52
57
  * @param props.onConfig - Called with every config this reads, so a caller that
53
58
  * fingerprints its inputs can record them.
54
59
  * @evidence contracts/common.md#principled-implementation Successful compiler showConfig supplies root membership observations, best-effort filesystem identity compares aliases, and visited identity keys terminate repeated graph states while declaration-order DFS selects the first observed containing project. Failed observations preserve the discovered fallback without certifying membership.
@@ -57,8 +62,8 @@ import { selectReferencedProject } from "./selectReferencedProject";
57
62
  * @evidence contracts/common.md#meaningful-documentation Native paragraphs state ownership order, fallback, callbacks and lookup-scoped reuse, with parameter explanations separated from acknowledgments.
58
63
  * @evidence contracts/portability.md#os-neutral-implementation Node path operations select native config spellings, actual filesystem identity compares target and roots, and spawnNative owns executable representation rather than applying OS-name case guesses.
59
64
  * @evidence contracts/performance.md#efficient-algorithms Each distinct observed config identity key is visited once in the DFS and receives at most one expansion plus one ctime-based retry; ctime can change for metadata updates as well as creation. Direct config reads/parsing, reference path text/stat work, delegated identity/case probes and binary selection precede or accompany compiler execution. Root-list decoding and membership scans cost their bytes and entries; DFS stack depth follows the reference chain and native recursion limits still apply. The no-reference path avoids a compiler subprocess, not config I/O.
60
- * @evidence contracts/performance.md#reuse-equivalent-work The lookup reuses discovered reference edges and filesystem identity observations; visited keys prevent expansion of a repeated observed identity, while later requests rerun because paths alone cannot prove unchanged inheritance, directory membership or compiler selection. Best-effort identity cannot guarantee deduplication of every physical alias.
61
- * @evidence contracts/performance.md#bound-retention-and-release-resources Identity maps, visited keys and root observations are lookup-local with no historical population retained here. Synchronous expansion delegates capture cleanup to spawnNative's finally path, whose cleanup failures are suppressed; output storage and child duration have no configured ceiling here. A nonterminating child prevents the lookup from finishing, rather than being released by this graph traversal.
65
+ * @evidence contracts/performance.md#reuse-equivalent-work The lookup reuses discovered reference edges and filesystem identity observations; visited keys prevent expansion of a repeated observed identity, while later requests rerun because paths alone cannot prove unchanged inheritance, directory membership or compiler selection, unless the caller keeps expansions for a lifetime its own builds already assume. Best-effort identity cannot guarantee deduplication of every physical alias.
66
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Identity maps, visited keys and root observations are lookup-local; only a caller-supplied expansions map retains root lists, one per config, for the caller's lifetime. Synchronous expansion delegates capture cleanup to spawnNative's finally path, whose cleanup failures are suppressed; output storage and child duration have no configured ceiling here. A nonterminating child prevents the lookup from finishing, rather than being released by this graph traversal.
62
67
  */
63
68
  export function resolveOwningProjectConfig(props: {
64
69
  /** Absolute or invocation-relative spelling of the discovered config. */
@@ -73,8 +78,19 @@ export function resolveOwningProjectConfig(props: {
73
78
  */
74
79
  binary?: string;
75
80
 
81
+ /** Complete environment for compiler selection and root-file expansion. */
82
+ env?: NodeJS.ProcessEnv;
83
+
76
84
  /** Observe each directly read config, including ones with no reference edges. */
77
85
  onConfig?: (config: string) => void;
86
+
87
+ /**
88
+ * Expanded root files kept across lookups, by config path, for a caller whose
89
+ * selected builds and compiler environment already hold for the same
90
+ * lifetime: ttsx builds each owning project once per process
91
+ * (samchon/ttsc#1747).
92
+ */
93
+ expansions?: Map<string, { files: readonly string[]; takenAt: number }>;
78
94
  }): string {
79
95
  const discovered = path.resolve(props.tsconfig);
80
96
  const discoveredReferences = readReferences(discovered, props.onConfig);
@@ -108,14 +124,19 @@ export function resolveOwningProjectConfig(props: {
108
124
  * root observations.
109
125
  */
110
126
  function contains(config: string): boolean {
111
- const roots = rootFiles(config, props.binary);
127
+ const roots = props.expansions?.get(config) ?? expand(config);
112
128
  if (listed(roots)) return true;
113
129
  // Recent ctime permits one retry after a miss; it can reflect a metadata
114
- // change rather than creation and is not an atomic freshness proof.
115
- return (
116
- createdSince(props.file, roots.takenAt) &&
117
- listed(rootFiles(config, props.binary))
118
- );
130
+ // change rather than creation and is not an atomic freshness proof. A kept
131
+ // expansion taken before the file appeared is retried the same way.
132
+ return createdSince(props.file, roots.takenAt) && listed(expand(config));
133
+ }
134
+
135
+ /** Expand `config` now, and keep the expansion when the caller asked to. */
136
+ function expand(config: string): RootFiles {
137
+ const roots = rootFiles(config, props.binary, props.env);
138
+ props.expansions?.set(config, roots);
139
+ return roots;
119
140
  }
120
141
  return selectReferencedProject(discovered, discoveredReferences, {
121
142
  identity: (config) => identities.resolve(config).key,
@@ -199,14 +220,19 @@ interface RootFiles {
199
220
  * include approximation or permanent failed answer substitutes for compiler
200
221
  * behavior.
201
222
  */
202
- function rootFiles(config: string, binary: string | undefined): RootFiles {
223
+ function rootFiles(
224
+ config: string,
225
+ binary: string | undefined,
226
+ env: NodeJS.ProcessEnv | undefined,
227
+ ): RootFiles {
203
228
  const takenAt = Date.now();
204
229
  let files: string[] = [];
205
230
  try {
206
231
  const directory = path.dirname(config);
207
- const tsgo = resolveTsgo({ binary, cwd: directory });
232
+ const tsgo = resolveTsgo({ binary, cwd: directory, env });
208
233
  const result = spawnNative(tsgo.binary, ["-p", config, "--showConfig"], {
209
234
  cwd: directory,
235
+ env,
210
236
  encoding: "utf8",
211
237
  });
212
238
  if (result.status === 0) {
@@ -84,7 +84,7 @@ export function readEffectiveCompilerOptions(
84
84
  } catch {
85
85
  return null;
86
86
  }
87
- const tsgo = resolveTsgo({ cwd: project.root, binary });
87
+ const tsgo = resolveTsgo({ cwd: project.root, binary, env });
88
88
  const result = spawnNative(
89
89
  tsgo.binary,
90
90
  ["-p", project.path, ...passthrough, "-p", project.path, "--showConfig"],
@@ -29,7 +29,10 @@ import { SidecarEnvironment } from "./sharedHost/SidecarEnvironment";
29
29
  * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Resolution owns no persistent map, descriptor or task and returns only the selected path spelling.
30
30
  */
31
31
  export function resolveBinary(
32
- opts: { env?: NodeJS.ProcessEnv } = {},
32
+ opts: {
33
+ /** Complete selection environment; omission reads the current process. */
34
+ env?: NodeJS.ProcessEnv;
35
+ } = {},
33
36
  ): string | null {
34
37
  const env = opts.env ?? process.env;
35
38
  const explicit = SidecarEnvironment.read(env, "TTSC_BINARY");
@@ -41,6 +41,13 @@ export function resolveTsgo(
41
41
 
42
42
  /** Directory from which to discover `typescript`. */
43
43
  cwd?: string;
44
+
45
+ /**
46
+ * Complete environment for executable selection; omission reads the current
47
+ * process environment. Public compiler owners merge caller overrides before
48
+ * invoking this resolver, retaining their original overlay for channel
49
+ * ownership.
50
+ */
44
51
  env?: NodeJS.ProcessEnv;
45
52
 
46
53
  /**
@@ -4,10 +4,12 @@ import path from "node:path";
4
4
  import { createCanonicalTempDirectory } from "../../internal/createCanonicalTempDirectory";
5
5
  import type { TtscSingleFileEmitOptions } from "../../structures/internal/TtscSingleFileEmitOptions";
6
6
  import { EmitOwnershipIndex } from "./EmitOwnershipIndex";
7
+ import { WatchInputPublication } from "./build/WatchInputPublication";
7
8
  import { runBuild } from "./build/runBuild";
8
9
  import { CompilerProjectSelection } from "./project/CompilerProjectSelection";
9
10
  import { readProjectConfig } from "./project/readProjectConfig";
10
11
  import { readEffectiveCompilerOptions } from "./readEffectiveCompilerOptions";
12
+ import { SidecarEnvironment } from "./sharedHost/SidecarEnvironment";
11
13
 
12
14
  /**
13
15
  * Emit one source file by building its project into a temporary directory.
@@ -20,10 +22,12 @@ import { readEffectiveCompilerOptions } from "./readEffectiveCompilerOptions";
20
22
  * failure propagates on success and is aggregated with an earlier operation
21
23
  * failure rather than replacing it.
22
24
  *
23
- * A launcher-supplied selection shares its project and original compiler argument
24
- * cwd with public output planning. Response freshness is checked before native
25
- * work and publication; these sequential checks do not pin bytes atomically.
26
- * Callers without that record retain explicit config/nearest-file discovery.
25
+ * A launcher-supplied selection shares its project and original compiler
26
+ * argument cwd with public output planning. Response freshness is checked
27
+ * before native work and publication; these sequential checks do not pin bytes
28
+ * atomically. Callers without that record retain explicit config/nearest-file
29
+ * discovery. Watch-input handlers must complete synchronously; use
30
+ * {@link runSingleFileEmit.async} for promise-returning handlers.
27
31
  *
28
32
  * @returns The transformed JavaScript source text.
29
33
  * @throws When the build exits non-zero, proves no JavaScript for the requested
@@ -43,107 +47,147 @@ export function runSingleFileEmit(
43
47
  options: TtscSingleFileEmitOptions,
44
48
  selection?: ReturnType<typeof CompilerProjectSelection.read>,
45
49
  ): string {
46
- const cwd = path.resolve(options.cwd ?? process.cwd());
47
- const sourceFile = realpathIfExists(
48
- path.isAbsolute(options.file)
49
- ? options.file
50
- : path.resolve(cwd, options.file),
51
- );
52
- const project =
53
- selection?.project ??
54
- readProjectConfig({
55
- cwd,
56
- file: options.file,
57
- projectRoot: options.projectRoot,
58
- tsconfig: options.tsconfig,
59
- });
60
- const tsconfig = project.path;
61
- const compilerArgsCwd = selection?.compilerArgsCwd ?? project.root;
62
- if (selection !== undefined) CompilerProjectSelection.assertCurrent(selection);
63
- // The root tsgo lays the outputs against: a `--rootDir` forwarded on the
64
- // command line, which reaches it after the config; otherwise the declared
65
- // one, which `readProjectConfig` already absolutized; otherwise the project's
66
- // own directory, which `pinInferredRootDir` hands it below.
67
- const effective = readEffectiveCompilerOptions(
68
- project,
69
- options.passthrough,
70
- options.binary,
71
- options.env,
72
- compilerArgsCwd,
73
- )?.("rootDir");
74
- const rootDir =
75
- typeof effective === "string"
76
- ? path.resolve(compilerArgsCwd, effective)
77
- : project.root;
78
- if (selection !== undefined) CompilerProjectSelection.assertCurrent(selection);
79
- const outDir = createCanonicalTempDirectory("ttsc-single-file-");
80
- let failed = false;
81
- let failure: unknown;
82
- try {
83
- const result = runBuild({
84
- ...options,
85
- cwd,
50
+ return WatchInputPublication.run(runSingleFileEmit.steps(options, selection));
51
+ }
52
+
53
+ /**
54
+ * Keep synchronous and awaited single-file emission on one temporary lifetime.
55
+ *
56
+ * @evidence contracts/common.md#principled-implementation Both entries share project/root selection, producing build, response freshness, provenance lookup and temporary-output cleanup; awaited completion joins every nested publication before compilation and output selection.
57
+ * @evidence contracts/common.md#clear-and-simple-design One generator owns project emission, optional public output and cleanup for both completion modes; the companion adds a runner without another output-selection implementation.
58
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Pending registration cannot authorize a build, and rejected publication reaches the same temporary cleanup path. Completion mode preserves response checks and provenance instead of reconstructing source ownership from filenames.
59
+ * @evidence contracts/common.md#meaningful-documentation Each entry states completion mode and the shared temporary lifetime; sequence prose requires rejection delivery into its finally path, while the primary entry documents output selection and cleanup failures.
60
+ * @evidence contracts/portability.md#os-neutral-implementation The shared sequence retains existing native source/output identity, cwd bases and private-directory handling across suspension without another native boundary.
61
+ * @evidence contracts/performance.md#efficient-algorithms One project selection and full-project build serve one provenance lookup; each handoff is awaited once with no additional source or output traversal.
62
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each sequence owns fresh compiler effects rather than an output cache or shared generation; higher-level owners decide equivalent build reuse.
63
+ * @evidence contracts/performance.md#bound-retention-and-release-resources One accepted private output directory spans nested handoff and build work, and the existing finally path removes it on success or failure and preserves cleanup error aggregation.
64
+ */
65
+ export namespace runSingleFileEmit {
66
+ /**
67
+ * Await watch-input registration before emitting and selecting one source's
68
+ * JavaScript. Rejected publication follows the same temporary cleanup path.
69
+ */
70
+ export function async(
71
+ options: TtscSingleFileEmitOptions,
72
+ selection?: ReturnType<typeof CompilerProjectSelection.read>,
73
+ ): Promise<string> {
74
+ return WatchInputPublication.runAsync(steps(options, selection));
75
+ }
76
+
77
+ /**
78
+ * Emit through one selected project while suspending at watch handoffs.
79
+ * Internal completion runners must deliver rejection into this sequence so
80
+ * its finally block retains temporary-output cleanup ownership.
81
+ */
82
+ export function* steps(
83
+ options: TtscSingleFileEmitOptions,
84
+ selection?: ReturnType<typeof CompilerProjectSelection.read>,
85
+ ): WatchInputPublication.Sequence<string> {
86
+ const cwd = path.resolve(options.cwd ?? process.cwd());
87
+ const sourceFile = realpathIfExists(
88
+ path.isAbsolute(options.file)
89
+ ? options.file
90
+ : path.resolve(cwd, options.file),
91
+ );
92
+ const project =
93
+ selection?.project ??
94
+ readProjectConfig({
95
+ cwd,
96
+ file: options.file,
97
+ projectRoot: options.projectRoot,
98
+ tsconfig: options.tsconfig,
99
+ });
100
+ const tsconfig = project.path;
101
+ const compilerArgsCwd = selection?.compilerArgsCwd ?? project.root;
102
+ if (selection !== undefined)
103
+ CompilerProjectSelection.assertCurrent(selection);
104
+ // The root tsgo lays the outputs against: a `--rootDir` forwarded on the
105
+ // command line, which reaches it after the config; otherwise the declared
106
+ // one, which `readProjectConfig` already absolutized; otherwise the project's
107
+ // own directory, which `pinInferredRootDir` hands it below.
108
+ const effective = readEffectiveCompilerOptions(
109
+ project,
110
+ options.passthrough,
111
+ options.binary,
112
+ SidecarEnvironment.merge(process.env, options.env),
86
113
  compilerArgsCwd,
87
- emit: true,
88
- forceEmitProvenance: true,
89
- isolateOutputsTo: outDir,
90
- outDir,
91
- // The private temp directory above is an `outDir` this lane injected, not
92
- // one the project declared, and tsgo answers an inferred common source
93
- // directory with TS5011 as soon as any `outDir` is in play. Pinning the
94
- // root tsgo would infer keeps `ttsc <file.ts>` working on a project that
95
- // declares no output at all, while the producing invocation reports its
96
- // actual source/output ownership separately.
97
- pinInferredRootDir: true,
98
- resolvedProject: project,
99
- tsconfig,
100
- });
101
- if (result.status !== 0) {
102
- throw new Error(
103
- "ttsc single-file emit exited " +
104
- result.status +
105
- "\n" +
106
- (result.stderr || result.stdout),
107
- );
108
- }
109
- const emitted = new EmitOwnershipIndex({
110
- emitDir: outDir,
111
- rootDir,
112
- emittedSources: result.emittedSources,
113
- emittedSourceProofFailures: result.emittedSourceProofFailures,
114
- outputs: EmitOwnershipIndex.listOutputs(outDir),
115
- }).find(sourceFile);
116
- if (emitted === null) {
117
- throw new Error(
118
- `ttsc single-file emit: ${tsconfig} emitted no JavaScript owned by ${sourceFile}; check that the project includes the source and its options permit JavaScript emission`,
119
- );
120
- }
121
- const transformed = fs.readFileSync(emitted, "utf8");
122
- if (selection !== undefined) CompilerProjectSelection.assertCurrent(selection);
123
- if (options.out) {
124
- const target = path.isAbsolute(options.out)
125
- ? options.out
126
- : path.resolve(cwd, options.out);
127
- fs.mkdirSync(path.dirname(target), { recursive: true });
128
- fs.writeFileSync(target, transformed, "utf8");
129
- }
130
- return transformed;
131
- } catch (error) {
132
- failed = true;
133
- failure = error;
134
- throw error;
135
- } finally {
114
+ )?.("rootDir");
115
+ const rootDir =
116
+ typeof effective === "string"
117
+ ? path.resolve(compilerArgsCwd, effective)
118
+ : project.root;
119
+ if (selection !== undefined)
120
+ CompilerProjectSelection.assertCurrent(selection);
121
+ const outDir = createCanonicalTempDirectory("ttsc-single-file-");
122
+ let failed = false;
123
+ let failure: unknown;
136
124
  try {
137
- fs.rmSync(outDir, { recursive: true, force: true });
138
- } catch (cleanupError) {
139
- if (failed) {
140
- throw new AggregateError(
141
- [failure, cleanupError],
142
- "ttsc: single-file emission and temporary output cleanup failed",
143
- { cause: failure },
125
+ const result = yield* runBuild.steps({
126
+ ...options,
127
+ cwd,
128
+ compilerArgsCwd,
129
+ emit: true,
130
+ forceEmitProvenance: true,
131
+ isolateOutputsTo: outDir,
132
+ outDir,
133
+ // The private temp directory above is an `outDir` this lane injected, not
134
+ // one the project declared, and tsgo answers an inferred common source
135
+ // directory with TS5011 as soon as any `outDir` is in play. Pinning the
136
+ // root tsgo would infer keeps `ttsc <file.ts>` working on a project that
137
+ // declares no output at all, while the producing invocation reports its
138
+ // actual source/output ownership separately.
139
+ pinInferredRootDir: true,
140
+ resolvedProject: project,
141
+ tsconfig,
142
+ });
143
+ if (result.status !== 0) {
144
+ throw new Error(
145
+ "ttsc single-file emit exited " +
146
+ result.status +
147
+ "\n" +
148
+ (result.stderr || result.stdout),
144
149
  );
145
150
  }
146
- throw cleanupError;
151
+ const emitted = new EmitOwnershipIndex({
152
+ emitDir: outDir,
153
+ rootDir,
154
+ emittedSources: result.emittedSources,
155
+ emittedSourceProofFailures: result.emittedSourceProofFailures,
156
+ outputs: EmitOwnershipIndex.listOutputs(outDir),
157
+ }).find(sourceFile);
158
+ if (emitted === null) {
159
+ throw new Error(
160
+ `ttsc single-file emit: ${tsconfig} emitted no JavaScript owned by ${sourceFile}; check that the project includes the source and its options permit JavaScript emission`,
161
+ );
162
+ }
163
+ const transformed = fs.readFileSync(emitted, "utf8");
164
+ if (selection !== undefined)
165
+ CompilerProjectSelection.assertCurrent(selection);
166
+ if (options.out) {
167
+ const target = path.isAbsolute(options.out)
168
+ ? options.out
169
+ : path.resolve(cwd, options.out);
170
+ fs.mkdirSync(path.dirname(target), { recursive: true });
171
+ fs.writeFileSync(target, transformed, "utf8");
172
+ }
173
+ return transformed;
174
+ } catch (error) {
175
+ failed = true;
176
+ failure = error;
177
+ throw error;
178
+ } finally {
179
+ try {
180
+ fs.rmSync(outDir, { recursive: true, force: true });
181
+ } catch (cleanupError) {
182
+ if (failed) {
183
+ throw new AggregateError(
184
+ [failure, cleanupError],
185
+ "ttsc: single-file emission and temporary output cleanup failed",
186
+ { cause: failure },
187
+ );
188
+ }
189
+ throw cleanupError;
190
+ }
147
191
  }
148
192
  }
149
193
  }
@@ -62,7 +62,10 @@ export function startResidentTransform(
62
62
  tsconfig: context.tsconfig,
63
63
  });
64
64
  const loaded = loadProjectPlugins({
65
- binary: resolveBinary(context) ?? "",
65
+ binary:
66
+ resolveBinary({
67
+ env: SidecarEnvironment.merge(process.env, context.env),
68
+ }) ?? "",
66
69
  cacheDir:
67
70
  context.cacheDir ??
68
71
  SidecarEnvironment.read(context.env, "TTSC_CACHE_DIR"),
@@ -85,7 +88,11 @@ export function startResidentTransform(
85
88
  assertSharedHostCompatibility(transformers, "source-to-source");
86
89
 
87
90
  const host = selectSharedHostPlugin(transformers);
88
- const tsgoBinary = resolveTsgo({ ...context, cwd: project.root }).binary;
91
+ const tsgoBinary = resolveTsgo({
92
+ ...context,
93
+ cwd: project.root,
94
+ env: SidecarEnvironment.merge(process.env, context.env),
95
+ }).binary;
89
96
  const resident = new ResidentTransformProcess({
90
97
  args: [
91
98
  "serve",
@@ -109,7 +109,10 @@ export function transformProjectInMemory(options: ITtscCompilerContext): {
109
109
  } {
110
110
  const cwd = path.resolve(options.cwd ?? process.cwd());
111
111
  const loaded = loadProjectPlugins({
112
- binary: resolveBinary(options) ?? "",
112
+ binary:
113
+ resolveBinary({
114
+ env: SidecarEnvironment.merge(process.env, options.env),
115
+ }) ?? "",
113
116
  cacheDir:
114
117
  options.cacheDir ??
115
118
  SidecarEnvironment.read(options.env, "TTSC_CACHE_DIR"),
@@ -188,11 +191,13 @@ function transformProjectWithNativeHost(
188
191
  baseline?.hostInputs ?? [],
189
192
  projectHostInputs,
190
193
  );
194
+ const env = inheritedSidecarEnv(options.env);
191
195
  const binary = buildNativeCompiler({
192
196
  cacheBaseDir: project.root,
193
197
  cacheDir:
194
198
  options.cacheDir ??
195
199
  SidecarEnvironment.read(options.env, "TTSC_CACHE_DIR"),
200
+ env,
196
201
  packageRoot: packageRootDir(),
197
202
  });
198
203
  const res = spawnNative(
@@ -200,7 +205,7 @@ function transformProjectWithNativeHost(
200
205
  ["api-transform", "--cwd", project.root, "--tsconfig", project.path],
201
206
  {
202
207
  cwd: project.root,
203
- env: inheritedSidecarEnv(options.env),
208
+ env,
204
209
  },
205
210
  );
206
211
  if (res.error) {
@@ -295,7 +300,11 @@ function transformProjectWithPlugins(
295
300
  const tsgoBinary =
296
301
  loaded.nativePlugins.length === 0
297
302
  ? ""
298
- : resolveTsgo({ ...options, cwd: project.root }).binary;
303
+ : resolveTsgo({
304
+ ...options,
305
+ cwd: project.root,
306
+ env: SidecarEnvironment.merge(process.env, options.env),
307
+ }).binary;
299
308
  const checked = runNativeChecks(
300
309
  options,
301
310
  project,
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { Worker } from "node:worker_threads";
4
4
 
5
5
  import { CompilerContextSnapshot } from "../../internal/CompilerContextSnapshot";
6
+ import { workerModuleUrl } from "../../internal/workerModuleUrl";
6
7
  import type { ITtscCompilerContext } from "../../structures/ITtscCompilerContext";
7
8
  import type { TransformProjectWorkerReply } from "./TransformProjectWorkerReply";
8
9
  import type { TransformProjectWorkerRequest } from "./TransformProjectWorkerRequest";
@@ -50,7 +51,7 @@ import type { transformProjectInMemory } from "./transformProjectInMemory";
50
51
  * @evidence contracts/common.md#clear-and-simple-design The public operation owns checkout, exclusive event listeners and return to the idle pool; two private helpers isolate idle-worker retirement from active request settlement.
51
52
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Blocking work runs in a supported worker boundary rather than rewriting the caller's environment around synchronous work; failures preserve real exceptions and do not substitute measured or expected results.
52
53
  * @evidence contracts/common.md#meaningful-documentation Native paragraphs explain why all transform phases move off-thread, environment isolation, warm reuse and retained-population limits following the documentation skill.
53
- * @evidence contracts/portability.md#os-neutral-implementation Worker entry is a native path and the environment snapshot uses native name identity; Windows worker copies are case-sensitive, so canonical merging prevents aliases from defeating caller overrides inside the worker.
54
+ * @evidence contracts/portability.md#os-neutral-implementation A supported module URL requires the native CommonJS source or emitted worker entry while Node retains inherited execution options and preload hooks. The environment snapshot uses native name identity; Windows worker copies are case-sensitive, so canonical merging prevents aliases from defeating caller overrides inside the worker.
54
55
  * @evidence contracts/performance.md#efficient-algorithms Pool checkout removes one entry and listeners; environment merge and plugin JSON serialization visit caller data before structured cloning, whose cost includes context, serialized payload and environment bytes. Caller JSON conversion can add arbitrary work. Idle-worker retirement searches/compacts at most the pool budget; worker startup and native transform costs remain delegated, and output cloning is transport work rather than a second transform.
55
56
  * @evidence contracts/performance.md#reuse-equivalent-work An exclusively checked-out warm worker reuses the loader's input-validated caches; each request adopts its complete current environment and still executes the transform, so thread reuse is not an unsupported response cache.
56
57
  * @evidence contracts/performance.md#bound-retention-and-release-resources At most the module-initialization CPU budget remains in the idle pool with event-loop references removed. Excess/failed workers receive unawaited termination requests; retiring native threads are not covered by the reusable-entry count. Terminal settlement removes active listeners, while pooled module caches retain their owning data policy. Active requests and live transforms have no concurrency cap or implicit deadline.
@@ -65,7 +66,14 @@ export function transformProjectInWorker(
65
66
  };
66
67
  const worker =
67
68
  takeIdleWorker() ??
68
- new Worker(path.join(__dirname, "transformProjectWorker.js"));
69
+ new Worker(
70
+ workerModuleUrl(
71
+ path.join(
72
+ __dirname,
73
+ `transformProjectWorker${path.extname(__filename)}`,
74
+ ),
75
+ ),
76
+ );
69
77
  worker.ref();
70
78
  return new Promise((resolve, reject) => {
71
79
  let settled = false;
@@ -29,8 +29,8 @@ export interface ParseOptions {
29
29
  readonly errorPrefix: string;
30
30
 
31
31
  /**
32
- * Observe a validated launcher assignment at its compiler-frame position.
33
- * The canonical schema name and parsed value retain alias/value semantics;
32
+ * Observe a validated launcher assignment at its compiler-frame position. The
33
+ * canonical schema name and parsed value retain alias/value semantics;
34
34
  * passthroughIndex counts compiler tokens already forwarded at this point.
35
35
  * Exceptions propagate from the caller's observer.
36
36
  *