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,106 @@
1
+ import type { PluginContentIdentities } from "../../../plugin/internal/source/PluginContentIdentities";
2
+ import type { ITtscProjectInputSnapshot } from "../../../structures/internal/ITtscProjectInputSnapshot";
3
+ import type { WatchInputChange } from "./WatchInputChange";
4
+ import type { WatchTopology } from "./WatchTopology";
5
+
6
+ /**
7
+ * Cloned requests and ordered notifications of one watch topology worker.
8
+ *
9
+ * @evidence contracts/common.md#principled-implementation Discriminated messages preserve operation arguments and separate callbacks from request completion; bigint reference maps retain the record store's separability evidence through structured cloning.
10
+ * @evidence contracts/common.md#clear-and-simple-design One internal protocol serves the facade and its worker without transferring callback functions.
11
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Messages carry actual topology operations and observations rather than predicted decisions.
12
+ * @evidence contracts/common.md#meaningful-documentation Members identify correlation, callback ordering and the cloneable option boundary.
13
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The types describe structured messages; execution owns native paths and workers.
14
+ * @evidenceExclude contracts/performance.md#efficient-algorithms These types execute no algorithm.
15
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work The protocol grants no result reuse.
16
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The types acquire no resources; the facade owns in-flight messages.
17
+ */
18
+ export namespace WatchTopologyWorkerProtocol {
19
+ /**
20
+ * Only the core's selected data options cross the worker boundary.
21
+ *
22
+ * @evidence contracts/common.md#principled-implementation Deriving the data type from the core constructor keeps option meaning with its owner.
23
+ * @evidence contracts/common.md#clear-and-simple-design One alias serves both ends of the worker's startup payload.
24
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The facade explicitly selects these fields without transferring invocation callbacks.
25
+ * @evidence contracts/common.md#meaningful-documentation Prose identifies the cloneable startup boundary.
26
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation This alias executes no native operation.
27
+ * @evidenceExclude contracts/performance.md#efficient-algorithms The alias chooses no algorithm.
28
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work The type grants no reuse.
29
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The type acquires no resource; the facade owns startup cloning.
30
+ */
31
+ export type Options = ConstructorParameters<typeof WatchTopology>[0];
32
+
33
+ /**
34
+ * Callback implementations remain on the launcher thread.
35
+ *
36
+ * @evidence contracts/common.md#principled-implementation Core callbacks retain their meanings; actual worker error or unexpected exit invokes separate terminal notification because authority was lost, requiring the launcher to retire its session.
37
+ * @evidence contracts/common.md#clear-and-simple-design One structural intersection adds a dedicated terminal callback beside recoverable subscription events.
38
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Callbacks report actual observations and authority loss without continuing to claim coverage after thread death.
39
+ * @evidence contracts/common.md#meaningful-documentation Prose identifies launcher-thread callbacks and the receiving owner's required retirement after terminal loss.
40
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation These signatures perform no native operation.
41
+ * @evidenceExclude contracts/performance.md#efficient-algorithms Callback signatures choose no algorithm.
42
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Callback declarations grant no event reuse; terminal failure belongs to one lifecycle.
43
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Signatures acquire no resources; callback implementations own session retention and cleanup.
44
+ */
45
+ export type Callbacks = ConstructorParameters<typeof WatchTopology>[1] & {
46
+ /** Terminal thread loss requires the launcher to retire its watch session. */
47
+ onFailure(error: unknown): void;
48
+ };
49
+
50
+ /**
51
+ * An operation carries only the arguments its core member consumes.
52
+ *
53
+ * @evidence contracts/common.md#principled-implementation Discriminated method names pair each actual operation with its required payload.
54
+ * @evidence contracts/common.md#clear-and-simple-design One union describes the core's five transport operations.
55
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Publications retain complete input snapshots and source proof stores without filtering.
56
+ * @evidence contracts/common.md#meaningful-documentation Prose identifies argument ownership and discriminated payloads.
57
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The union executes no native operation.
58
+ * @evidenceExclude contracts/performance.md#efficient-algorithms The type chooses no algorithm.
59
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work The payload grants no result reuse.
60
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Transport and core owners retain cloned payloads rather than this type.
61
+ */
62
+ export type Operation =
63
+ | { method: "refresh"; notify: boolean }
64
+ | {
65
+ method: "setExtraInputs";
66
+ inputs: readonly string[];
67
+ identities?: PluginContentIdentities.Store;
68
+ }
69
+ | { method: "setProjectInputs"; inputs: ITtscProjectInputSnapshot }
70
+ | { method: "whenIdle" }
71
+ | { method: "close" };
72
+
73
+ /**
74
+ * Each operation receives its own correlated completion or failure.
75
+ *
76
+ * @evidence contracts/common.md#principled-implementation A per-facade monotonic id distinguishes concurrent pending operations.
77
+ * @evidence contracts/common.md#clear-and-simple-design Correlation is one field intersected with the operation union.
78
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts An id identifies the actual request rather than an assumed ordered result.
79
+ * @evidence contracts/common.md#meaningful-documentation Prose states correlation and terminal outcomes.
80
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The representation performs no native operation.
81
+ * @evidenceExclude contracts/performance.md#efficient-algorithms This type runs no algorithm.
82
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Request identity grants no reuse.
83
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The facade owns pending correlation entries.
84
+ */
85
+ export type Request = { id: number } & Operation;
86
+
87
+ /**
88
+ * Callbacks and answers use one FIFO port, preserving their causal order.
89
+ *
90
+ * @evidence contracts/common.md#principled-implementation Event tags distinguish callbacks from correlated success or failure, and one MessagePort orders callback transmission before operation completion.
91
+ * @evidence contracts/common.md#clear-and-simple-design One union carries observed changes, coverage reports, recoverable errors and request outcomes.
92
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Failures retain serialized causes and actual observed input paths; no expected response replaces them.
93
+ * @evidence contracts/common.md#meaningful-documentation Prose states causal FIFO ordering.
94
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The union describes portable message data without a native operation.
95
+ * @evidenceExclude contracts/performance.md#efficient-algorithms The type chooses no algorithm.
96
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each message belongs to its actual event or operation.
97
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Message and correlation retention belongs to worker and facade implementations.
98
+ */
99
+ export type Reply =
100
+ | { id: number; ok: true }
101
+ | { id: number; ok: false; error: unknown }
102
+ | { event: "input"; change: WatchInputChange }
103
+ | { event: "topology" }
104
+ | { event: "error"; location: string; error: unknown }
105
+ | { event: "roots" | "unavailable"; roots: readonly string[] };
106
+ }
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
 
3
3
  import { type ProjectInputPathIdentity } from "../../../internal/pathIdentity/ProjectInputPathIdentity";
4
+ import { type ProjectInputPathIdentityContext } from "../../../internal/pathIdentity/ProjectInputPathIdentityContext";
4
5
  import { createProjectInputPathIdentityContext } from "../../../internal/pathIdentity/createProjectInputPathIdentityContext";
5
6
  import { literalGlobRoot } from "./literalGlobRoot";
6
7
 
@@ -12,6 +13,8 @@ import { literalGlobRoot } from "./literalGlobRoot";
12
13
  * lane only after the surrounding change detector admits the event; unchanged
13
14
  * bytes remain quiet before this classifier is observed.
14
15
  *
16
+ * @param identities The decision's identity transaction; absence creates a
17
+ * native one for this classification alone.
15
18
  * @evidence contracts/common.md#principled-implementation Exact selection files and immediate resolution-directory entries choose execution reload; a strictly nested declared glob can explain its own ancestor's membership delta without exempting unrelated selection changes.
16
19
  * @evidence contracts/common.md#clear-and-simple-design Exact-file membership, immediate-entry membership and causal explanations have distinct helpers under one classification operation.
17
20
  * @evidence contracts/common.md#prohibited-implementation-shortcuts A data exemption must explain the same directory's observed delta, not cancel every reload because some unrelated data event occurred.
@@ -22,15 +25,17 @@ import { literalGlobRoot } from "./literalGlobRoot";
22
25
  *
23
26
  * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Identity caches and predicate collections are local to one decision, without a resident historical cache or native handle.
24
27
  */
25
- export function projectInputReloadEventShouldNotify(input: {
26
- causedBy?: readonly string[];
27
- changed?: string;
28
- changedInputs: readonly string[];
29
- reloadDirectories?: readonly string[];
30
- reloadFiles: readonly string[];
31
- globs?: readonly string[];
32
- }): boolean {
33
- const identities = createProjectInputPathIdentityContext();
28
+ export function projectInputReloadEventShouldNotify(
29
+ input: {
30
+ causedBy?: readonly string[];
31
+ changed?: string;
32
+ changedInputs: readonly string[];
33
+ reloadDirectories?: readonly string[];
34
+ reloadFiles: readonly string[];
35
+ globs?: readonly string[];
36
+ },
37
+ identities: ProjectInputPathIdentityContext = createProjectInputPathIdentityContext(),
38
+ ): boolean {
34
39
  const reloadFiles = new Set(
35
40
  input.reloadFiles.map((location) => identities.resolve(location).key),
36
41
  );
@@ -0,0 +1,75 @@
1
+ import type { MessagePort } from "node:worker_threads";
2
+
3
+ import { serializeCompilerError } from "../../../internal/serializeCompilerError";
4
+ import type { WatchTopology } from "./WatchTopology";
5
+ import type { WatchTopologyWorkerProtocol } from "./WatchTopologyWorkerProtocol";
6
+
7
+ /**
8
+ * Serve ordered topology operations until close joins all outstanding answers.
9
+ *
10
+ * Mutation runs synchronously when its message arrives. An idle barrier yields
11
+ * so a later close can stop continuous native events; closing then waits for
12
+ * those earlier barriers to answer before closing the callback channel.
13
+ *
14
+ * @evidence contracts/common.md#principled-implementation FIFO messages invoke actual core operations in order, while close releases subscriptions before outstanding idle barriers finish and the reply port retires.
15
+ * @evidence contracts/common.md#clear-and-simple-design One dispatcher owns operation correlation and answer lifetime; core callbacks and filesystem decisions remain separate.
16
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The supplied core is the production owner, and serialization retains actual operation failures without fabricating completion.
17
+ * @evidence contracts/common.md#meaningful-documentation Native prose explains ordered mutation, yielding idle barriers and close ordering.
18
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation MessagePort supplies portable FIFO transport; the core owns native work.
19
+ * @evidence contracts/performance.md#efficient-algorithms Dispatch and correlation use constant structural work per message plus cloned reply bytes; delegated topology work retains its original costs. Shutdown visits outstanding promises once.
20
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each message invokes its own operation rather than reusing an earlier result.
21
+ * @evidence contracts/performance.md#bound-retention-and-release-resources In-flight answers occupy an uncapped set and leave it on completion. Close releases the port after all accepted answers settle; core close failure still reaches the caller, which owns worker termination.
22
+ */
23
+ export function serveWatchTopologyWorker(
24
+ topology: WatchTopology,
25
+ port: MessagePort,
26
+ ): void {
27
+ const active = new Set<Promise<void>>();
28
+ let closing = false;
29
+ port.on("message", (request: WatchTopologyWorkerProtocol.Request) => {
30
+ const answering = (
31
+ closing
32
+ ? Promise.reject(new Error("ttsc: watch topology is closing"))
33
+ : execute(topology, request)
34
+ ).then(
35
+ () => port.postMessage({ id: request.id, ok: true }),
36
+ (error: unknown) =>
37
+ port.postMessage({
38
+ id: request.id,
39
+ ok: false,
40
+ error: serializeCompilerError(error),
41
+ }),
42
+ );
43
+ active.add(answering);
44
+ if (request.method === "close") closing = true;
45
+ void answering.then(() => {
46
+ active.delete(answering);
47
+ if (request.method === "close")
48
+ void Promise.allSettled([...active]).then(() => port.close());
49
+ });
50
+ });
51
+ }
52
+
53
+ /** Dispatch one actual core operation; only the idle barrier yields. */
54
+ async function execute(
55
+ topology: WatchTopology,
56
+ request: WatchTopologyWorkerProtocol.Request,
57
+ ): Promise<void> {
58
+ switch (request.method) {
59
+ case "refresh":
60
+ topology.refresh(request.notify);
61
+ break;
62
+ case "setExtraInputs":
63
+ topology.setExtraInputs(request.inputs, request.identities);
64
+ break;
65
+ case "setProjectInputs":
66
+ topology.setProjectInputs(request.inputs);
67
+ break;
68
+ case "whenIdle":
69
+ await topology.whenIdle();
70
+ break;
71
+ case "close":
72
+ topology.close();
73
+ break;
74
+ }
75
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Run the synchronous topology on its owned thread. Keeping native probes,
3
+ * listings and digests together preserves transaction and baseline semantics
4
+ * while the launcher's resident process timers remain free to run (#1758).
5
+ */
6
+ import { parentPort, workerData } from "node:worker_threads";
7
+
8
+ import { serializeCompilerError } from "../../../internal/serializeCompilerError";
9
+ import { WatchTopology } from "./WatchTopology";
10
+ import type { WatchTopologyWorkerProtocol } from "./WatchTopologyWorkerProtocol";
11
+ import { serveWatchTopologyWorker } from "./serveWatchTopologyWorker";
12
+
13
+ const port = parentPort!;
14
+ const send = (reply: WatchTopologyWorkerProtocol.Reply): void =>
15
+ port.postMessage(reply);
16
+ const topology = new WatchTopology(
17
+ workerData as WatchTopologyWorkerProtocol.Options,
18
+ {
19
+ onError: (location, error) =>
20
+ send({ event: "error", location, error: serializeCompilerError(error) }),
21
+ onInputChange: (change) => send({ event: "input", change }),
22
+ onProjectInputWatchRoots: (roots) => send({ event: "roots", roots }),
23
+ onProjectInputWatchUnavailable: (roots) =>
24
+ send({ event: "unavailable", roots }),
25
+ onTopologyChange: () => send({ event: "topology" }),
26
+ },
27
+ );
28
+
29
+ serveWatchTopologyWorker(topology, port);
@@ -8,6 +8,7 @@ import { Worker } from "node:worker_threads";
8
8
  import { E2ETrace } from "../internal/E2ETrace";
9
9
  import { OwnedNativeProcess } from "../internal/OwnedNativeProcess";
10
10
  import { serializeCompilerError } from "../internal/serializeCompilerError";
11
+ import { workerModuleUrl } from "../internal/workerModuleUrl";
11
12
  import type { ITtscCapabilityPlugin } from "./ITtscCapabilityPlugin";
12
13
  import { receiveCapabilityFailure } from "./internal/receiveCapabilityFailure";
13
14
 
@@ -22,7 +23,7 @@ import { receiveCapabilityFailure } from "./internal/receiveCapabilityFailure";
22
23
  * @evidence contracts/common.md#clear-and-simple-design This owner controls admission, worker RPC, opaque proof handles and shutdown; the worker owns synchronous discovery and the native owner controls process trees.
23
24
  * @evidence contracts/common.md#prohibited-implementation-shortcuts No consumer inventory replaces resolver authority and no warmup, altered timeout or uncontained fallback hides a cold producer.
24
25
  * @evidence contracts/common.md#meaningful-documentation Native prose explains proof lifetime, cancellation cleanup and the independent native process owner.
25
- * @evidence contracts/portability.md#os-neutral-implementation The compiled worker uses the SDK's CommonJS file layout, cloned environments and platform helper resolution rather than consumer-specific paths.
26
+ * @evidence contracts/portability.md#os-neutral-implementation The worker requires the SDK's CommonJS source or emitted entry through a supported module URL while Node retains inherited execution options and preload hooks; cloned environments and platform helper resolution remain native inputs.
26
27
  * @evidence contracts/performance.md#efficient-algorithms One worker serves serialized requests and retains original proof closures; command output serialization scales with returned bytes and request admission grows with submitted demand.
27
28
  * @evidence contracts/performance.md#reuse-equivalent-work Worker reuse retains validated SDK caches but every resolve and currentness request still calls its original authority; proof handles never transfer freshness between generations.
28
29
  * @evidence contracts/performance.md#bound-retention-and-release-resources Explicit release drops proof handles. Close aborts admitted work, joins command supervisors and waits for worker exit; private command exchange storage is removed after its request settles.
@@ -426,7 +427,13 @@ export class CapabilityPluginResolver {
426
427
  /** Keep idle worker failures observed and prevent reuse of a retired thread. */
427
428
  private createWorker(): Worker {
428
429
  const worker = new Worker(
429
- path.join(__dirname, "internal", "capabilityPluginWorker.js"),
430
+ workerModuleUrl(
431
+ path.join(
432
+ __dirname,
433
+ "internal",
434
+ `capabilityPluginWorker${path.extname(__filename)}`,
435
+ ),
436
+ ),
430
437
  );
431
438
  const retire = () => {
432
439
  if (this.worker === worker) this.worker = undefined;
@@ -21,11 +21,11 @@ import { realpathHostInputPaths } from "./realpathHostInputPaths";
21
21
  * descriptor's own external-read declarations, supply persistence premises;
22
22
  * their presence is not detection of every omitted read or side effect. An
23
23
  * answer is kept under everything else the evaluation was given: the
24
- * descriptor, its factory context, the effective environment it ran under, the
25
- * runtimes that ran it, and this ttsc build. A hit requires matching current
26
- * stored content/realpath projections; these sequential observations are not an
27
- * atomic filesystem snapshot and do not distinguish every cause of an
28
- * unavailable null value.
24
+ * descriptor, its factory context, the working directory, the effective
25
+ * environment, the runtimes that ran it, and this ttsc build. A hit requires
26
+ * matching current stored content/realpath projections; these sequential
27
+ * observations are not an atomic filesystem snapshot and do not distinguish
28
+ * every cause of an unavailable null value.
29
29
  *
30
30
  * The files a descriptor reads outside its module graph are its own to declare
31
31
  * (`hostInputHashes`), so only the answer of a descriptor supplying that
@@ -37,12 +37,12 @@ import { realpathHostInputPaths } from "./realpathHostInputPaths";
37
37
  * Startup preloads run before these observations and cannot authorize
38
38
  * persistence merely through a stable NODE_OPTIONS string.
39
39
  *
40
- * @evidence contracts/common.md#principled-implementation Persistent evaluation identity includes evaluator format, descriptor/context, effective environment, actual primary/secondary executable content identities and ttsc version. Acceptance checks recorded input observations and producer-declared external content, not every possible read; unobserved startup preload authority refuses persistence.
40
+ * @evidence contracts/common.md#principled-implementation Persistent evaluation identity includes evaluator format, descriptor/context, working directory, effective environment, actual primary/secondary executable content identities and ttsc version. Acceptance checks recorded input observations and producer-declared external content, not every possible read; unobserved startup preload authority refuses persistence.
41
41
  * @evidence contracts/common.md#clear-and-simple-design Locate/read/write separate key construction, proof validation and publication while the loader owns evaluation and its diagnostics.
42
42
  * @evidence contracts/common.md#prohibited-implementation-shortcuts A descriptor without its explicit external-read declaration is not persisted, and the caller excludes nonempty captured diagnostics because a cache hit cannot replay them.
43
- * @evidence contracts/common.md#meaningful-documentation Native JSDoc explains isolation cost, input-proof responsibility, environment identity and write refusal in distinct paragraphs; exported operation comments and tag spacing follow the documentation skill.
43
+ * @evidence contracts/common.md#meaningful-documentation Native JSDoc explains isolation cost, input-proof responsibility, working-directory/environment identity and write refusal in distinct paragraphs; exported operation comments and tag spacing follow the documentation skill.
44
44
  * @evidence contracts/portability.md#os-neutral-implementation Native executable identity and same-directory publication handle OS-neutral paths; sorted environment data and actual content/lexical/physical observations detect replacement without OS-name guesses.
45
- * @evidence contracts/performance.md#efficient-algorithms Key construction includes environment/name sorting, context serialization, executable hashing and delegated cache-root lookup. Reads validate full JSON/maps and observe content/realpaths; writes normalize proof keys and serialize complete entries. Native path/query work and environment/context/record/file bytes contribute cost; a valid hit avoids evaluation without establishing an unmeasured launch-time ranking.
45
+ * @evidence contracts/performance.md#efficient-algorithms Key construction includes working-directory normalization, environment/name sorting, context serialization, executable hashing and delegated cache-root lookup. Reads validate full JSON/maps and observe content/realpaths; writes normalize proof keys and serialize complete entries. Native path/query work and environment/context/record/file bytes contribute cost; a valid hit avoids evaluation without establishing an unmeasured launch-time ranking.
46
46
  * @evidence contracts/performance.md#reuse-equivalent-work Entries share only fully keyed evaluations whose declared inputs still match; incomplete or contradictory fingerprints refuse persistence rather than reviving an unproved answer.
47
47
  * @evidence contracts/performance.md#bound-retention-and-release-resources Default JSON entries and staging leftovers are eligible for single-file pruning subject to its interval/protection/error policy; explicit roots remain caller-owned. Hits attempt usage refresh, and writes attempt staging removal in finally. Cleanup or later pruning can fail; no unconditional deletion, entry-count or byte ceiling is supplied here.
48
48
  */
@@ -89,18 +89,19 @@ export namespace PluginDescriptorEvaluationCache {
89
89
  * @param props.cacheDir The plugin cache directory the caller selected.
90
90
  * @param props.request The resolved descriptor module.
91
91
  * @param props.context The factory context the descriptor is invoked with.
92
+ * @param props.cwd The working directory passed to the actual evaluator.
92
93
  * @param props.env The complete environment the evaluation runs under.
93
94
  * @param props.runtime The runtime executable that evaluates it.
94
95
  * @param props.additionalRuntime Selected Node executable exposed to the
95
96
  * descriptor and used by ttsx/config loaders, when distinct from runtime.
96
97
  * @param props.version This ttsc build's version.
97
- * @evidence contracts/common.md#principled-implementation Canonical identity includes factory context, sorted effective environment and fresh content/lexical/physical proof of both actual runtime authorities; unobserved startup preloads and unreadable runtimes cannot select a reusable entry.
98
+ * @evidence contracts/common.md#principled-implementation Canonical identity includes factory context, normalized evaluator working directory, sorted effective environment and fresh content/lexical/physical proof of both actual runtime authorities; unobserved startup preloads and unreadable runtimes cannot select a reusable entry.
98
99
  * @evidence contracts/common.md#clear-and-simple-design The locator delegates cache-root policy and runtime observation, then constructs only the descriptor entry identity.
99
100
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Unidentifiable runtime/storage returns null for uncached evaluation rather than weakening the key or selecting a fixture-specific runtime.
100
101
  * @evidence contracts/common.md#meaningful-documentation Native JSDoc identifies every key premise and null result; parameter entries and separate tags follow the documentation skill.
101
- * @evidence contracts/portability.md#os-neutral-implementation Native realpath/stat and path.resolve/join preserve executable and cache paths; environment ordering is lexical map data, not shell syntax.
102
+ * @evidence contracts/portability.md#os-neutral-implementation Native realpath/stat and path.resolve/join preserve executable, working-directory and cache paths; environment ordering is lexical map data, not shell syntax.
102
103
  * @evidence contracts/performance.md#efficient-algorithms Environment entries are merged/filtered/sorted by name bytes; context and key fields are serialized and hashed. Primary/secondary executable observation uses bounded read buffers but native metadata/path and escaping identity text also cost work; identical supplied spellings share one proof. Delegated root selection can walk ancestors/read manifests and inspect cache layout, so environment/runtime bytes are not the complete cost dimensions.
103
- * @evidence contracts/performance.md#reuse-equivalent-work The key shares only evaluations with identical descriptor/context/environment/runtime/version authorities; observed source state is separately checked by read.
104
+ * @evidence contracts/performance.md#reuse-equivalent-work The key shares only evaluations with identical descriptor/context/working-directory/environment/runtime/version authorities; observed source state is separately checked by read.
104
105
  *
105
106
  * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The locator stores no handle or historical entry population; executable observation owns its transient descriptor/close attempt and returns an unproved result on failure. Serialized key/context and returned path transfer without freezing mutable caller authority; persistence/pruning own stored entry lifetime.
106
107
  */
@@ -114,10 +115,13 @@ export namespace PluginDescriptorEvaluationCache {
114
115
  /** Complete JSON-serializable factory context passed to evaluation. */
115
116
  context: unknown;
116
117
 
118
+ /** Actual evaluator working directory, independent of its factory context. */
119
+ cwd: string;
120
+
117
121
  /** Complete child environment, with undefined values omitted from identity. */
118
122
  env: NodeJS.ProcessEnv;
119
123
 
120
- /** Consumer project anchoring cache-root selection. */
124
+ /** Project anchoring cache-root selection. */
121
125
  projectRoot: string;
122
126
 
123
127
  /** Resolved descriptor entry module whose evaluation is cached. */
@@ -167,6 +171,7 @@ export namespace PluginDescriptorEvaluationCache {
167
171
  props.version,
168
172
  path.resolve(props.request),
169
173
  props.context,
174
+ path.resolve(props.cwd),
170
175
  Object.entries(environment)
171
176
  .filter(
172
177
  (entry): entry is [string, string] => entry[1] !== undefined,
@@ -6,6 +6,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  import { readJsonFile } from "../../../compiler/internal/project/readJsonFile";
7
7
  import { isRelativePluginSpecifier } from "./isRelativePluginSpecifier";
8
8
  import { moduleResolutionBaseSelects } from "./moduleResolutionBaseSelects";
9
+ import { pluginResolutionFailure } from "./pluginResolutionFailure";
9
10
 
10
11
  /**
11
12
  * How ttsc finds a plugin package and reads its manifest.
@@ -277,7 +278,7 @@ export namespace PluginPackageResolution {
277
278
  }
278
279
 
279
280
  /**
280
- * Resolve a plugin specifier from the project root, using realpath when
281
+ * Resolve a plugin specifier from its consumer directory, using realpath when
281
282
  * available and retaining selected native spelling on canonicalization
282
283
  * failure.
283
284
  *
@@ -290,7 +291,7 @@ export namespace PluginPackageResolution {
290
291
  * @evidence contracts/common.md#principled-implementation Native absolute/relative inputs use their explicit base; bare packages opt into ttsc target semantics only when a matching branch exists, otherwise createRequire anchors Node resolution at the consuming project's package.json. This authority remains explicit when the caller was itself imported as CommonJS through an ESM loader.
291
292
  * @evidence contracts/common.md#clear-and-simple-design One dispatcher keeps plugin-only conditions local and returns best-effort canonical selection to descriptor loading; unsuccessful realpath is not physical-identity proof.
292
293
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Dedicated condition selection is a declared package extension, not process-wide patching; opted-in invalid targets fail without falling back to unrelated runtime exports.
293
- * @evidence contracts/common.md#meaningful-documentation Native JSDoc explains path versus package behavior and condition scope; helper comments explain error/null semantics and target constraints, with separated prose under the documentation skill.
294
+ * @evidence contracts/common.md#meaningful-documentation Native JSDoc explains path versus package behavior and condition scope; package failures retain their cause and code while naming the requested plugin and consumer directory, including runtimes whose resolver message is empty.
294
295
  * @evidence contracts/portability.md#os-neutral-implementation Native path classification/resolution and best-effort realpath preserve caller bases. Package target URL resolution uses a native pathToFileURL anchor and fileURLToPath decoding before native containment/file checks; URL/export grammar is not interpreted as literal native percent spelling.
295
296
  * @evidence contracts/performance.md#efficient-algorithms Path cases still perform native normalization/realpath; package cases include manifest discovery/read/parse and Node queries. Export selection classifies keys, compares patterns and may recursively substitute/inspect target nodes before short-circuit target resolution. Key/target/path/manifest bytes and native operations contribute cost; no target depth or size ceiling is supplied here.
296
297
  *
@@ -313,14 +314,18 @@ export namespace PluginPackageResolution {
313
314
  // loading the transform would have to build the runtime the transform
314
315
  // emits). Such a package opts in with a `ttsc` export condition that points
315
316
  // at a runtime-free descriptor; honour it here, scoped to plugin resolution.
316
- const conditioned = resolvePluginExportCondition(specifier, projectRoot);
317
- if (conditioned !== null) {
318
- return conditioned;
317
+ try {
318
+ const conditioned = resolvePluginExportCondition(specifier, projectRoot);
319
+ if (conditioned !== null) {
320
+ return conditioned;
321
+ }
322
+ const projectRequire = createRequire(
323
+ path.join(projectRoot, "package.json"),
324
+ );
325
+ return resolveRealPath(projectRequire.resolve(specifier));
326
+ } catch (error) {
327
+ throw pluginResolutionFailure(specifier, projectRoot, error);
319
328
  }
320
- const projectRequire = createRequire(
321
- path.join(projectRoot, "package.json"),
322
- );
323
- return resolveRealPath(projectRequire.resolve(specifier));
324
329
  }
325
330
 
326
331
  /**
@@ -9,12 +9,10 @@ import { isRelativePluginSpecifier } from "./isRelativePluginSpecifier";
9
9
  * The plugin entries a project declares, from its `tsconfig.json` and from the
10
10
  * `ttsc` block of its direct dependencies' manifests.
11
11
  *
12
- * Every entry carries the directory its specifier resolves from. A bare package
13
- * specifier resolves from the project root, never from the base config an
14
- * `extends` chain declared it in. A configured relative entry uses its known
15
- * declaring directory, falling back to project root; an automatic relative
16
- * marker uses its package directory. Explicit invocation lists use project root
17
- * for every entry, rather than recovering declaration provenance.
12
+ * Every entry carries the directory its specifier resolves from. Bare packages
13
+ * and explicit invocation lists use pluginConfigDir when supplied, otherwise
14
+ * the selected config directory. Configured relative entries retain their
15
+ * declaring directory; automatic relative markers use their package directory.
18
16
  *
19
17
  * @evidence contracts/common.md#principled-implementation Explicit entries replace discovery; otherwise inherited relative transforms retain their declaring base while bare packages resolve from the consumer, followed by distinct direct-dependency markers.
20
18
  * @evidence contracts/common.md#clear-and-simple-design Entry extraction owns ordering/provenance and delegates physical package resolution; descriptor execution/building remain outside discovery.
@@ -27,6 +25,32 @@ import { isRelativePluginSpecifier } from "./isRelativePluginSpecifier";
27
25
  * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Discovery collections and returned entries are call-owned; no retained resource or cross-call population is acquired.
28
26
  */
29
27
  export namespace ProjectPluginEntries {
28
+ /**
29
+ * Select the consumer directory for plugin packages and factory contexts.
30
+ *
31
+ * Generated configs supply their original directory through pluginConfigDir.
32
+ * The compiler's source root can include sibling packages and does not choose
33
+ * the consumer's dependencies.
34
+ *
35
+ * @evidence contracts/common.md#principled-implementation The selected config owns the consumer dependency lookup; an explicit pluginConfigDir restores that authority for generated wrappers independently of the compiler source root.
36
+ * @evidence contracts/common.md#clear-and-simple-design One path selection is shared by entry discovery, presence, host-input observation and factory contexts.
37
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Caller-declared wrapper provenance and the selected config determine the directory without package names or fixture paths.
38
+ * @evidence contracts/common.md#meaningful-documentation The comment explains the consumer anchor and why sibling-source roots cannot replace it.
39
+ * @evidence contracts/portability.md#os-neutral-implementation Native dirname and resolve preserve config path identity and resolve explicit relative origins from the recorded invocation cwd.
40
+ * @evidence contracts/performance.md#efficient-algorithms Selection performs native path operations proportional to path text without filesystem traversal.
41
+ *
42
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work This path selection owns no reusable observation or cross-call cache.
43
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources It returns a string and acquires no resource or retained population.
44
+ */
45
+ export function resolvePluginBaseDir(
46
+ project: ITtscParsedProjectConfig,
47
+ pluginConfigDir?: string,
48
+ ): string {
49
+ return pluginConfigDir === undefined || pluginConfigDir === ""
50
+ ? path.dirname(project.path)
51
+ : path.resolve(project.identity.invocationCwd, pluginConfigDir);
52
+ }
53
+
30
54
  /**
31
55
  * One declared plugin and the directory its specifier resolves from.
32
56
  *
@@ -66,16 +90,18 @@ export namespace ProjectPluginEntries {
66
90
  export function resolvePluginEntries(
67
91
  project: ITtscParsedProjectConfig,
68
92
  entries?: readonly ITtscProjectPluginConfig[],
93
+ pluginConfigDir?: string,
69
94
  ): ProjectPluginEntry[] {
95
+ const consumerDir = resolvePluginBaseDir(project, pluginConfigDir);
70
96
  if (entries !== undefined) {
71
97
  return entries.map((config) => ({
72
- baseDir: project.root,
98
+ baseDir: consumerDir,
73
99
  config,
74
100
  }));
75
101
  }
76
102
  const configured = project.compilerOptions.plugins.map((config, index) => {
77
- // A bare/package plugin specifier (e.g. "typia/lib/transform") must resolve
78
- // from the project's own node_modules, not from the tsconfig that declared
103
+ // A bare package specifier resolves from the consumer's dependencies,
104
+ // not from the tsconfig that declared
79
105
  // it: an `extends`ed base config (a shared `tests/config/tsconfig.json`)
80
106
  // declares the plugin, but the package is installed under the consuming
81
107
  // project. Only a relative specifier ("./plugin") is meaningful relative to
@@ -86,22 +112,21 @@ export namespace ProjectPluginEntries {
86
112
  isRelativePluginSpecifier(config.transform) &&
87
113
  declaringDir !== undefined
88
114
  ? declaringDir
89
- : project.root;
115
+ : consumerDir;
90
116
  return { baseDir, config };
91
117
  });
92
118
  return [
93
119
  ...configured,
94
- ...discoverPackagePluginEntries(project, configured),
120
+ ...discoverPackagePluginEntries(consumerDir, configured),
95
121
  ];
96
122
  }
97
123
 
98
124
  function discoverPackagePluginEntries(
99
- project: ITtscParsedProjectConfig,
125
+ consumerDir: string,
100
126
  configured: readonly ProjectPluginEntry[],
101
127
  ): ProjectPluginEntry[] {
102
- const projectPackageJson = PluginPackageResolution.findNearestPackageJson(
103
- project.root,
104
- );
128
+ const projectPackageJson =
129
+ PluginPackageResolution.findNearestPackageJson(consumerDir);
105
130
  if (projectPackageJson === undefined) {
106
131
  return [];
107
132
  }
@@ -3,11 +3,14 @@ import path from "node:path";
3
3
 
4
4
  import type { ITtscParsedProjectConfig } from "../../../structures/internal/ITtscParsedProjectConfig";
5
5
  import { PluginPackageResolution } from "./PluginPackageResolution";
6
+ import { ProjectPluginEntries } from "./ProjectPluginEntries";
6
7
  import { moduleResolutionBaseSelects } from "./moduleResolutionBaseSelects";
7
8
 
8
9
  /**
9
10
  * Return config ancestry and package-discovery manifest inputs, including
10
11
  * missing nearer candidates that could redirect the selected package later.
12
+ * Discovery uses the selected config directory or explicit pluginConfigDir,
13
+ * matching the entry resolver independently of the compiler's source root.
11
14
  *
12
15
  * @evidence contracts/common.md#principled-implementation Config ancestry, lexical selection and missing config candidates influence selection; automatic discovery additionally observes nearest manifest and dependency search candidates through the selecting root. The project reader separately states whether these config observations are complete.
13
16
  * @evidence contracts/common.md#clear-and-simple-design The collector obtains package resolution from its owner and assembles universal loader inputs, leaving source Program dependencies to the native reference graph.
@@ -22,6 +25,7 @@ import { moduleResolutionBaseSelects } from "./moduleResolutionBaseSelects";
22
25
  export function collectProjectHostInputs(
23
26
  project: ITtscParsedProjectConfig,
24
27
  includePluginDiscovery: boolean = true,
28
+ pluginConfigDir?: string,
25
29
  ): string[] {
26
30
  const inputs = new Set<string>(
27
31
  [...project.configPaths, ...(project.configInputs ?? [])].map((file) =>
@@ -30,7 +34,9 @@ export function collectProjectHostInputs(
30
34
  );
31
35
  if (!includePluginDiscovery) return [...inputs].sort();
32
36
  const manifestCandidates =
33
- PluginPackageResolution.collectNearestPackageJsonCandidates(project.root);
37
+ PluginPackageResolution.collectNearestPackageJsonCandidates(
38
+ ProjectPluginEntries.resolvePluginBaseDir(project, pluginConfigDir),
39
+ );
34
40
  for (const candidate of manifestCandidates) inputs.add(candidate);
35
41
  const manifest = manifestCandidates.find(
36
42
  PluginPackageResolution.existingFile,
@@ -11,6 +11,8 @@ import { ProjectPluginEntries } from "./ProjectPluginEntries";
11
11
  * omitted entries can therefore read manifests and resolve native packages.
12
12
  *
13
13
  * @param entries - Explicit entries; `false` always returns `false`.
14
+ * @param pluginConfigDir - Original consumer directory for generated configs;
15
+ * omitted entries otherwise discover from the selected config directory.
14
16
  * @evidence contracts/common.md#principled-implementation Explicit false disables all plugin discovery; otherwise the shared entry resolver's enabled-not-false predicate checks the same population the loader would consider.
15
17
  * @evidence contracts/common.md#clear-and-simple-design The presence query delegates config/package selection to its owner instead of reproducing descriptor evaluation or build logic.
16
18
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Enabled selection follows declared configuration, without assuming a known package marker or fabricating absence after a resolver error.
@@ -23,11 +25,14 @@ import { ProjectPluginEntries } from "./ProjectPluginEntries";
23
25
  export function hasProjectPluginEntries(
24
26
  project: ITtscParsedProjectConfig,
25
27
  entries?: readonly ITtscProjectPluginConfig[] | false,
28
+ pluginConfigDir?: string,
26
29
  ): boolean {
27
30
  if (entries === false) {
28
31
  return false;
29
32
  }
30
- return ProjectPluginEntries.resolvePluginEntries(project, entries).some(
31
- (entry) => entry.config.enabled !== false,
32
- );
33
+ return ProjectPluginEntries.resolvePluginEntries(
34
+ project,
35
+ entries,
36
+ pluginConfigDir,
37
+ ).some((entry) => entry.config.enabled !== false);
33
38
  }