repopo 0.10.0 → 0.12.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 (229) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +41 -2
  3. package/THIRD-PARTY-LICENSES.txt +71 -11
  4. package/bin/dev.js +5 -1
  5. package/esm/baseCommand.d.ts +1 -1
  6. package/esm/baseCommand.d.ts.map +1 -1
  7. package/esm/baseCommand.js +6 -9
  8. package/esm/baseCommand.js.map +1 -1
  9. package/esm/commands/check-native.d.ts +25 -0
  10. package/esm/commands/check-native.d.ts.map +1 -0
  11. package/esm/commands/check-native.js +137 -0
  12. package/esm/commands/check-native.js.map +1 -0
  13. package/esm/commands/check.d.ts +8 -20
  14. package/esm/commands/check.d.ts.map +1 -1
  15. package/esm/commands/check.js +77 -186
  16. package/esm/commands/check.js.map +1 -1
  17. package/esm/commands/list.d.ts.map +1 -1
  18. package/esm/commands/list.js +5 -6
  19. package/esm/commands/list.js.map +1 -1
  20. package/esm/context.d.ts +13 -3
  21. package/esm/context.d.ts.map +1 -1
  22. package/esm/context.js +11 -1
  23. package/esm/context.js.map +1 -1
  24. package/esm/index.d.ts +6 -1
  25. package/esm/index.d.ts.map +1 -1
  26. package/esm/index.js +3 -0
  27. package/esm/index.js.map +1 -1
  28. package/esm/makePolicy.d.ts +29 -2
  29. package/esm/makePolicy.d.ts.map +1 -1
  30. package/esm/makePolicy.js +46 -2
  31. package/esm/makePolicy.js.map +1 -1
  32. package/esm/perf.d.ts +7 -2
  33. package/esm/perf.d.ts.map +1 -1
  34. package/esm/perf.js.map +1 -1
  35. package/esm/policies/CargoLicenceValidated.d.ts +41 -0
  36. package/esm/policies/CargoLicenceValidated.d.ts.map +1 -0
  37. package/esm/policies/CargoLicenceValidated.js +45 -0
  38. package/esm/policies/CargoLicenceValidated.js.map +1 -0
  39. package/esm/policies/CargoLintsConfigured.d.ts +30 -0
  40. package/esm/policies/CargoLintsConfigured.d.ts.map +1 -0
  41. package/esm/policies/CargoLintsConfigured.js +61 -0
  42. package/esm/policies/CargoLintsConfigured.js.map +1 -0
  43. package/esm/policies/CargoLockPolicy.d.ts +34 -0
  44. package/esm/policies/CargoLockPolicy.d.ts.map +1 -0
  45. package/esm/policies/CargoLockPolicy.js +57 -0
  46. package/esm/policies/CargoLockPolicy.js.map +1 -0
  47. package/esm/policies/CargoTomlRequired.d.ts +34 -0
  48. package/esm/policies/CargoTomlRequired.d.ts.map +1 -0
  49. package/esm/policies/CargoTomlRequired.js +77 -0
  50. package/esm/policies/CargoTomlRequired.js.map +1 -0
  51. package/esm/policies/CargoTomlSorted.d.ts +20 -0
  52. package/esm/policies/CargoTomlSorted.d.ts.map +1 -0
  53. package/esm/policies/CargoTomlSorted.js +164 -0
  54. package/esm/policies/CargoTomlSorted.js.map +1 -0
  55. package/esm/policies/ConditionalScripts.d.ts.map +1 -1
  56. package/esm/policies/ConditionalScripts.js +3 -2
  57. package/esm/policies/ConditionalScripts.js.map +1 -1
  58. package/esm/policies/ExactScripts.d.ts.map +1 -1
  59. package/esm/policies/ExactScripts.js +3 -2
  60. package/esm/policies/ExactScripts.js.map +1 -1
  61. package/esm/policies/GleamDepVersions.d.ts +25 -0
  62. package/esm/policies/GleamDepVersions.d.ts.map +1 -0
  63. package/esm/policies/GleamDepVersions.js +67 -0
  64. package/esm/policies/GleamDepVersions.js.map +1 -0
  65. package/esm/policies/GleamDocExists.d.ts +24 -0
  66. package/esm/policies/GleamDocExists.d.ts.map +1 -0
  67. package/esm/policies/GleamDocExists.js +39 -0
  68. package/esm/policies/GleamDocExists.js.map +1 -0
  69. package/esm/policies/GleamInternalModulesConfigured.d.ts +21 -0
  70. package/esm/policies/GleamInternalModulesConfigured.d.ts.map +1 -0
  71. package/esm/policies/GleamInternalModulesConfigured.js +30 -0
  72. package/esm/policies/GleamInternalModulesConfigured.js.map +1 -0
  73. package/esm/policies/GleamLicenceConfigured.d.ts +29 -0
  74. package/esm/policies/GleamLicenceConfigured.d.ts.map +1 -0
  75. package/esm/policies/GleamLicenceConfigured.js +34 -0
  76. package/esm/policies/GleamLicenceConfigured.js.map +1 -0
  77. package/esm/policies/GleamManifestCommitted.d.ts +9 -0
  78. package/esm/policies/GleamManifestCommitted.d.ts.map +1 -0
  79. package/esm/policies/GleamManifestCommitted.js +26 -0
  80. package/esm/policies/GleamManifestCommitted.js.map +1 -0
  81. package/esm/policies/GleamMonorepoConsistency.d.ts +30 -0
  82. package/esm/policies/GleamMonorepoConsistency.d.ts.map +1 -0
  83. package/esm/policies/GleamMonorepoConsistency.js +74 -0
  84. package/esm/policies/GleamMonorepoConsistency.js.map +1 -0
  85. package/esm/policies/GleamNameRestrictions.d.ts +25 -0
  86. package/esm/policies/GleamNameRestrictions.d.ts.map +1 -0
  87. package/esm/policies/GleamNameRestrictions.js +44 -0
  88. package/esm/policies/GleamNameRestrictions.js.map +1 -0
  89. package/esm/policies/GleamNoPathDepsInPublished.d.ts +22 -0
  90. package/esm/policies/GleamNoPathDepsInPublished.d.ts.map +1 -0
  91. package/esm/policies/GleamNoPathDepsInPublished.js +43 -0
  92. package/esm/policies/GleamNoPathDepsInPublished.js.map +1 -0
  93. package/esm/policies/GleamPathDepsValid.d.ts +9 -0
  94. package/esm/policies/GleamPathDepsValid.d.ts.map +1 -0
  95. package/esm/policies/GleamPathDepsValid.js +59 -0
  96. package/esm/policies/GleamPathDepsValid.js.map +1 -0
  97. package/esm/policies/GleamProjectStructure.d.ts +25 -0
  98. package/esm/policies/GleamProjectStructure.d.ts.map +1 -0
  99. package/esm/policies/GleamProjectStructure.js +33 -0
  100. package/esm/policies/GleamProjectStructure.js.map +1 -0
  101. package/esm/policies/GleamSharedDependencyVersions.d.ts +29 -0
  102. package/esm/policies/GleamSharedDependencyVersions.d.ts.map +1 -0
  103. package/esm/policies/GleamSharedDependencyVersions.js +101 -0
  104. package/esm/policies/GleamSharedDependencyVersions.js.map +1 -0
  105. package/esm/policies/GleamTomlRequired.d.ts +30 -0
  106. package/esm/policies/GleamTomlRequired.d.ts.map +1 -0
  107. package/esm/policies/GleamTomlRequired.js +37 -0
  108. package/esm/policies/GleamTomlRequired.js.map +1 -0
  109. package/esm/policies/GleamTomlSorted.d.ts +20 -0
  110. package/esm/policies/GleamTomlSorted.d.ts.map +1 -0
  111. package/esm/policies/GleamTomlSorted.js +153 -0
  112. package/esm/policies/GleamTomlSorted.js.map +1 -0
  113. package/esm/policies/GleamVersionConstraint.d.ts +20 -0
  114. package/esm/policies/GleamVersionConstraint.d.ts.map +1 -0
  115. package/esm/policies/GleamVersionConstraint.js +64 -0
  116. package/esm/policies/GleamVersionConstraint.js.map +1 -0
  117. package/esm/policies/HtmlFileHeaders.js +1 -1
  118. package/esm/policies/HtmlFileHeaders.js.map +1 -1
  119. package/esm/policies/NoBuildDirectory.d.ts +9 -0
  120. package/esm/policies/NoBuildDirectory.d.ts.map +1 -0
  121. package/esm/policies/NoBuildDirectory.js +29 -0
  122. package/esm/policies/NoBuildDirectory.js.map +1 -0
  123. package/esm/policies/NoTargetArtifacts.d.ts +8 -0
  124. package/esm/policies/NoTargetArtifacts.d.ts.map +1 -0
  125. package/esm/policies/NoTargetArtifacts.js +42 -0
  126. package/esm/policies/NoTargetArtifacts.js.map +1 -0
  127. package/esm/policies/NoUnsafeWithoutJustification.d.ts +24 -0
  128. package/esm/policies/NoUnsafeWithoutJustification.d.ts.map +1 -0
  129. package/esm/policies/NoUnsafeWithoutJustification.js +80 -0
  130. package/esm/policies/NoUnsafeWithoutJustification.js.map +1 -0
  131. package/esm/policies/NoWildcardDependencies.d.ts +30 -0
  132. package/esm/policies/NoWildcardDependencies.d.ts.map +1 -0
  133. package/esm/policies/NoWildcardDependencies.js +83 -0
  134. package/esm/policies/NoWildcardDependencies.js.map +1 -0
  135. package/esm/policies/PackageEsmType.d.ts.map +1 -1
  136. package/esm/policies/PackageEsmType.js +7 -5
  137. package/esm/policies/PackageEsmType.js.map +1 -1
  138. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  139. package/esm/policies/PackageJsonProperties.js +12 -4
  140. package/esm/policies/PackageJsonProperties.js.map +1 -1
  141. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  142. package/esm/policies/PackageJsonRepoDirectoryProperty.js +3 -2
  143. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  144. package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
  145. package/esm/policies/PackageJsonSorted.js +42 -7
  146. package/esm/policies/PackageJsonSorted.js.map +1 -1
  147. package/esm/policies/PackageLicense.d.ts.map +1 -1
  148. package/esm/policies/PackageLicense.js +1 -2
  149. package/esm/policies/PackageLicense.js.map +1 -1
  150. package/esm/policies/PackagePrivateField.d.ts.map +1 -1
  151. package/esm/policies/PackagePrivateField.js +3 -2
  152. package/esm/policies/PackagePrivateField.js.map +1 -1
  153. package/esm/policies/PackageReadme.js +2 -2
  154. package/esm/policies/PackageReadme.js.map +1 -1
  155. package/esm/policies/PackageScripts.d.ts.map +1 -1
  156. package/esm/policies/PackageScripts.js +3 -2
  157. package/esm/policies/PackageScripts.js.map +1 -1
  158. package/esm/policies/PackageTestScripts.js +3 -3
  159. package/esm/policies/PackageTestScripts.js.map +1 -1
  160. package/esm/policies/PublicApiDocumented.d.ts +21 -0
  161. package/esm/policies/PublicApiDocumented.d.ts.map +1 -0
  162. package/esm/policies/PublicApiDocumented.js +36 -0
  163. package/esm/policies/PublicApiDocumented.js.map +1 -0
  164. package/esm/policies/RequiredScripts.d.ts.map +1 -1
  165. package/esm/policies/RequiredScripts.js +3 -2
  166. package/esm/policies/RequiredScripts.js.map +1 -1
  167. package/esm/policies/RustDocExists.d.ts +24 -0
  168. package/esm/policies/RustDocExists.d.ts.map +1 -0
  169. package/esm/policies/RustDocExists.js +49 -0
  170. package/esm/policies/RustDocExists.js.map +1 -0
  171. package/esm/policies/RustToolchainExists.d.ts +20 -0
  172. package/esm/policies/RustToolchainExists.d.ts.map +1 -0
  173. package/esm/policies/RustToolchainExists.js +27 -0
  174. package/esm/policies/RustToolchainExists.js.map +1 -0
  175. package/esm/policies/RustfmtConfigExists.d.ts +20 -0
  176. package/esm/policies/RustfmtConfigExists.d.ts.map +1 -0
  177. package/esm/policies/RustfmtConfigExists.js +27 -0
  178. package/esm/policies/RustfmtConfigExists.js.map +1 -0
  179. package/esm/policies/SharedDependencyVersions.d.ts +24 -0
  180. package/esm/policies/SharedDependencyVersions.d.ts.map +1 -0
  181. package/esm/policies/SharedDependencyVersions.js +135 -0
  182. package/esm/policies/SharedDependencyVersions.js.map +1 -0
  183. package/esm/policies/WorkspaceInheritance.d.ts +24 -0
  184. package/esm/policies/WorkspaceInheritance.d.ts.map +1 -0
  185. package/esm/policies/WorkspaceInheritance.js +94 -0
  186. package/esm/policies/WorkspaceInheritance.js.map +1 -0
  187. package/esm/policies/WorkspaceMembersValid.d.ts +20 -0
  188. package/esm/policies/WorkspaceMembersValid.d.ts.map +1 -0
  189. package/esm/policies/WorkspaceMembersValid.js +88 -0
  190. package/esm/policies/WorkspaceMembersValid.js.map +1 -0
  191. package/esm/policies/constants.d.ts +8 -0
  192. package/esm/policies/constants.d.ts.map +1 -1
  193. package/esm/policies/constants.js +8 -0
  194. package/esm/policies/constants.js.map +1 -1
  195. package/esm/policies.d.ts +30 -0
  196. package/esm/policies.d.ts.map +1 -1
  197. package/esm/policies.js +51 -1
  198. package/esm/policies.js.map +1 -1
  199. package/esm/policy.d.ts +24 -0
  200. package/esm/policy.d.ts.map +1 -1
  201. package/esm/policy.js.map +1 -1
  202. package/esm/policyDefiners/defineCargoPolicy.d.ts +66 -0
  203. package/esm/policyDefiners/defineCargoPolicy.d.ts.map +1 -0
  204. package/esm/policyDefiners/defineCargoPolicy.js +60 -0
  205. package/esm/policyDefiners/defineCargoPolicy.js.map +1 -0
  206. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
  207. package/esm/policyDefiners/defineFileHeaderPolicy.js +6 -7
  208. package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
  209. package/esm/policyDefiners/defineGleamPolicy.d.ts +65 -0
  210. package/esm/policyDefiners/defineGleamPolicy.d.ts.map +1 -0
  211. package/esm/policyDefiners/defineGleamPolicy.js +59 -0
  212. package/esm/policyDefiners/defineGleamPolicy.js.map +1 -0
  213. package/esm/policyDefiners/spdxValidator.d.ts +74 -0
  214. package/esm/policyDefiners/spdxValidator.d.ts.map +1 -0
  215. package/esm/policyDefiners/spdxValidator.js +127 -0
  216. package/esm/policyDefiners/spdxValidator.js.map +1 -0
  217. package/esm/policyDefiners/tomlParser.d.ts +8 -0
  218. package/esm/policyDefiners/tomlParser.d.ts.map +1 -0
  219. package/esm/policyDefiners/tomlParser.js +17 -0
  220. package/esm/policyDefiners/tomlParser.js.map +1 -0
  221. package/esm/runner.d.ts +60 -0
  222. package/esm/runner.d.ts.map +1 -0
  223. package/esm/runner.js +174 -0
  224. package/esm/runner.js.map +1 -0
  225. package/esm/tsdoc-metadata.json +1 -1
  226. package/oclif.manifest.json +100 -3
  227. package/package.json +46 -31
  228. package/scripts/postinstall.mjs +154 -0
  229. package/sidecar/sidecar.mjs +468 -0
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Node.js sidecar process for repopo-core (Rust).
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" }
23
+ */
24
+
25
+ import { stat } from "node:fs/promises";
26
+ import { createInterface } from "node:readline";
27
+ import { TypeScriptLoader } from "@tylerbu/lilconfig-loader-ts";
28
+ import { run } from "effection";
29
+ 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");
50
+ }
51
+
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
+ };
62
+ }
63
+
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
+ };
91
+ }
92
+
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
+ );
105
+ }
106
+
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) {
136
+ 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
+ });
199
+ }
200
+ }
201
+
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
+ });
235
+ }
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) {
245
+ 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;
253
+ }
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
+ });
282
+ }
283
+ } catch (err) {
284
+ fail.push({
285
+ file,
286
+ error: `Handler error: ${err.message}`,
287
+ fixable: false,
288
+ });
289
+ }
290
+ }
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
+ });
298
+ }
299
+ }
300
+
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
+ }
315
+
316
+ if (typeof policy.resolver !== "function") {
317
+ respond({
318
+ ok: false,
319
+ error: `Policy ${params.policyName} has no resolver`,
320
+ });
321
+ return;
322
+ }
323
+
324
+ const args = {
325
+ file: params.file,
326
+ root: params.root,
327
+ config: policy.config,
328
+ };
329
+
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
+ }
338
+ }
339
+
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) {
347
+ 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
+ }
364
+
365
+ /** @type {string[]} */
366
+ const pass = [];
367
+ /** @type {Array<{file: string, error?: string, errorMessages?: string[], fixable?: boolean, fixed?: boolean, manualFix?: string}>} */
368
+ const fail = [];
369
+
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
+ });
389
+ }
390
+ } catch (err) {
391
+ fail.push({
392
+ file,
393
+ error: `Resolver error: ${err.message}`,
394
+ fixable: false,
395
+ fixed: false,
396
+ });
397
+ }
398
+ }
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
+ });
417
+
418
+ for await (const line of rl) {
419
+ if (!line.trim()) continue;
420
+
421
+ let request;
422
+ 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);
454
+ break;
455
+
456
+ default:
457
+ respond({ ok: false, error: `Unknown method: ${method}` });
458
+ }
459
+ }
460
+
461
+ // stdin closed — exit cleanly
462
+ process.exit(0);
463
+ }
464
+
465
+ main().catch((err) => {
466
+ process.stderr.write(`Sidecar fatal error: ${err.message}\n`);
467
+ process.exit(1);
468
+ });