repopo 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +57 -90
  3. package/THIRD-PARTY-LICENSES.txt +169 -11
  4. package/bin/dev.js +0 -0
  5. package/esm/adapters/fluidFramework.d.ts +13 -9
  6. package/esm/adapters/fluidFramework.d.ts.map +1 -1
  7. package/esm/adapters/fluidFramework.js +20 -43
  8. package/esm/adapters/fluidFramework.js.map +1 -1
  9. package/esm/api.d.ts +66 -1
  10. package/esm/api.d.ts.map +1 -1
  11. package/esm/api.js +18 -1
  12. package/esm/api.js.map +1 -1
  13. package/esm/baseCommand.d.ts.map +1 -1
  14. package/esm/baseCommand.js +2 -2
  15. package/esm/baseCommand.js.map +1 -1
  16. package/esm/commands/check-native.d.ts +5 -3
  17. package/esm/commands/check-native.d.ts.map +1 -1
  18. package/esm/commands/check-native.js +35 -7
  19. package/esm/commands/check-native.js.map +1 -1
  20. package/esm/commands/check.d.ts +8 -21
  21. package/esm/commands/check.d.ts.map +1 -1
  22. package/esm/commands/check.js +98 -154
  23. package/esm/commands/check.js.map +1 -1
  24. package/esm/commands/list.d.ts.map +1 -1
  25. package/esm/commands/list.js +1 -0
  26. package/esm/commands/list.js.map +1 -1
  27. package/esm/concurrency.d.ts +3 -0
  28. package/esm/concurrency.d.ts.map +1 -0
  29. package/esm/concurrency.js +8 -0
  30. package/esm/concurrency.js.map +1 -0
  31. package/esm/config.d.ts +10 -39
  32. package/esm/config.d.ts.map +1 -1
  33. package/esm/config.js +3 -7
  34. package/esm/config.js.map +1 -1
  35. package/esm/context.d.ts +10 -1
  36. package/esm/context.d.ts.map +1 -1
  37. package/esm/context.js +19 -1
  38. package/esm/context.js.map +1 -1
  39. package/esm/executionContractError.d.ts +4 -0
  40. package/esm/executionContractError.d.ts.map +1 -0
  41. package/esm/executionContractError.js +4 -0
  42. package/esm/executionContractError.js.map +1 -0
  43. package/esm/fileCache.d.ts +12 -0
  44. package/esm/fileCache.d.ts.map +1 -0
  45. package/esm/fileCache.js +37 -0
  46. package/esm/fileCache.js.map +1 -0
  47. package/esm/filePaths.d.ts +2 -0
  48. package/esm/filePaths.d.ts.map +1 -0
  49. package/esm/filePaths.js +9 -0
  50. package/esm/filePaths.js.map +1 -0
  51. package/esm/generators.d.ts +0 -20
  52. package/esm/generators.d.ts.map +1 -1
  53. package/esm/generators.js.map +1 -1
  54. package/esm/index.d.ts +3 -16
  55. package/esm/index.d.ts.map +1 -1
  56. package/esm/index.js +3 -17
  57. package/esm/index.js.map +1 -1
  58. package/esm/makePolicy.d.ts +20 -100
  59. package/esm/makePolicy.d.ts.map +1 -1
  60. package/esm/makePolicy.js +46 -132
  61. package/esm/makePolicy.js.map +1 -1
  62. package/esm/perf.d.ts +5 -2
  63. package/esm/perf.d.ts.map +1 -1
  64. package/esm/perf.js +10 -9
  65. package/esm/perf.js.map +1 -1
  66. package/esm/policies/CargoLicenceValidated.d.ts +4 -4
  67. package/esm/policies/CargoLicenceValidated.d.ts.map +1 -1
  68. package/esm/policies/CargoLicenceValidated.js +9 -7
  69. package/esm/policies/CargoLicenceValidated.js.map +1 -1
  70. package/esm/policies/CargoLintsConfigured.d.ts +4 -4
  71. package/esm/policies/CargoLintsConfigured.d.ts.map +1 -1
  72. package/esm/policies/CargoLintsConfigured.js +19 -15
  73. package/esm/policies/CargoLintsConfigured.js.map +1 -1
  74. package/esm/policies/CargoLockPolicy.d.ts +4 -4
  75. package/esm/policies/CargoLockPolicy.d.ts.map +1 -1
  76. package/esm/policies/CargoLockPolicy.js +15 -11
  77. package/esm/policies/CargoLockPolicy.js.map +1 -1
  78. package/esm/policies/CargoTomlRequired.d.ts +5 -5
  79. package/esm/policies/CargoTomlRequired.d.ts.map +1 -1
  80. package/esm/policies/CargoTomlRequired.js +7 -7
  81. package/esm/policies/CargoTomlRequired.js.map +1 -1
  82. package/esm/policies/CargoTomlSorted.d.ts +4 -4
  83. package/esm/policies/CargoTomlSorted.d.ts.map +1 -1
  84. package/esm/policies/CargoTomlSorted.js +21 -17
  85. package/esm/policies/CargoTomlSorted.js.map +1 -1
  86. package/esm/policies/ConditionalScripts.d.ts +10 -8
  87. package/esm/policies/ConditionalScripts.d.ts.map +1 -1
  88. package/esm/policies/ConditionalScripts.js +24 -22
  89. package/esm/policies/ConditionalScripts.js.map +1 -1
  90. package/esm/policies/ExactScripts.d.ts +8 -6
  91. package/esm/policies/ExactScripts.d.ts.map +1 -1
  92. package/esm/policies/ExactScripts.js +15 -18
  93. package/esm/policies/ExactScripts.js.map +1 -1
  94. package/esm/policies/GleamDepVersions.d.ts +5 -5
  95. package/esm/policies/GleamDepVersions.d.ts.map +1 -1
  96. package/esm/policies/GleamDepVersions.js +5 -5
  97. package/esm/policies/GleamDepVersions.js.map +1 -1
  98. package/esm/policies/GleamDocExists.d.ts +4 -4
  99. package/esm/policies/GleamDocExists.d.ts.map +1 -1
  100. package/esm/policies/GleamDocExists.js +6 -6
  101. package/esm/policies/GleamDocExists.js.map +1 -1
  102. package/esm/policies/GleamInternalModulesConfigured.d.ts +4 -4
  103. package/esm/policies/GleamInternalModulesConfigured.d.ts.map +1 -1
  104. package/esm/policies/GleamInternalModulesConfigured.js +8 -6
  105. package/esm/policies/GleamInternalModulesConfigured.js.map +1 -1
  106. package/esm/policies/GleamLicenceConfigured.d.ts +4 -4
  107. package/esm/policies/GleamLicenceConfigured.d.ts.map +1 -1
  108. package/esm/policies/GleamLicenceConfigured.js +8 -6
  109. package/esm/policies/GleamLicenceConfigured.js.map +1 -1
  110. package/esm/policies/GleamManifestCommitted.d.ts +10 -3
  111. package/esm/policies/GleamManifestCommitted.d.ts.map +1 -1
  112. package/esm/policies/GleamManifestCommitted.js +10 -9
  113. package/esm/policies/GleamManifestCommitted.js.map +1 -1
  114. package/esm/policies/GleamMonorepoConsistency.d.ts +13 -5
  115. package/esm/policies/GleamMonorepoConsistency.d.ts.map +1 -1
  116. package/esm/policies/GleamMonorepoConsistency.js +10 -10
  117. package/esm/policies/GleamMonorepoConsistency.js.map +1 -1
  118. package/esm/policies/GleamNameRestrictions.d.ts +4 -4
  119. package/esm/policies/GleamNameRestrictions.d.ts.map +1 -1
  120. package/esm/policies/GleamNameRestrictions.js +6 -6
  121. package/esm/policies/GleamNameRestrictions.js.map +1 -1
  122. package/esm/policies/GleamNoPathDepsInPublished.d.ts +4 -4
  123. package/esm/policies/GleamNoPathDepsInPublished.d.ts.map +1 -1
  124. package/esm/policies/GleamNoPathDepsInPublished.js +6 -6
  125. package/esm/policies/GleamNoPathDepsInPublished.js.map +1 -1
  126. package/esm/policies/GleamPathDepsValid.d.ts +3 -3
  127. package/esm/policies/GleamPathDepsValid.d.ts.map +1 -1
  128. package/esm/policies/GleamPathDepsValid.js +5 -5
  129. package/esm/policies/GleamPathDepsValid.js.map +1 -1
  130. package/esm/policies/GleamProjectStructure.d.ts +11 -4
  131. package/esm/policies/GleamProjectStructure.d.ts.map +1 -1
  132. package/esm/policies/GleamProjectStructure.js +7 -8
  133. package/esm/policies/GleamProjectStructure.js.map +1 -1
  134. package/esm/policies/GleamSharedDependencyVersions.d.ts +12 -4
  135. package/esm/policies/GleamSharedDependencyVersions.d.ts.map +1 -1
  136. package/esm/policies/GleamSharedDependencyVersions.js +10 -10
  137. package/esm/policies/GleamSharedDependencyVersions.js.map +1 -1
  138. package/esm/policies/GleamTomlRequired.d.ts +4 -4
  139. package/esm/policies/GleamTomlRequired.d.ts.map +1 -1
  140. package/esm/policies/GleamTomlRequired.js +5 -5
  141. package/esm/policies/GleamTomlRequired.js.map +1 -1
  142. package/esm/policies/GleamTomlSorted.d.ts +4 -4
  143. package/esm/policies/GleamTomlSorted.d.ts.map +1 -1
  144. package/esm/policies/GleamTomlSorted.js +21 -17
  145. package/esm/policies/GleamTomlSorted.js.map +1 -1
  146. package/esm/policies/GleamVersionConstraint.d.ts +4 -4
  147. package/esm/policies/GleamVersionConstraint.d.ts.map +1 -1
  148. package/esm/policies/GleamVersionConstraint.js +14 -10
  149. package/esm/policies/GleamVersionConstraint.js.map +1 -1
  150. package/esm/policies/HtmlFileHeaders.d.ts +4 -2
  151. package/esm/policies/HtmlFileHeaders.d.ts.map +1 -1
  152. package/esm/policies/HtmlFileHeaders.js +1 -1
  153. package/esm/policies/HtmlFileHeaders.js.map +1 -1
  154. package/esm/policies/JsTsFileHeaders.d.ts +4 -2
  155. package/esm/policies/JsTsFileHeaders.d.ts.map +1 -1
  156. package/esm/policies/JsTsFileHeaders.js +1 -1
  157. package/esm/policies/JsTsFileHeaders.js.map +1 -1
  158. package/esm/policies/LicenseFileExists.d.ts +10 -4
  159. package/esm/policies/LicenseFileExists.d.ts.map +1 -1
  160. package/esm/policies/LicenseFileExists.js +27 -20
  161. package/esm/policies/LicenseFileExists.js.map +1 -1
  162. package/esm/policies/MaxChangedFiles.d.ts +17 -0
  163. package/esm/policies/MaxChangedFiles.d.ts.map +1 -0
  164. package/esm/policies/MaxChangedFiles.js +22 -0
  165. package/esm/policies/MaxChangedFiles.js.map +1 -0
  166. package/esm/policies/MutuallyExclusiveScripts.d.ts +8 -6
  167. package/esm/policies/MutuallyExclusiveScripts.d.ts.map +1 -1
  168. package/esm/policies/MutuallyExclusiveScripts.js +9 -13
  169. package/esm/policies/MutuallyExclusiveScripts.js.map +1 -1
  170. package/esm/policies/NoBuildDirectory.d.ts +9 -3
  171. package/esm/policies/NoBuildDirectory.d.ts.map +1 -1
  172. package/esm/policies/NoBuildDirectory.js +8 -13
  173. package/esm/policies/NoBuildDirectory.js.map +1 -1
  174. package/esm/policies/NoJsFileExtensions.d.ts +9 -3
  175. package/esm/policies/NoJsFileExtensions.d.ts.map +1 -1
  176. package/esm/policies/NoJsFileExtensions.js +9 -14
  177. package/esm/policies/NoJsFileExtensions.js.map +1 -1
  178. package/esm/policies/NoLargeBinaryFiles.d.ts +9 -4
  179. package/esm/policies/NoLargeBinaryFiles.d.ts.map +1 -1
  180. package/esm/policies/NoLargeBinaryFiles.js +10 -16
  181. package/esm/policies/NoLargeBinaryFiles.js.map +1 -1
  182. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts +10 -6
  183. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts.map +1 -1
  184. package/esm/policies/NoPrivateWorkspaceDependencies.js +12 -14
  185. package/esm/policies/NoPrivateWorkspaceDependencies.js.map +1 -1
  186. package/esm/policies/NoTargetArtifacts.d.ts +9 -3
  187. package/esm/policies/NoTargetArtifacts.d.ts.map +1 -1
  188. package/esm/policies/NoTargetArtifacts.js +9 -14
  189. package/esm/policies/NoTargetArtifacts.js.map +1 -1
  190. package/esm/policies/NoUnsafeWithoutJustification.d.ts +11 -4
  191. package/esm/policies/NoUnsafeWithoutJustification.d.ts.map +1 -1
  192. package/esm/policies/NoUnsafeWithoutJustification.js +8 -9
  193. package/esm/policies/NoUnsafeWithoutJustification.js.map +1 -1
  194. package/esm/policies/NoWildcardDependencies.d.ts +5 -5
  195. package/esm/policies/NoWildcardDependencies.d.ts.map +1 -1
  196. package/esm/policies/NoWildcardDependencies.js +6 -6
  197. package/esm/policies/NoWildcardDependencies.js.map +1 -1
  198. package/esm/policies/PackageAllowedScopes.d.ts +9 -5
  199. package/esm/policies/PackageAllowedScopes.d.ts.map +1 -1
  200. package/esm/policies/PackageAllowedScopes.js +15 -16
  201. package/esm/policies/PackageAllowedScopes.js.map +1 -1
  202. package/esm/policies/PackageEsmType.d.ts +7 -5
  203. package/esm/policies/PackageEsmType.d.ts.map +1 -1
  204. package/esm/policies/PackageEsmType.js +31 -31
  205. package/esm/policies/PackageEsmType.js.map +1 -1
  206. package/esm/policies/PackageFolderName.d.ts +8 -6
  207. package/esm/policies/PackageFolderName.d.ts.map +1 -1
  208. package/esm/policies/PackageFolderName.js +13 -16
  209. package/esm/policies/PackageFolderName.js.map +1 -1
  210. package/esm/policies/PackageJsonProperties.d.ts +5 -3
  211. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  212. package/esm/policies/PackageJsonProperties.js +6 -17
  213. package/esm/policies/PackageJsonProperties.js.map +1 -1
  214. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts +2 -7
  215. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  216. package/esm/policies/PackageJsonRepoDirectoryProperty.js +28 -42
  217. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  218. package/esm/policies/PackageJsonSorted.d.ts +2 -2
  219. package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
  220. package/esm/policies/PackageJsonSorted.js +15 -30
  221. package/esm/policies/PackageJsonSorted.js.map +1 -1
  222. package/esm/policies/PackageLicense.d.ts +4 -4
  223. package/esm/policies/PackageLicense.d.ts.map +1 -1
  224. package/esm/policies/PackageLicense.js +31 -52
  225. package/esm/policies/PackageLicense.js.map +1 -1
  226. package/esm/policies/PackagePrivateField.d.ts +12 -8
  227. package/esm/policies/PackagePrivateField.d.ts.map +1 -1
  228. package/esm/policies/PackagePrivateField.js +31 -23
  229. package/esm/policies/PackagePrivateField.js.map +1 -1
  230. package/esm/policies/PackageReadme.d.ts +6 -5
  231. package/esm/policies/PackageReadme.d.ts.map +1 -1
  232. package/esm/policies/PackageReadme.js +22 -59
  233. package/esm/policies/PackageReadme.js.map +1 -1
  234. package/esm/policies/PackageScripts.d.ts +26 -22
  235. package/esm/policies/PackageScripts.d.ts.map +1 -1
  236. package/esm/policies/PackageScripts.js +39 -36
  237. package/esm/policies/PackageScripts.js.map +1 -1
  238. package/esm/policies/PackageTestScripts.d.ts +10 -6
  239. package/esm/policies/PackageTestScripts.d.ts.map +1 -1
  240. package/esm/policies/PackageTestScripts.js +14 -16
  241. package/esm/policies/PackageTestScripts.js.map +1 -1
  242. package/esm/policies/PublicApiDocumented.d.ts +4 -4
  243. package/esm/policies/PublicApiDocumented.d.ts.map +1 -1
  244. package/esm/policies/PublicApiDocumented.js +10 -8
  245. package/esm/policies/PublicApiDocumented.js.map +1 -1
  246. package/esm/policies/RequireCompanionChanges.d.ts +16 -0
  247. package/esm/policies/RequireCompanionChanges.d.ts.map +1 -0
  248. package/esm/policies/RequireCompanionChanges.js +42 -0
  249. package/esm/policies/RequireCompanionChanges.js.map +1 -0
  250. package/esm/policies/RequiredGitignorePatterns.d.ts +11 -4
  251. package/esm/policies/RequiredGitignorePatterns.d.ts.map +1 -1
  252. package/esm/policies/RequiredGitignorePatterns.js +29 -36
  253. package/esm/policies/RequiredGitignorePatterns.js.map +1 -1
  254. package/esm/policies/RequiredScripts.d.ts +9 -7
  255. package/esm/policies/RequiredScripts.d.ts.map +1 -1
  256. package/esm/policies/RequiredScripts.js +24 -24
  257. package/esm/policies/RequiredScripts.js.map +1 -1
  258. package/esm/policies/RustDocExists.d.ts +4 -4
  259. package/esm/policies/RustDocExists.d.ts.map +1 -1
  260. package/esm/policies/RustDocExists.js +6 -6
  261. package/esm/policies/RustDocExists.js.map +1 -1
  262. package/esm/policies/RustToolchainExists.d.ts +11 -4
  263. package/esm/policies/RustToolchainExists.d.ts.map +1 -1
  264. package/esm/policies/RustToolchainExists.js +10 -9
  265. package/esm/policies/RustToolchainExists.js.map +1 -1
  266. package/esm/policies/RustfmtConfigExists.d.ts +11 -4
  267. package/esm/policies/RustfmtConfigExists.d.ts.map +1 -1
  268. package/esm/policies/RustfmtConfigExists.js +10 -9
  269. package/esm/policies/RustfmtConfigExists.js.map +1 -1
  270. package/esm/policies/ScriptContains.d.ts +9 -7
  271. package/esm/policies/ScriptContains.d.ts.map +1 -1
  272. package/esm/policies/ScriptContains.js +9 -13
  273. package/esm/policies/ScriptContains.js.map +1 -1
  274. package/esm/policies/SharedDependencyVersions.d.ts +9 -4
  275. package/esm/policies/SharedDependencyVersions.d.ts.map +1 -1
  276. package/esm/policies/SharedDependencyVersions.js +28 -20
  277. package/esm/policies/SharedDependencyVersions.js.map +1 -1
  278. package/esm/policies/WorkspaceInheritance.d.ts +11 -4
  279. package/esm/policies/WorkspaceInheritance.d.ts.map +1 -1
  280. package/esm/policies/WorkspaceInheritance.js +10 -11
  281. package/esm/policies/WorkspaceInheritance.js.map +1 -1
  282. package/esm/policies/WorkspaceMembersValid.d.ts +11 -4
  283. package/esm/policies/WorkspaceMembersValid.d.ts.map +1 -1
  284. package/esm/policies/WorkspaceMembersValid.js +10 -11
  285. package/esm/policies/WorkspaceMembersValid.js.map +1 -1
  286. package/esm/policies/constants.d.ts.map +1 -1
  287. package/esm/policies/constants.js +1 -1
  288. package/esm/policies/constants.js.map +1 -1
  289. package/esm/policies.d.ts +15 -5
  290. package/esm/policies.d.ts.map +1 -1
  291. package/esm/policies.js +14 -5
  292. package/esm/policies.js.map +1 -1
  293. package/esm/policy.d.ts +67 -289
  294. package/esm/policy.d.ts.map +1 -1
  295. package/esm/policy.js +1 -122
  296. package/esm/policy.js.map +1 -1
  297. package/esm/policyDefiners/defineCargoPolicy.d.ts +25 -9
  298. package/esm/policyDefiners/defineCargoPolicy.d.ts.map +1 -1
  299. package/esm/policyDefiners/defineCargoPolicy.js +10 -28
  300. package/esm/policyDefiners/defineCargoPolicy.js.map +1 -1
  301. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts +27 -14
  302. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
  303. package/esm/policyDefiners/defineFileHeaderPolicy.js +16 -35
  304. package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
  305. package/esm/policyDefiners/defineGleamPolicy.d.ts +25 -9
  306. package/esm/policyDefiners/defineGleamPolicy.d.ts.map +1 -1
  307. package/esm/policyDefiners/defineGleamPolicy.js +10 -27
  308. package/esm/policyDefiners/defineGleamPolicy.js.map +1 -1
  309. package/esm/policyDefiners/definePackagePolicy.d.ts +24 -13
  310. package/esm/policyDefiners/definePackagePolicy.d.ts.map +1 -1
  311. package/esm/policyDefiners/definePackagePolicy.js +9 -32
  312. package/esm/policyDefiners/definePackagePolicy.js.map +1 -1
  313. package/esm/policyDefiners/spdxValidator.d.ts +4 -12
  314. package/esm/policyDefiners/spdxValidator.d.ts.map +1 -1
  315. package/esm/policyDefiners/spdxValidator.js +26 -14
  316. package/esm/policyDefiners/spdxValidator.js.map +1 -1
  317. package/esm/policyExecution.d.ts +20 -0
  318. package/esm/policyExecution.d.ts.map +1 -0
  319. package/esm/policyExecution.js +234 -0
  320. package/esm/policyExecution.js.map +1 -0
  321. package/esm/repositoryChanges.d.ts +26 -0
  322. package/esm/repositoryChanges.d.ts.map +1 -0
  323. package/esm/repositoryChanges.js +172 -0
  324. package/esm/repositoryChanges.js.map +1 -0
  325. package/esm/repositoryFiles.d.ts +6 -0
  326. package/esm/repositoryFiles.d.ts.map +1 -0
  327. package/esm/repositoryFiles.js +11 -0
  328. package/esm/repositoryFiles.js.map +1 -0
  329. package/esm/runner.d.ts +73 -47
  330. package/esm/runner.d.ts.map +1 -1
  331. package/esm/runner.js +158 -144
  332. package/esm/runner.js.map +1 -1
  333. package/esm/settlePolicyPromise.d.ts +4 -0
  334. package/esm/settlePolicyPromise.d.ts.map +1 -0
  335. package/esm/settlePolicyPromise.js +10 -0
  336. package/esm/settlePolicyPromise.js.map +1 -0
  337. package/esm/tsdoc-metadata.json +1 -1
  338. package/esm/utils/safePaths.d.ts +9 -0
  339. package/esm/utils/safePaths.d.ts.map +1 -0
  340. package/esm/utils/safePaths.js +40 -0
  341. package/esm/utils/safePaths.js.map +1 -0
  342. package/oclif.manifest.json +57 -2
  343. package/package.json +42 -27
  344. package/scripts/benchmark.mjs +341 -0
  345. package/sidecar/sidecar.mjs +388 -417
@@ -1,468 +1,439 @@
1
1
  /**
2
- * Node.js sidecar process for repopo-core (Rust).
2
+ * Newline-delimited JSON bridge for the native CLI.
3
3
  *
4
- * This process loads the TypeScript repopo configuration and executes
5
- * policy handlers on behalf of the Rust core engine. Communication
6
- * happens over stdin/stdout using newline-delimited JSON.
7
- *
8
- * Protocol:
9
- * Rust -> Node: JSON request (one per line)
10
- * Node -> Rust: JSON response (one per line)
11
- *
12
- * Request format:
13
- * { "method": "load_config", "params": { "configPath": "...", "gitRoot": "..." } }
14
- * { "method": "run_handler", "params": { "policyName": "...", "file": "...", "root": "...", "resolve": false } }
15
- * { "method": "run_resolver", "params": { "policyName": "...", "file": "...", "root": "..." } }
16
- * { "method": "run_handler_batch", "params": { "policyName": "...", "files": [...], "root": "...", "resolve": false } }
17
- * { "method": "run_resolver_batch", "params": { "policyName": "...", "files": [...], "root": "..." } }
18
- * { "method": "shutdown" }
19
- *
20
- * Response format:
21
- * { "ok": true, "data": ... }
22
- * { "ok": false, "error": "message" }
4
+ * Rust owns matching and scheduling. This bridge executes only explicitly
5
+ * selected policy units, sharing contracts and reconciliation with `check`.
23
6
  */
24
-
7
+ import { Console } from "node:console";
25
8
  import { stat } from "node:fs/promises";
9
+ import { env } from "node:process";
26
10
  import { createInterface } from "node:readline";
11
+ import { types } from "node:util";
27
12
  import { TypeScriptLoader } from "@tylerbu/lilconfig-loader-ts";
28
- import { run } from "effection";
13
+ import { all, run } from "effection";
29
14
  import { lilconfig } from "lilconfig";
30
-
31
- /** @type {Map<string, import("../src/policy.js").ConfiguredPolicy>} */
32
- let policiesByName = new Map();
33
-
34
- /** @type {Array<import("../src/policy.js").ConfiguredPolicy>} */
35
- let policiesByIndex = [];
36
-
37
- /** @type {string} */
38
- let storedGitRoot = "";
39
-
40
- /** @type {import("../src/config.js").RepopoConfig | undefined} */
41
- let loadedConfig = undefined;
42
-
43
- /**
44
- * Respond to the Rust side with a JSON message on stdout.
45
- * @param {object} response
46
- */
47
- function respond(response) {
48
- const json = JSON.stringify(response);
49
- process.stdout.write(json + "\n");
15
+ import { DefaultPolicyConfig } from "../esm/config.js";
16
+ import {
17
+ createExcludedPolicyFileMap,
18
+ exclusionRegex,
19
+ matchesPath,
20
+ } from "../esm/context.js";
21
+ import { ParsedFileCache } from "../esm/fileCache.js";
22
+ import { identifyPolicyInstances, makePolicy } from "../esm/makePolicy.js";
23
+ import { newPerfStats } from "../esm/perf.js";
24
+ import { executionMessage, PolicyExecutor } from "../esm/policyExecution.js";
25
+ import {
26
+ annotateInventory,
27
+ collectRepositoryInventory,
28
+ validateChangeOptions,
29
+ } from "../esm/repositoryChanges.js";
30
+
31
+ // Policy logging must not corrupt the JSON response stream.
32
+ globalThis.console = new Console(process.stderr);
33
+ const nativeStackGetter = Object.getOwnPropertyDescriptor(
34
+ new Error(),
35
+ "stack",
36
+ )?.get;
37
+ const nativePrepareStackTrace = Object.getOwnPropertyDescriptor(
38
+ Error,
39
+ "prepareStackTrace",
40
+ )?.value;
41
+
42
+ let registration;
43
+ let active;
44
+ let session;
45
+ let interrupted = false;
46
+ let closeInput;
47
+ for (const signal of ["SIGINT", "SIGTERM"]) {
48
+ process.on(signal, () => {
49
+ interrupted = true;
50
+ active?.abort(new Error(`Sidecar received ${signal}`));
51
+ closeInput?.();
52
+ });
50
53
  }
51
54
 
52
- /**
53
- * Serialize a RegExp to a pattern string and flags for the Rust side.
54
- * @param {RegExp} regex
55
- * @returns {{ source: string, flags: string }}
56
- */
57
- function serializeRegex(regex) {
58
- return {
59
- source: regex.source,
60
- flags: regex.flags,
61
- };
55
+ async function respond(response) {
56
+ await new Promise((resolve, reject) => {
57
+ process.stdout.write(`${JSON.stringify(response)}\n`, (error) =>
58
+ error ? reject(error) : resolve(),
59
+ );
60
+ });
62
61
  }
63
62
 
64
- /**
65
- * Serialize policy metadata for the Rust side.
66
- * Strips out functions and sends only what Rust needs for matching.
67
- * @param {import("../src/policy.js").ConfiguredPolicy} policy
68
- * @returns {object}
69
- */
70
- function serializePolicy(policy) {
71
- const match = serializeRegex(policy.match);
72
-
73
- // Convert excludeFiles to regex pattern strings
74
- const excludeFiles = (policy.excludeFiles ?? policy.exclude ?? []).map(
75
- (e) => {
76
- if (e instanceof RegExp) {
77
- return e.source;
78
- }
79
- return String(e);
80
- },
81
- );
82
-
83
- return {
84
- name: policy.name,
85
- description: policy.description,
86
- matchPattern: match.source,
87
- matchFlags: match.flags,
88
- hasResolver: typeof policy.resolver === "function",
89
- excludeFiles,
90
- };
63
+ function serializeRegex(regex) {
64
+ return { source: regex.source, flags: regex.flags };
91
65
  }
92
66
 
93
- /**
94
- * Check if a value is an Effection Operation (generator).
95
- * @param {unknown} value
96
- * @returns {boolean}
97
- */
98
- function isOperation(value) {
99
- return (
100
- typeof value === "object" &&
101
- value !== null &&
102
- "next" in value &&
103
- typeof value.next === "function"
104
- );
67
+ function serializeExclusion(pattern) {
68
+ return pattern instanceof RegExp ? serializeRegex(pattern) : pattern;
105
69
  }
106
70
 
107
- /**
108
- * Execute a policy handler, handling both async and generator (Effection) return types.
109
- * @param {import("../src/policy.js").PolicyHandler} handler
110
- * @param {import("../src/policy.js").PolicyArgs} args
111
- * @returns {Promise<import("../src/policy.js").PolicyHandlerResult>}
112
- */
113
- async function executeHandler(handler, args) {
114
- const result = handler(args);
115
-
116
- if (result instanceof Promise) {
117
- return result;
118
- }
119
-
120
- if (isOperation(result)) {
121
- // Run the generator through Effection
122
- return run(function* () {
123
- return yield* result;
124
- });
125
- }
126
-
127
- // Shouldn't happen, but handle it
128
- return result;
129
- }
130
-
131
- /**
132
- * Handle a load_config request.
133
- * @param {object} params
134
- */
135
- async function handleLoadConfig(params) {
71
+ /** Preserve Error's non-enumerable details and safely encode non-JSON causes. */
72
+ function serializeCause(value, seen = new WeakSet(), depth = 0) {
136
73
  try {
137
- const searchPath = params.configPath ?? process.cwd();
138
-
139
- // Load config using lilconfig (same mechanism as CommandWithConfig)
140
- const configLoader = lilconfig("repopo", {
141
- searchPlaces: [
142
- "repopo.config.ts",
143
- "repopo.config.mjs",
144
- "repopo.config.cjs",
145
- ],
146
- loaders: {
147
- ".ts": TypeScriptLoader,
148
- },
149
- });
150
-
151
- const pathStats = await stat(searchPath);
152
- const maybeConfig = pathStats.isDirectory()
153
- ? await configLoader.search(searchPath)
154
- : await configLoader.load(searchPath);
155
-
156
- if (maybeConfig !== null) {
157
- loadedConfig = maybeConfig.config?.default ?? maybeConfig.config;
158
- } else {
159
- // Use default config
160
- const { DefaultPolicyConfig } = await import("../esm/config.js");
161
- loadedConfig = DefaultPolicyConfig;
162
- }
163
-
164
- // Store git root for reuse in batch calls
165
- storedGitRoot = params.gitRoot ?? process.cwd();
166
-
167
- // Build the policy map and index array
168
- policiesByName.clear();
169
- policiesByIndex = [];
170
- const policies =
171
- loadedConfig?.policies ??
172
- (await import("../esm/config.js")).DefaultPolicyConfig.policies;
173
- for (const p of policies) {
174
- policiesByName.set(p.name, p);
175
- policiesByIndex.push(p);
176
- }
177
-
178
- // Serialize for Rust
179
- const serializedPolicies = policies.map(serializePolicy);
180
- const excludeFiles = (loadedConfig?.excludeFiles ?? []).map((e) => {
181
- if (e instanceof RegExp) {
182
- return e.source;
183
- }
184
- return String(e);
185
- });
186
-
187
- respond({
188
- ok: true,
189
- data: {
190
- policies: serializedPolicies,
191
- excludeFiles,
192
- },
193
- });
194
- } catch (err) {
195
- respond({
196
- ok: false,
197
- error: `Failed to load config: ${err.message}`,
198
- });
74
+ return serializeValue(value, seen, depth);
75
+ } catch {
76
+ return { type: "uninspectable" };
199
77
  }
200
78
  }
201
79
 
202
- /**
203
- * Handle a run_handler request.
204
- * @param {object} params
205
- */
206
- async function handleRunHandler(params) {
207
- try {
208
- const policy = policiesByName.get(params.policyName);
209
- if (!policy) {
210
- respond({
211
- ok: false,
212
- error: `Unknown policy: ${params.policyName}`,
213
- });
214
- return;
215
- }
216
-
217
- const handler = policy._internalHandler ?? policy.handler;
218
- const args = {
219
- file: params.file,
220
- root: params.root,
221
- resolve: params.resolve ?? false,
222
- config: policy.config,
223
- };
224
-
225
- const result = await executeHandler(handler, args);
226
-
227
- // Serialize the result — `true` passes through as-is,
228
- // objects (PolicyError/PolicyFailure) are serialized.
229
- respond({ ok: true, data: result });
230
- } catch (err) {
231
- respond({
232
- ok: false,
233
- error: `Handler error for ${params.policyName}: ${err.message}`,
234
- });
80
+ function serializeValue(value, seen, depth) {
81
+ if (value === undefined) return { type: "undefined" };
82
+ if (typeof value === "bigint" || typeof value === "symbol") {
83
+ return { type: typeof value, value: String(value) };
235
84
  }
236
- }
237
-
238
- /**
239
- * Handle a run_handler_batch request.
240
- * Runs the handler for each file sequentially and returns compact results.
241
- * Accepts policyId (index) and uses stored gitRoot.
242
- * @param {object} params
243
- */
244
- async function handleRunHandlerBatch(params) {
85
+ if (typeof value === "function") return { type: "function" };
86
+ if (typeof value === "number" && !Number.isFinite(value)) {
87
+ return { type: "number", value: String(value) };
88
+ }
89
+ if (value === null || typeof value !== "object") return value;
90
+ // Proxy traps are arbitrary code, including during descriptor inspection.
91
+ if (types.isProxy(value)) return { type: "proxy" };
92
+ if (seen.has(value)) return { type: "circular" };
93
+ if (depth >= 20) return { type: "truncated" };
94
+ seen.add(value);
245
95
  try {
246
- const policy = policiesByIndex[params.policyId];
247
- if (!policy) {
248
- respond({
249
- ok: false,
250
- error: `Unknown policy index: ${params.policyId}`,
251
- });
252
- return;
96
+ if (Array.isArray(value)) {
97
+ const length = Object.getOwnPropertyDescriptor(value, "length").value;
98
+ const array = Array.from(
99
+ { length: Math.min(length, 10000) },
100
+ (_, index) => {
101
+ const descriptor = Object.getOwnPropertyDescriptor(
102
+ value,
103
+ String(index),
104
+ );
105
+ return descriptor && "value" in descriptor
106
+ ? serializeCause(descriptor.value, seen, depth + 1)
107
+ : { type: descriptor ? "accessor" : "hole" };
108
+ },
109
+ );
110
+ if (length > array.length) array.push({ type: "truncated" });
111
+ return array;
253
112
  }
254
-
255
- const handler = policy._internalHandler ?? policy.handler;
256
- const resolve = params.resolve ?? false;
257
- /** @type {string[]} */
258
- const pass = [];
259
- /** @type {Array<{file: string, error?: string, errorMessages?: string[], fixable?: boolean, fixed?: boolean, manualFix?: string}>} */
260
- const fail = [];
261
-
262
- for (const file of params.files) {
263
- try {
264
- const args = {
265
- file,
266
- root: storedGitRoot,
267
- resolve,
268
- config: policy.config,
269
- };
270
- const result = await executeHandler(handler, args);
271
- if (result === true) {
272
- pass.push(file);
273
- } else {
274
- fail.push({
275
- file,
276
- error: result.error,
277
- errorMessages: result.errorMessages,
278
- fixable: result.fixable ?? result.autoFixable,
279
- fixed: result.fixed ?? result.resolved,
280
- manualFix: result.manualFix,
281
- });
113
+ const entries = [];
114
+ if (types.isNativeError(value)) {
115
+ for (const key of ["name", "message"]) {
116
+ let current = value;
117
+ let found = false;
118
+ while (current && !types.isProxy(current)) {
119
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
120
+ if (descriptor) {
121
+ found = true;
122
+ entries.push([
123
+ key,
124
+ "value" in descriptor
125
+ ? serializeCause(descriptor.value, seen, depth + 1)
126
+ : { type: "accessor" },
127
+ ]);
128
+ break;
129
+ }
130
+ current = Object.getPrototypeOf(current);
282
131
  }
283
- } catch (err) {
284
- fail.push({
285
- file,
286
- error: `Handler error: ${err.message}`,
287
- fixable: false,
132
+ if (!found && current) entries.push([key, { type: "proxy" }]);
133
+ }
134
+ }
135
+ for (const key of Object.getOwnPropertyNames(value)) {
136
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
137
+ // V8 lazily exposes native Error stacks via a native accessor.
138
+ // Never call a user getter or Error.prepareStackTrace hook.
139
+ if (key === "stack" && types.isNativeError(value) && descriptor?.get) {
140
+ const prepare = Object.getOwnPropertyDescriptor(
141
+ Error,
142
+ "prepareStackTrace",
143
+ );
144
+ const plain = ["name", "message"].every((field) => {
145
+ const entry = entries.find(([name]) => name === field)?.[1];
146
+ return entry === undefined || typeof entry === "string";
288
147
  });
148
+ if (
149
+ descriptor.get === nativeStackGetter &&
150
+ (!prepare ||
151
+ ("value" in prepare &&
152
+ prepare.value === nativePrepareStackTrace)) &&
153
+ plain
154
+ ) {
155
+ const stack = descriptor.get.call(value);
156
+ entries.push([
157
+ key,
158
+ typeof stack === "string" ? stack : { type: "uninspectable" },
159
+ ]);
160
+ continue;
161
+ }
289
162
  }
163
+ entries.push([
164
+ key,
165
+ descriptor && "value" in descriptor
166
+ ? serializeCause(descriptor.value, seen, depth + 1)
167
+ : { type: "accessor" },
168
+ ]);
290
169
  }
291
-
292
- respond({ ok: true, data: { pass, fail } });
293
- } catch (err) {
294
- respond({
295
- ok: false,
296
- error: `Batch handler error for policy index ${params.policyId}: ${err.message}`,
297
- });
170
+ return Object.fromEntries(entries);
171
+ } finally {
172
+ seen.delete(value);
298
173
  }
299
174
  }
300
175
 
301
- /**
302
- * Handle a run_resolver request.
303
- * @param {object} params
304
- */
305
- async function handleRunResolver(params) {
306
- try {
307
- const policy = policiesByName.get(params.policyName);
308
- if (!policy) {
309
- respond({
310
- ok: false,
311
- error: `Unknown policy: ${params.policyName}`,
312
- });
313
- return;
314
- }
176
+ function serializeRun(result) {
177
+ return {
178
+ ...result,
179
+ errors: result.errors.map((error) => ({
180
+ ...error,
181
+ cause: serializeCause(error.cause),
182
+ })),
183
+ perfStats: {
184
+ ...result.perfStats,
185
+ data: Object.fromEntries(
186
+ [...result.perfStats.data].map(([action, timings]) => [
187
+ action,
188
+ Object.fromEntries(timings),
189
+ ]),
190
+ ),
191
+ },
192
+ };
193
+ }
315
194
 
316
- if (typeof policy.resolver !== "function") {
317
- respond({
318
- ok: false,
319
- error: `Policy ${params.policyName} has no resolver`,
195
+ async function loadConfig(params) {
196
+ registration = undefined;
197
+ session?.cache?.clear();
198
+ session = undefined;
199
+ const searchPath = params.configPath ?? process.cwd();
200
+ const loader = lilconfig("repopo", {
201
+ searchPlaces: [
202
+ "repopo.config.ts",
203
+ "repopo.config.mjs",
204
+ "repopo.config.cjs",
205
+ ],
206
+ loaders: { ".ts": TypeScriptLoader },
207
+ });
208
+ const pathStats = await stat(searchPath);
209
+ const loaded = pathStats.isDirectory()
210
+ ? await loader.search(searchPath)
211
+ : await loader.load(searchPath);
212
+ const config =
213
+ loaded?.config?.default ?? loaded?.config ?? DefaultPolicyConfig;
214
+ const policies = identifyPolicyInstances(
215
+ (config.policies ?? DefaultPolicyConfig.policies).map((policy) => {
216
+ if ("excludeFiles" in policy) {
217
+ throw new TypeError(
218
+ "Per-instance excludeFiles is retired; use exclude.",
219
+ );
220
+ }
221
+ return makePolicy(policy, {
222
+ config: policy.config,
223
+ exclude: policy.exclude,
224
+ instanceId: policy.instanceId,
320
225
  });
321
- return;
322
- }
323
-
324
- const args = {
325
- file: params.file,
326
- root: params.root,
327
- config: policy.config,
328
- };
226
+ }),
227
+ );
228
+ const options = {
229
+ policies,
230
+ root: params.gitRoot ?? process.cwd(),
231
+ exclude: config.excludeFiles ?? [],
232
+ changes: config.changes,
233
+ };
234
+ options.exclude.forEach(exclusionRegex);
235
+ createExcludedPolicyFileMap(policies);
236
+ const metadata = {
237
+ policies: policies.map((policy) => ({
238
+ name: policy.name,
239
+ instanceId: policy.instanceId,
240
+ description: policy.description,
241
+ scope: policy.scope ?? "file",
242
+ ...(policy.match ? { match: serializeRegex(policy.match) } : {}),
243
+ hasResolver: typeof policy.resolver === "function",
244
+ exclude: (policy.exclude ?? []).map(serializeExclusion),
245
+ })),
246
+ excludeFiles: options.exclude.map(serializeExclusion),
247
+ };
248
+ registration = options;
249
+ return metadata;
250
+ }
329
251
 
330
- const result = await executeHandler(policy.resolver, args);
331
- respond({ ok: true, data: result });
332
- } catch (err) {
333
- respond({
334
- ok: false,
335
- error: `Resolver error for ${params.policyName}: ${err.message}`,
336
- });
337
- }
252
+ async function beginRun(params) {
253
+ if (!registration)
254
+ throw new Error("Load configuration before running policies.");
255
+ session?.cache?.clear();
256
+ session = undefined;
257
+ const mode = params.changeMode ?? registration.changes?.mode;
258
+ const options = {
259
+ mode,
260
+ base:
261
+ params.changeBase ??
262
+ ((mode ?? "pr") === "pr"
263
+ ? env.GITHUB_BASE_REF ||
264
+ env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME ||
265
+ registration.changes?.base
266
+ : undefined),
267
+ };
268
+ validateChangeOptions(options);
269
+ const inventory =
270
+ registration.policies.some((policy) => policy.scope === "changes") ||
271
+ params.changeMode != null ||
272
+ params.changeBase != null
273
+ ? await collectRepositoryInventory(registration.root, options)
274
+ : [];
275
+ session = {
276
+ inventory,
277
+ executor: new PolicyExecutor(registration.root, params.fix ?? false),
278
+ cache: params.fix ? undefined : new ParsedFileCache(),
279
+ };
280
+ return {};
338
281
  }
339
282
 
340
- /**
341
- * Handle a run_resolver_batch request.
342
- * Runs the resolver for each file sequentially and returns compact results.
343
- * Accepts policyId (index) and uses stored gitRoot.
344
- * @param {object} params
345
- */
346
- async function handleRunResolverBatch(params) {
283
+ async function executeUnits(params) {
284
+ if (!registration || !session)
285
+ throw new Error(
286
+ "Load configuration and begin a run before executing units.",
287
+ );
288
+ active = new AbortController();
289
+ const task = run(function* () {
290
+ return yield* all(
291
+ params.units.map((unit) =>
292
+ (function* () {
293
+ const policy = registration.policies.find(
294
+ (entry) => entry.instanceId === unit.policyId,
295
+ );
296
+ if (!policy)
297
+ throw new Error(`Unknown policy instance: ${unit.policyId}`);
298
+ const result = {
299
+ findings: [],
300
+ errors: [],
301
+ skipped: [],
302
+ perfStats: newPerfStats(),
303
+ };
304
+ if (policy.scope === "repository" || policy.scope === "changes") {
305
+ if (!Array.isArray(unit.files))
306
+ throw new Error("Repository units require explicit inventory.");
307
+ const exclusions = [
308
+ ...registration.exclude,
309
+ ...(policy.exclude ?? []),
310
+ ].map(exclusionRegex);
311
+ const entries = annotateInventory(
312
+ unit.files,
313
+ session.inventory,
314
+ ).filter(
315
+ (entry) =>
316
+ !exclusions.some((regex) => matchesPath(regex, entry.path)),
317
+ );
318
+ yield* session.executor.repository(policy, entries, result);
319
+ } else {
320
+ if (typeof unit.file !== "string")
321
+ throw new Error("File units require a file.");
322
+ yield* session.executor.file(
323
+ unit.file,
324
+ policy,
325
+ result,
326
+ session.cache,
327
+ );
328
+ }
329
+ return serializeRun(result);
330
+ })(),
331
+ ),
332
+ );
333
+ });
334
+ const abort = () => {
335
+ task.halt().catch(() => undefined);
336
+ };
337
+ active.signal.addEventListener("abort", abort, { once: true });
347
338
  try {
348
- const policy = policiesByIndex[params.policyId];
349
- if (!policy) {
350
- respond({
351
- ok: false,
352
- error: `Unknown policy index: ${params.policyId}`,
353
- });
354
- return;
355
- }
356
-
357
- if (typeof policy.resolver !== "function") {
358
- respond({
359
- ok: false,
360
- error: `Policy index ${params.policyId} has no resolver`,
361
- });
362
- return;
363
- }
339
+ return await task;
340
+ } finally {
341
+ active.signal.removeEventListener("abort", abort);
342
+ active = undefined;
343
+ }
344
+ }
364
345
 
365
- /** @type {string[]} */
366
- const pass = [];
367
- /** @type {Array<{file: string, error?: string, errorMessages?: string[], fixable?: boolean, fixed?: boolean, manualFix?: string}>} */
368
- const fail = [];
346
+ function matchFiles(params) {
347
+ const regex =
348
+ typeof params.pattern === "string"
349
+ ? exclusionRegex(params.pattern)
350
+ : new RegExp(params.pattern.source, params.pattern.flags);
351
+ return params.files.map((file) => {
352
+ regex.lastIndex = 0;
353
+ return regex.test(file);
354
+ });
355
+ }
369
356
 
370
- for (const file of params.files) {
371
- try {
372
- const args = {
373
- file,
374
- root: storedGitRoot,
375
- config: policy.config,
376
- };
377
- const result = await executeHandler(policy.resolver, args);
378
- if (result === true) {
379
- pass.push(file);
380
- } else {
381
- fail.push({
382
- file,
383
- error: result.error,
384
- errorMessages: result.errorMessages,
385
- fixable: result.fixable ?? result.autoFixable,
386
- fixed: result.fixed ?? result.resolved,
387
- manualFix: result.manualFix,
388
- });
357
+ async function main() {
358
+ const rl = createInterface({ input: process.stdin, terminal: false });
359
+ closeInput = () => rl.close();
360
+ let closing = false;
361
+ let shutdownRequested = false;
362
+ let pendingWork = 0;
363
+ rl.on("line", (line) => {
364
+ try {
365
+ const { method } = JSON.parse(line);
366
+ if (method === "execute_units") pendingWork++;
367
+ if (method === "shutdown") {
368
+ closing = true;
369
+ shutdownRequested = true;
370
+ if (active || pendingWork > 0) {
371
+ interrupted = true;
372
+ active?.abort(new Error("Native parent requested shutdown"));
389
373
  }
390
- } catch (err) {
391
- fail.push({
392
- file,
393
- error: `Resolver error: ${err.message}`,
394
- fixable: false,
395
- fixed: false,
396
- });
397
374
  }
375
+ } catch {
376
+ // The request loop reports malformed JSON.
377
+ }
378
+ });
379
+ // A dead native parent closes the pipe. Halt owned operations before exit.
380
+ rl.on("close", () => {
381
+ if (!closing) {
382
+ interrupted = true;
383
+ active?.abort(new Error("Native parent closed the request stream"));
398
384
  }
399
-
400
- respond({ ok: true, data: { pass, fail } });
401
- } catch (err) {
402
- respond({
403
- ok: false,
404
- error: `Batch resolver error for policy index ${params.policyId}: ${err.message}`,
405
- });
406
- }
407
- }
408
-
409
- /**
410
- * Main loop: read JSON requests from stdin, dispatch, respond on stdout.
411
- */
412
- async function main() {
413
- const rl = createInterface({
414
- input: process.stdin,
415
- terminal: false,
416
385
  });
417
-
418
386
  for await (const line of rl) {
387
+ if (interrupted || shutdownRequested) break;
419
388
  if (!line.trim()) continue;
420
-
421
- let request;
422
389
  try {
423
- request = JSON.parse(line);
424
- } catch {
425
- respond({ ok: false, error: `Invalid JSON: ${line}` });
426
- continue;
427
- }
428
-
429
- const { method, params } = request;
430
-
431
- switch (method) {
432
- case "load_config":
433
- await handleLoadConfig(params ?? {});
434
- break;
435
-
436
- case "run_handler":
437
- await handleRunHandler(params ?? {});
438
- break;
439
-
440
- case "run_resolver":
441
- await handleRunResolver(params ?? {});
442
- break;
443
-
444
- case "run_handler_batch":
445
- await handleRunHandlerBatch(params ?? {});
446
- break;
447
-
448
- case "run_resolver_batch":
449
- await handleRunResolverBatch(params ?? {});
450
- break;
451
-
452
- case "shutdown":
453
- process.exit(0);
390
+ const { method, params = {} } = JSON.parse(line);
391
+ if (method === "shutdown") {
392
+ closing = true;
454
393
  break;
455
-
456
- default:
457
- respond({ ok: false, error: `Unknown method: ${method}` });
394
+ }
395
+ let data;
396
+ switch (method) {
397
+ case "load_config":
398
+ data = await loadConfig(params);
399
+ break;
400
+ case "begin_run":
401
+ data = await beginRun(params);
402
+ break;
403
+ case "execute_units":
404
+ try {
405
+ data = await executeUnits(params);
406
+ } finally {
407
+ pendingWork--;
408
+ }
409
+ break;
410
+ case "match_files":
411
+ if (!registration)
412
+ throw new Error("Load configuration before matching.");
413
+ data = matchFiles(params);
414
+ break;
415
+ default:
416
+ throw new Error(`Unknown method: ${method}`);
417
+ }
418
+ if (interrupted) break;
419
+ await respond({ ok: true, data });
420
+ } catch (error) {
421
+ if (interrupted) break;
422
+ await respond({
423
+ ok: false,
424
+ error: executionMessage(error),
425
+ cause: serializeCause(error),
426
+ });
458
427
  }
428
+ if (interrupted) break;
459
429
  }
460
-
461
- // stdin closed — exit cleanly
462
- process.exit(0);
430
+ closing = true;
431
+ session?.cache?.clear();
432
+ rl.close();
433
+ process.exit(interrupted ? 130 : 0);
463
434
  }
464
435
 
465
- main().catch((err) => {
466
- process.stderr.write(`Sidecar fatal error: ${err.message}\n`);
436
+ main().catch((error) => {
437
+ process.stderr.write(`Sidecar fatal error: ${error.message}\n`);
467
438
  process.exit(1);
468
439
  });