@hominis/fireforge 0.9.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 (316) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.md +294 -0
  3. package/README.md +435 -0
  4. package/dist/bin/fireforge.d.ts +10 -0
  5. package/dist/bin/fireforge.js +29 -0
  6. package/dist/src/cli.d.ts +33 -0
  7. package/dist/src/cli.js +180 -0
  8. package/dist/src/commands/bootstrap.d.ts +9 -0
  9. package/dist/src/commands/bootstrap.js +73 -0
  10. package/dist/src/commands/build.d.ts +11 -0
  11. package/dist/src/commands/build.js +102 -0
  12. package/dist/src/commands/config.d.ts +13 -0
  13. package/dist/src/commands/config.js +135 -0
  14. package/dist/src/commands/discard.d.ts +12 -0
  15. package/dist/src/commands/discard.js +84 -0
  16. package/dist/src/commands/doctor.d.ts +18 -0
  17. package/dist/src/commands/doctor.js +356 -0
  18. package/dist/src/commands/download.d.ts +11 -0
  19. package/dist/src/commands/download.js +127 -0
  20. package/dist/src/commands/export-all.d.ts +11 -0
  21. package/dist/src/commands/export-all.js +122 -0
  22. package/dist/src/commands/export-shared.d.ts +48 -0
  23. package/dist/src/commands/export-shared.js +208 -0
  24. package/dist/src/commands/export.d.ts +13 -0
  25. package/dist/src/commands/export.js +178 -0
  26. package/dist/src/commands/furnace/apply.d.ts +7 -0
  27. package/dist/src/commands/furnace/apply.js +80 -0
  28. package/dist/src/commands/furnace/create.d.ts +8 -0
  29. package/dist/src/commands/furnace/create.js +377 -0
  30. package/dist/src/commands/furnace/deploy.d.ts +8 -0
  31. package/dist/src/commands/furnace/deploy.js +338 -0
  32. package/dist/src/commands/furnace/diff.d.ts +7 -0
  33. package/dist/src/commands/furnace/diff.js +119 -0
  34. package/dist/src/commands/furnace/index.d.ts +16 -0
  35. package/dist/src/commands/furnace/index.js +121 -0
  36. package/dist/src/commands/furnace/list.d.ts +5 -0
  37. package/dist/src/commands/furnace/list.js +65 -0
  38. package/dist/src/commands/furnace/override.d.ts +8 -0
  39. package/dist/src/commands/furnace/override.js +188 -0
  40. package/dist/src/commands/furnace/preview.d.ts +7 -0
  41. package/dist/src/commands/furnace/preview.js +96 -0
  42. package/dist/src/commands/furnace/remove.d.ts +8 -0
  43. package/dist/src/commands/furnace/remove.js +159 -0
  44. package/dist/src/commands/furnace/scan.d.ts +5 -0
  45. package/dist/src/commands/furnace/scan.js +112 -0
  46. package/dist/src/commands/furnace/status.d.ts +7 -0
  47. package/dist/src/commands/furnace/status.js +137 -0
  48. package/dist/src/commands/furnace/validate.d.ts +6 -0
  49. package/dist/src/commands/furnace/validate.js +91 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +7 -0
  51. package/dist/src/commands/furnace/validation-output.js +22 -0
  52. package/dist/src/commands/import.d.ts +11 -0
  53. package/dist/src/commands/import.js +241 -0
  54. package/dist/src/commands/lint.d.ts +10 -0
  55. package/dist/src/commands/lint.js +118 -0
  56. package/dist/src/commands/package.d.ts +11 -0
  57. package/dist/src/commands/package.js +80 -0
  58. package/dist/src/commands/re-export.d.ts +12 -0
  59. package/dist/src/commands/re-export.js +242 -0
  60. package/dist/src/commands/rebase/abort.d.ts +7 -0
  61. package/dist/src/commands/rebase/abort.js +49 -0
  62. package/dist/src/commands/rebase/confirm.d.ts +18 -0
  63. package/dist/src/commands/rebase/confirm.js +33 -0
  64. package/dist/src/commands/rebase/continue.d.ts +7 -0
  65. package/dist/src/commands/rebase/continue.js +81 -0
  66. package/dist/src/commands/rebase/index.d.ts +22 -0
  67. package/dist/src/commands/rebase/index.js +127 -0
  68. package/dist/src/commands/rebase/patch-loop.d.ts +9 -0
  69. package/dist/src/commands/rebase/patch-loop.js +135 -0
  70. package/dist/src/commands/rebase/summary.d.ts +12 -0
  71. package/dist/src/commands/rebase/summary.js +43 -0
  72. package/dist/src/commands/rebase.d.ts +4 -0
  73. package/dist/src/commands/rebase.js +6 -0
  74. package/dist/src/commands/register.d.ts +13 -0
  75. package/dist/src/commands/register.js +67 -0
  76. package/dist/src/commands/reset.d.ts +11 -0
  77. package/dist/src/commands/reset.js +83 -0
  78. package/dist/src/commands/resolve.d.ts +9 -0
  79. package/dist/src/commands/resolve.js +124 -0
  80. package/dist/src/commands/run.d.ts +9 -0
  81. package/dist/src/commands/run.js +91 -0
  82. package/dist/src/commands/setup-support.d.ts +23 -0
  83. package/dist/src/commands/setup-support.js +310 -0
  84. package/dist/src/commands/setup.d.ts +11 -0
  85. package/dist/src/commands/setup.js +94 -0
  86. package/dist/src/commands/status.d.ts +11 -0
  87. package/dist/src/commands/status.js +268 -0
  88. package/dist/src/commands/test.d.ts +12 -0
  89. package/dist/src/commands/test.js +182 -0
  90. package/dist/src/commands/token-coverage.d.ts +5 -0
  91. package/dist/src/commands/token-coverage.js +57 -0
  92. package/dist/src/commands/token.d.ts +14 -0
  93. package/dist/src/commands/token.js +121 -0
  94. package/dist/src/commands/watch.d.ts +9 -0
  95. package/dist/src/commands/watch.js +112 -0
  96. package/dist/src/commands/wire.d.ts +13 -0
  97. package/dist/src/commands/wire.js +149 -0
  98. package/dist/src/core/ast-utils.d.ts +47 -0
  99. package/dist/src/core/ast-utils.js +57 -0
  100. package/dist/src/core/brand-validation.d.ts +7 -0
  101. package/dist/src/core/brand-validation.js +15 -0
  102. package/dist/src/core/branding.d.ts +49 -0
  103. package/dist/src/core/branding.js +229 -0
  104. package/dist/src/core/browser-wire.d.ts +40 -0
  105. package/dist/src/core/browser-wire.js +66 -0
  106. package/dist/src/core/build-prepare.d.ts +25 -0
  107. package/dist/src/core/build-prepare.js +93 -0
  108. package/dist/src/core/config-mutate.d.ts +15 -0
  109. package/dist/src/core/config-mutate.js +51 -0
  110. package/dist/src/core/config-paths.d.ts +28 -0
  111. package/dist/src/core/config-paths.js +65 -0
  112. package/dist/src/core/config-state.d.ts +28 -0
  113. package/dist/src/core/config-state.js +152 -0
  114. package/dist/src/core/config-validate.d.ts +11 -0
  115. package/dist/src/core/config-validate.js +141 -0
  116. package/dist/src/core/config.d.ts +39 -0
  117. package/dist/src/core/config.js +70 -0
  118. package/dist/src/core/file-lock.d.ts +11 -0
  119. package/dist/src/core/file-lock.js +80 -0
  120. package/dist/src/core/firefox-archive.d.ts +40 -0
  121. package/dist/src/core/firefox-archive.js +63 -0
  122. package/dist/src/core/firefox-cache.d.ts +23 -0
  123. package/dist/src/core/firefox-cache.js +134 -0
  124. package/dist/src/core/firefox-download.d.ts +21 -0
  125. package/dist/src/core/firefox-download.js +129 -0
  126. package/dist/src/core/firefox-extract.d.ts +21 -0
  127. package/dist/src/core/firefox-extract.js +53 -0
  128. package/dist/src/core/firefox.d.ts +34 -0
  129. package/dist/src/core/firefox.js +78 -0
  130. package/dist/src/core/furnace-apply-helpers.d.ts +21 -0
  131. package/dist/src/core/furnace-apply-helpers.js +244 -0
  132. package/dist/src/core/furnace-apply.d.ts +16 -0
  133. package/dist/src/core/furnace-apply.js +147 -0
  134. package/dist/src/core/furnace-config.d.ts +94 -0
  135. package/dist/src/core/furnace-config.js +372 -0
  136. package/dist/src/core/furnace-constants.d.ts +4 -0
  137. package/dist/src/core/furnace-constants.js +6 -0
  138. package/dist/src/core/furnace-registration-ast.d.ts +24 -0
  139. package/dist/src/core/furnace-registration-ast.js +218 -0
  140. package/dist/src/core/furnace-registration-remove.d.ts +14 -0
  141. package/dist/src/core/furnace-registration-remove.js +89 -0
  142. package/dist/src/core/furnace-registration-validate.d.ts +20 -0
  143. package/dist/src/core/furnace-registration-validate.js +40 -0
  144. package/dist/src/core/furnace-registration.d.ts +29 -0
  145. package/dist/src/core/furnace-registration.js +96 -0
  146. package/dist/src/core/furnace-rollback.d.ts +20 -0
  147. package/dist/src/core/furnace-rollback.js +66 -0
  148. package/dist/src/core/furnace-scanner.d.ts +40 -0
  149. package/dist/src/core/furnace-scanner.js +143 -0
  150. package/dist/src/core/furnace-stories.d.ts +37 -0
  151. package/dist/src/core/furnace-stories.js +185 -0
  152. package/dist/src/core/furnace-validate-accessibility.d.ts +6 -0
  153. package/dist/src/core/furnace-validate-accessibility.js +32 -0
  154. package/dist/src/core/furnace-validate-checks.d.ts +4 -0
  155. package/dist/src/core/furnace-validate-checks.js +7 -0
  156. package/dist/src/core/furnace-validate-compatibility.d.ts +6 -0
  157. package/dist/src/core/furnace-validate-compatibility.js +57 -0
  158. package/dist/src/core/furnace-validate-helpers.d.ts +28 -0
  159. package/dist/src/core/furnace-validate-helpers.js +129 -0
  160. package/dist/src/core/furnace-validate-registration.d.ts +37 -0
  161. package/dist/src/core/furnace-validate-registration.js +220 -0
  162. package/dist/src/core/furnace-validate-structure.d.ts +6 -0
  163. package/dist/src/core/furnace-validate-structure.js +66 -0
  164. package/dist/src/core/furnace-validate.d.ts +16 -0
  165. package/dist/src/core/furnace-validate.js +103 -0
  166. package/dist/src/core/git-base.d.ts +47 -0
  167. package/dist/src/core/git-base.js +50 -0
  168. package/dist/src/core/git-diff.d.ts +63 -0
  169. package/dist/src/core/git-diff.js +246 -0
  170. package/dist/src/core/git-file-ops.d.ts +65 -0
  171. package/dist/src/core/git-file-ops.js +141 -0
  172. package/dist/src/core/git-status.d.ts +65 -0
  173. package/dist/src/core/git-status.js +163 -0
  174. package/dist/src/core/git.d.ts +113 -0
  175. package/dist/src/core/git.js +363 -0
  176. package/dist/src/core/license-headers.d.ts +36 -0
  177. package/dist/src/core/license-headers.js +83 -0
  178. package/dist/src/core/mach-build-artifacts.d.ts +29 -0
  179. package/dist/src/core/mach-build-artifacts.js +117 -0
  180. package/dist/src/core/mach-mozconfig.d.ts +17 -0
  181. package/dist/src/core/mach-mozconfig.js +50 -0
  182. package/dist/src/core/mach-python.d.ts +16 -0
  183. package/dist/src/core/mach-python.js +126 -0
  184. package/dist/src/core/mach.d.ts +106 -0
  185. package/dist/src/core/mach.js +166 -0
  186. package/dist/src/core/manifest-helpers.d.ts +25 -0
  187. package/dist/src/core/manifest-helpers.js +96 -0
  188. package/dist/src/core/manifest-register.d.ts +30 -0
  189. package/dist/src/core/manifest-register.js +65 -0
  190. package/dist/src/core/manifest-rules.d.ts +39 -0
  191. package/dist/src/core/manifest-rules.js +151 -0
  192. package/dist/src/core/manifest-tokenizers.d.ts +34 -0
  193. package/dist/src/core/manifest-tokenizers.js +84 -0
  194. package/dist/src/core/parser-fallback.d.ts +36 -0
  195. package/dist/src/core/parser-fallback.js +43 -0
  196. package/dist/src/core/patch-apply-fuzz.d.ts +29 -0
  197. package/dist/src/core/patch-apply-fuzz.js +70 -0
  198. package/dist/src/core/patch-apply.d.ts +46 -0
  199. package/dist/src/core/patch-apply.js +235 -0
  200. package/dist/src/core/patch-export.d.ts +99 -0
  201. package/dist/src/core/patch-export.js +314 -0
  202. package/dist/src/core/patch-files.d.ts +11 -0
  203. package/dist/src/core/patch-files.js +51 -0
  204. package/dist/src/core/patch-lint.d.ts +72 -0
  205. package/dist/src/core/patch-lint.js +403 -0
  206. package/dist/src/core/patch-lock.d.ts +8 -0
  207. package/dist/src/core/patch-lock.js +29 -0
  208. package/dist/src/core/patch-manifest-consistency.d.ts +24 -0
  209. package/dist/src/core/patch-manifest-consistency.js +135 -0
  210. package/dist/src/core/patch-manifest-io.d.ts +36 -0
  211. package/dist/src/core/patch-manifest-io.js +77 -0
  212. package/dist/src/core/patch-manifest-query.d.ts +48 -0
  213. package/dist/src/core/patch-manifest-query.js +124 -0
  214. package/dist/src/core/patch-manifest-validate.d.ts +22 -0
  215. package/dist/src/core/patch-manifest-validate.js +72 -0
  216. package/dist/src/core/patch-manifest.d.ts +11 -0
  217. package/dist/src/core/patch-manifest.js +12 -0
  218. package/dist/src/core/patch-parse.d.ts +43 -0
  219. package/dist/src/core/patch-parse.js +143 -0
  220. package/dist/src/core/patch-transform.d.ts +21 -0
  221. package/dist/src/core/patch-transform.js +138 -0
  222. package/dist/src/core/rebase-session.d.ts +47 -0
  223. package/dist/src/core/rebase-session.js +65 -0
  224. package/dist/src/core/register-browser-content.d.ts +11 -0
  225. package/dist/src/core/register-browser-content.js +116 -0
  226. package/dist/src/core/register-module.d.ts +11 -0
  227. package/dist/src/core/register-module.js +76 -0
  228. package/dist/src/core/register-shared-css.d.ts +11 -0
  229. package/dist/src/core/register-shared-css.js +117 -0
  230. package/dist/src/core/register-test-manifest.d.ts +18 -0
  231. package/dist/src/core/register-test-manifest.js +99 -0
  232. package/dist/src/core/state-file.d.ts +4 -0
  233. package/dist/src/core/state-file.js +25 -0
  234. package/dist/src/core/token-coverage.d.ts +12 -0
  235. package/dist/src/core/token-coverage.js +74 -0
  236. package/dist/src/core/token-manager.d.ts +55 -0
  237. package/dist/src/core/token-manager.js +387 -0
  238. package/dist/src/core/wire-destroy.d.ts +21 -0
  239. package/dist/src/core/wire-destroy.js +103 -0
  240. package/dist/src/core/wire-dom-fragment.d.ts +23 -0
  241. package/dist/src/core/wire-dom-fragment.js +129 -0
  242. package/dist/src/core/wire-init.d.ts +23 -0
  243. package/dist/src/core/wire-init.js +201 -0
  244. package/dist/src/core/wire-subscript.d.ts +20 -0
  245. package/dist/src/core/wire-subscript.js +134 -0
  246. package/dist/src/core/wire-targets.d.ts +7 -0
  247. package/dist/src/core/wire-targets.js +9 -0
  248. package/dist/src/core/wire-utils.d.ts +88 -0
  249. package/dist/src/core/wire-utils.js +279 -0
  250. package/dist/src/errors/base.d.ts +60 -0
  251. package/dist/src/errors/base.js +87 -0
  252. package/dist/src/errors/build.d.ts +52 -0
  253. package/dist/src/errors/build.js +114 -0
  254. package/dist/src/errors/codes.d.ts +29 -0
  255. package/dist/src/errors/codes.js +30 -0
  256. package/dist/src/errors/config.d.ts +31 -0
  257. package/dist/src/errors/config.js +61 -0
  258. package/dist/src/errors/download.d.ts +42 -0
  259. package/dist/src/errors/download.js +95 -0
  260. package/dist/src/errors/furnace.d.ts +10 -0
  261. package/dist/src/errors/furnace.js +22 -0
  262. package/dist/src/errors/git.d.ts +41 -0
  263. package/dist/src/errors/git.js +99 -0
  264. package/dist/src/errors/patch.d.ts +10 -0
  265. package/dist/src/errors/patch.js +26 -0
  266. package/dist/src/errors/rebase.d.ts +20 -0
  267. package/dist/src/errors/rebase.js +30 -0
  268. package/dist/src/index.d.ts +21 -0
  269. package/dist/src/index.js +21 -0
  270. package/dist/src/types/cli.d.ts +14 -0
  271. package/dist/src/types/cli.js +2 -0
  272. package/dist/src/types/commands/index.d.ts +6 -0
  273. package/dist/src/types/commands/index.js +6 -0
  274. package/dist/src/types/commands/options.d.ts +239 -0
  275. package/dist/src/types/commands/options.js +6 -0
  276. package/dist/src/types/commands/patches.d.ts +89 -0
  277. package/dist/src/types/commands/patches.js +6 -0
  278. package/dist/src/types/commands/project.d.ts +71 -0
  279. package/dist/src/types/commands/project.js +6 -0
  280. package/dist/src/types/config.d.ts +101 -0
  281. package/dist/src/types/config.js +2 -0
  282. package/dist/src/types/furnace.d.ts +158 -0
  283. package/dist/src/types/furnace.js +2 -0
  284. package/dist/src/types/index.d.ts +6 -0
  285. package/dist/src/types/index.js +6 -0
  286. package/dist/src/utils/errors.d.ts +2 -0
  287. package/dist/src/utils/errors.js +15 -0
  288. package/dist/src/utils/fs.d.ts +72 -0
  289. package/dist/src/utils/fs.js +179 -0
  290. package/dist/src/utils/logger.d.ts +58 -0
  291. package/dist/src/utils/logger.js +120 -0
  292. package/dist/src/utils/options.d.ts +8 -0
  293. package/dist/src/utils/options.js +16 -0
  294. package/dist/src/utils/package-root.d.ts +10 -0
  295. package/dist/src/utils/package-root.js +53 -0
  296. package/dist/src/utils/parse.d.ts +110 -0
  297. package/dist/src/utils/parse.js +200 -0
  298. package/dist/src/utils/paths.d.ts +10 -0
  299. package/dist/src/utils/paths.js +43 -0
  300. package/dist/src/utils/platform.d.ts +38 -0
  301. package/dist/src/utils/platform.js +56 -0
  302. package/dist/src/utils/process.d.ts +80 -0
  303. package/dist/src/utils/process.js +188 -0
  304. package/dist/src/utils/regex.d.ts +24 -0
  305. package/dist/src/utils/regex.js +40 -0
  306. package/dist/src/utils/validation.d.ts +133 -0
  307. package/dist/src/utils/validation.js +250 -0
  308. package/package.json +106 -0
  309. package/templates/configs/common.mozconfig +24 -0
  310. package/templates/configs/darwin.mozconfig +10 -0
  311. package/templates/configs/linux.mozconfig +12 -0
  312. package/templates/configs/win32.mozconfig +14 -0
  313. package/templates/licenses/0BSD.md +14 -0
  314. package/templates/licenses/EUPL-1.2.md +294 -0
  315. package/templates/licenses/GPL-2.0-or-later.md +339 -0
  316. package/templates/licenses/MPL-2.0.md +383 -0
@@ -0,0 +1,70 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Fuzzy patch application with escalating fuzz factors.
4
+ *
5
+ * Tries an exact `git apply` first, then retries with increasing `--fuzz=N`
6
+ * values. If all fuzz levels fail, falls through to `git apply --reject`
7
+ * so the user gets `.rej` files for manual resolution.
8
+ */
9
+ import { verbose } from '../utils/logger.js';
10
+ import { exec } from '../utils/process.js';
11
+ import { ensureGit } from './git-base.js';
12
+ // ── Implementation ──
13
+ /**
14
+ * Attempts to apply a patch with escalating fuzz factors.
15
+ *
16
+ * 1. `git apply --check` at fuzz 0 … maxFuzz
17
+ * 2. `git apply --fuzz=N` at the first passing level
18
+ * 3. Fall through to `git apply --reject` if nothing succeeds
19
+ *
20
+ * @param patchPath - Absolute path to the `.patch` file
21
+ * @param engineDir - Working directory (engine/)
22
+ * @param maxFuzz - Maximum fuzz factor to try (default 3)
23
+ */
24
+ export async function applyPatchWithFuzz(patchPath, engineDir, maxFuzz = 3) {
25
+ await ensureGit();
26
+ // Try exact match first, then escalate
27
+ for (let fuzz = 0; fuzz <= maxFuzz; fuzz++) {
28
+ const fuzzArgs = fuzz > 0 ? [`--fuzz=${fuzz}`] : [];
29
+ const check = await exec('git', ['apply', '--check', ...fuzzArgs, '--', patchPath], {
30
+ cwd: engineDir,
31
+ });
32
+ if (check.exitCode === 0) {
33
+ // --check passed: apply for real
34
+ const apply = await exec('git', ['apply', ...fuzzArgs, '--', patchPath], {
35
+ cwd: engineDir,
36
+ });
37
+ if (apply.exitCode === 0) {
38
+ if (fuzz > 0) {
39
+ verbose(`Patch applied with fuzz=${fuzz}: ${patchPath}`);
40
+ }
41
+ return { success: true, fuzzFactor: fuzz };
42
+ }
43
+ // Unlikely: --check passed but apply failed; fall through to next fuzz
44
+ verbose(`git apply fuzz=${fuzz} --check passed but apply failed: ${apply.stderr.trim()}`);
45
+ }
46
+ }
47
+ // All fuzz levels failed → generate .rej files for manual resolution
48
+ const rejectResult = await exec('git', ['apply', '--reject', '--', patchPath], {
49
+ cwd: engineDir,
50
+ });
51
+ const errorMessage = rejectResult.stderr.trim() || 'All fuzz levels failed';
52
+ // Extract .rej file paths from stderr
53
+ const rejectFiles = [];
54
+ for (const line of rejectResult.stderr.split('\n')) {
55
+ const match = line.match(/Applying patch .* with (\d+) reject/);
56
+ if (match)
57
+ continue;
58
+ const rejMatch = line.match(/Rejected hunk.*to (.+\.rej)/);
59
+ if (rejMatch?.[1]) {
60
+ rejectFiles.push(rejMatch[1]);
61
+ }
62
+ }
63
+ return {
64
+ success: false,
65
+ fuzzFactor: maxFuzz,
66
+ error: errorMessage,
67
+ rejectFiles,
68
+ };
69
+ }
70
+ //# sourceMappingURL=patch-apply-fuzz.js.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Patch orchestration — coordinates patch discovery, application, and validation.
3
+ * Pure parsing, content transformation, and lock management are in separate modules.
4
+ */
5
+ import type { ImportSummary, PatchResult } from '../types/commands/index.js';
6
+ export { PatchError } from '../errors/patch.js';
7
+ export { countPatches, discoverPatches, getAllTargetFilesFromPatch, getTargetFileFromPatch, isNewFilePatch, } from './patch-files.js';
8
+ export { withPatchDirectoryLock } from './patch-lock.js';
9
+ export { extractAffectedFiles, extractOrder, isNewFileInPatch, parseHunksForFile, } from './patch-parse.js';
10
+ export { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
11
+ /**
12
+ * Applies all patches in order. Rolls back all successfully applied
13
+ * patches when one fails so the engine directory stays clean.
14
+ * @param patchesDir - Path to the patches directory
15
+ * @param engineDir - Path to the engine directory
16
+ * @returns Results for each patch
17
+ */
18
+ export declare function applyPatches(patchesDir: string, engineDir: string): Promise<PatchResult[]>;
19
+ /**
20
+ * Validates that all patches can be applied.
21
+ * @param patchesDir - Path to the patches directory
22
+ * @param engineDir - Path to the engine directory
23
+ * @returns Validation results
24
+ */
25
+ export declare function validatePatches(patchesDir: string, engineDir: string): Promise<{
26
+ valid: boolean;
27
+ errors: string[];
28
+ }>;
29
+ /**
30
+ * Enhanced patch application with continue mode.
31
+ * When continueOnFailure is false, rolls back all previously applied patches
32
+ * on the first failure to keep the engine directory in a clean state.
33
+ * @param patchesDir - Path to the patches directory
34
+ * @param engineDir - Path to the engine directory
35
+ * @param continueOnFailure - Whether to continue after failures
36
+ * @returns Import summary with all results
37
+ */
38
+ export declare function applyPatchesWithContinue(patchesDir: string, engineDir: string, continueOnFailure?: boolean): Promise<ImportSummary>;
39
+ /**
40
+ * Computes the cumulative patched content for a file.
41
+ * @param patchesDir - Path to the patches directory
42
+ * @param engineDir - Path to the engine directory
43
+ * @param filePath - File path to compute content for
44
+ * @returns Content after all patches applied, or null if file doesn't exist
45
+ */
46
+ export declare function computePatchedContent(patchesDir: string, engineDir: string, filePath: string): Promise<string | null>;
@@ -0,0 +1,235 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Patch orchestration — coordinates patch discovery, application, and validation.
4
+ * Pure parsing, content transformation, and lock management are in separate modules.
5
+ */
6
+ import { join } from 'node:path';
7
+ import { PatchError } from '../errors/patch.js';
8
+ import { toError } from '../utils/errors.js';
9
+ import { pathExists, readText, writeText } from '../utils/fs.js';
10
+ import { verbose } from '../utils/logger.js';
11
+ import { isContainedRelativePath } from '../utils/paths.js';
12
+ import { exec } from '../utils/process.js';
13
+ import { applyPatchIdempotent, reversePatch } from './git.js';
14
+ import { getFileContentFromHead } from './git-file-ops.js';
15
+ import { discoverPatches } from './patch-files.js';
16
+ import { findPatchesAffectingFile } from './patch-manifest.js';
17
+ import { extractAffectedFiles, extractConflictingFiles, isNewFileInPatch } from './patch-parse.js';
18
+ import { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
19
+ // Re-export from split modules so existing import sites continue working
20
+ export { PatchError } from '../errors/patch.js';
21
+ export { countPatches, discoverPatches, getAllTargetFilesFromPatch, getTargetFileFromPatch, isNewFilePatch, } from './patch-files.js';
22
+ export { withPatchDirectoryLock } from './patch-lock.js';
23
+ export { extractAffectedFiles, extractOrder, isNewFileInPatch, parseHunksForFile, } from './patch-parse.js';
24
+ export { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
25
+ /**
26
+ * Applies a single patch.
27
+ * @param patch - Patch info
28
+ * @param engineDir - Path to the engine directory
29
+ * @returns Patch result
30
+ */
31
+ async function applySinglePatch(patch, engineDir) {
32
+ let patchContent = '';
33
+ let affectedFiles = [];
34
+ try {
35
+ patchContent = await readText(patch.path);
36
+ affectedFiles = extractAffectedFiles(patchContent);
37
+ validatePatchTargets(patch, affectedFiles);
38
+ await applyPatchIdempotent(patch.path, engineDir);
39
+ return { patch, success: true };
40
+ }
41
+ catch (error) {
42
+ if (error instanceof PatchError) {
43
+ return { patch, success: false, error: error.message };
44
+ }
45
+ const applyError = toError(error);
46
+ // Check if this is a resolvable "new file" conflict
47
+ let resolvedNewFiles = false;
48
+ // Save original content for files we might overwrite, so we can restore on failure
49
+ const savedContents = new Map();
50
+ for (const file of affectedFiles) {
51
+ if (isNewFileInPatch(patchContent, file)) {
52
+ const targetPath = join(engineDir, file);
53
+ if (await pathExists(targetPath)) {
54
+ savedContents.set(file, await readText(targetPath));
55
+ const content = await extractNewFileContent(patch.path, file);
56
+ await writeText(targetPath, content);
57
+ resolvedNewFiles = true;
58
+ }
59
+ }
60
+ }
61
+ if (resolvedNewFiles) {
62
+ try {
63
+ await applyPatchIdempotent(patch.path, engineDir);
64
+ return { patch, success: true, autoResolved: true };
65
+ }
66
+ catch (retryError) {
67
+ verbose(`Auto-resolved new-file retry failed for ${patch.filename}: ${toError(retryError).message}`);
68
+ // Restore original file content before falling through to --reject
69
+ for (const [file, originalContent] of savedContents) {
70
+ await writeText(join(engineDir, file), originalContent);
71
+ }
72
+ }
73
+ }
74
+ // If it's not a simple new-file conflict, try with --reject to help manual resolution
75
+ let errorMessage = applyError.message;
76
+ try {
77
+ // Use --reject to apply what we can and create .rej files for what we can't
78
+ await applyPatchIdempotent(patch.path, engineDir, { reject: true });
79
+ // If this somehow succeeds with --reject but failed without, it still shouldn't
80
+ // happen because applyPatch first runs --check which would fail.
81
+ // But if it did succeed, we should still return failure because manual fix is needed
82
+ // for the rejected hunks.
83
+ }
84
+ catch (rejectError) {
85
+ // This is expected to fail, but now we have .rej files
86
+ errorMessage = toError(rejectError).message;
87
+ }
88
+ return { patch, success: false, error: errorMessage };
89
+ }
90
+ }
91
+ /**
92
+ * Reverses previously applied patches in reverse order.
93
+ * Best-effort: logs warnings for individual failures but does not throw.
94
+ */
95
+ async function rollbackPatches(results, engineDir) {
96
+ for (let i = results.length - 1; i >= 0; i--) {
97
+ const result = results[i];
98
+ if (!result?.success)
99
+ continue;
100
+ try {
101
+ await reversePatch(result.patch.path, engineDir);
102
+ verbose(`Rolled back ${result.patch.filename}`);
103
+ }
104
+ catch (rollbackError) {
105
+ verbose(`Failed to roll back ${result.patch.filename}: ${toError(rollbackError).message}`);
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * Applies all patches in order. Rolls back all successfully applied
111
+ * patches when one fails so the engine directory stays clean.
112
+ * @param patchesDir - Path to the patches directory
113
+ * @param engineDir - Path to the engine directory
114
+ * @returns Results for each patch
115
+ */
116
+ export async function applyPatches(patchesDir, engineDir) {
117
+ const patches = await discoverPatches(patchesDir);
118
+ const results = [];
119
+ for (const patch of patches) {
120
+ const result = await applySinglePatch(patch, engineDir);
121
+ results.push(result);
122
+ // Stop on first failure and roll back all previously applied patches
123
+ if (!result.success) {
124
+ const succeeded = results.filter((r) => r.success);
125
+ if (succeeded.length > 0) {
126
+ verbose(`Rolling back ${succeeded.length} previously applied patch(es)…`);
127
+ await rollbackPatches(succeeded, engineDir);
128
+ }
129
+ break;
130
+ }
131
+ }
132
+ return results;
133
+ }
134
+ /**
135
+ * Validates that all patches can be applied.
136
+ * @param patchesDir - Path to the patches directory
137
+ * @param engineDir - Path to the engine directory
138
+ * @returns Validation results
139
+ */
140
+ export async function validatePatches(patchesDir, engineDir) {
141
+ const patches = await discoverPatches(patchesDir);
142
+ const errors = [];
143
+ for (const patch of patches) {
144
+ try {
145
+ const patchContent = await readText(patch.path);
146
+ validatePatchTargets(patch, extractAffectedFiles(patchContent));
147
+ }
148
+ catch (error) {
149
+ errors.push(`${patch.filename}: ${toError(error).message}`);
150
+ continue;
151
+ }
152
+ const result = await exec('git', ['apply', '--check', '--', patch.path], { cwd: engineDir });
153
+ if (result.exitCode !== 0) {
154
+ const message = result.stderr.trim() || 'git apply --check failed';
155
+ errors.push(`${patch.filename}: ${message}`);
156
+ }
157
+ }
158
+ return { valid: errors.length === 0, errors };
159
+ }
160
+ function validatePatchTargets(patch, affectedFiles) {
161
+ for (const file of affectedFiles) {
162
+ if (!isContainedRelativePath(file)) {
163
+ throw new PatchError(`Patch targets a path outside engine/: ${file}`, patch.filename);
164
+ }
165
+ }
166
+ }
167
+ /**
168
+ * Enhanced patch application with continue mode.
169
+ * When continueOnFailure is false, rolls back all previously applied patches
170
+ * on the first failure to keep the engine directory in a clean state.
171
+ * @param patchesDir - Path to the patches directory
172
+ * @param engineDir - Path to the engine directory
173
+ * @param continueOnFailure - Whether to continue after failures
174
+ * @returns Import summary with all results
175
+ */
176
+ export async function applyPatchesWithContinue(patchesDir, engineDir, continueOnFailure = false) {
177
+ const patches = await discoverPatches(patchesDir);
178
+ const succeeded = [];
179
+ const failed = [];
180
+ const skipped = [];
181
+ for (const patch of patches) {
182
+ const result = await applySinglePatch(patch, engineDir);
183
+ if (result.success) {
184
+ succeeded.push(result);
185
+ }
186
+ else {
187
+ // Try to extract conflicting files from error message
188
+ result.conflictingFiles = extractConflictingFiles(result.error);
189
+ failed.push(result);
190
+ if (!continueOnFailure) {
191
+ // Roll back successfully applied patches to keep engine clean
192
+ if (succeeded.length > 0) {
193
+ verbose(`Rolling back ${succeeded.length} previously applied patch(es)…`);
194
+ await rollbackPatches(succeeded, engineDir);
195
+ }
196
+ // Mark remaining patches as skipped
197
+ const currentIndex = patches.indexOf(patch);
198
+ for (let i = currentIndex + 1; i < patches.length; i++) {
199
+ const remainingPatch = patches[i];
200
+ if (remainingPatch) {
201
+ skipped.push(remainingPatch);
202
+ }
203
+ }
204
+ break;
205
+ }
206
+ }
207
+ }
208
+ return {
209
+ total: patches.length,
210
+ succeeded,
211
+ failed,
212
+ skipped,
213
+ };
214
+ }
215
+ /**
216
+ * Computes the cumulative patched content for a file.
217
+ * @param patchesDir - Path to the patches directory
218
+ * @param engineDir - Path to the engine directory
219
+ * @param filePath - File path to compute content for
220
+ * @returns Content after all patches applied, or null if file doesn't exist
221
+ */
222
+ export async function computePatchedContent(patchesDir, engineDir, filePath) {
223
+ let content = await getFileContentFromHead(engineDir, filePath);
224
+ // Find all patches affecting this file
225
+ const affectingPatches = await findPatchesAffectingFile(patchesDir, filePath);
226
+ if (affectingPatches.length === 0) {
227
+ return content;
228
+ }
229
+ // Apply each patch in order
230
+ for (const { patch } of affectingPatches) {
231
+ content = await applyPatchToContent(content, patch.path, filePath);
232
+ }
233
+ return content;
234
+ }
235
+ //# sourceMappingURL=patch-apply.js.map
@@ -0,0 +1,99 @@
1
+ import type { PatchCategory, PatchInfo, PatchMetadata } from '../types/commands/index.js';
2
+ /**
3
+ * Gets the next patch number for a new patch.
4
+ * @param patchesDir - Path to the patches directory
5
+ * @returns Next patch number (e.g., "005" for 4 existing patches)
6
+ */
7
+ export declare function getNextPatchNumber(patchesDir: string): Promise<string>;
8
+ /**
9
+ * Generates the next patch filename with category.
10
+ * @param patchesDir - Path to the patches directory
11
+ * @param category - Patch category
12
+ * @param name - Human-readable name
13
+ * @returns Filename like "001-ui-sidebar.patch"
14
+ */
15
+ export declare function getNextPatchFilename(patchesDir: string, category: PatchCategory, name: string): Promise<string>;
16
+ export interface CommitExportedPatchInput {
17
+ patchesDir: string;
18
+ category: PatchCategory;
19
+ name: string;
20
+ description: string;
21
+ diff: string;
22
+ filesAffected: string[];
23
+ sourceEsrVersion: string;
24
+ }
25
+ export interface CommitExportedPatchResult {
26
+ patchFilename: string;
27
+ metadata: PatchMetadata;
28
+ superseded: PatchInfo[];
29
+ }
30
+ /**
31
+ * Commits a freshly generated patch file and manifest update under an exclusive
32
+ * patch directory lock so concurrent exports cannot allocate the same number.
33
+ */
34
+ export declare function commitExportedPatch(input: CommitExportedPatchInput): Promise<CommitExportedPatchResult>;
35
+ /**
36
+ * Parses a patch filename to extract order, category, and name.
37
+ * Supports both new format (001-category-name.patch) and legacy (001-name.patch).
38
+ */
39
+ export declare function parseFilename(filename: string): {
40
+ order: number;
41
+ category: PatchCategory | null;
42
+ name: string;
43
+ };
44
+ /**
45
+ * Finds an existing patch that contains the specified file.
46
+ * Returns the most recent (highest order) patch if multiple exist.
47
+ * @param patchesDir - Path to the patches directory
48
+ * @param filePath - File path to search for
49
+ * @returns The patch info and metadata, or null if not found
50
+ */
51
+ export declare function findExistingPatchForFile(patchesDir: string, filePath: string): Promise<{
52
+ patch: PatchInfo;
53
+ metadata: PatchMetadata;
54
+ } | null>;
55
+ /**
56
+ * Updates the content of a patch file.
57
+ * @param patchPath - Path to the patch file
58
+ * @param newContent - New patch content
59
+ */
60
+ export declare function updatePatch(patchPath: string, newContent: string): Promise<void>;
61
+ /**
62
+ * Updates metadata for a patch in the manifest.
63
+ * @param patchesDir - Path to the patches directory
64
+ * @param filename - Patch filename
65
+ * @param updates - Partial metadata updates
66
+ */
67
+ export declare function updatePatchMetadata(patchesDir: string, filename: string, updates: Partial<PatchMetadata>): Promise<void>;
68
+ /**
69
+ * Finds patches that are completely superseded by newer patches.
70
+ * A patch is superseded if all its affected files are covered by newer patches.
71
+ * @param patchesDir - Path to the patches directory
72
+ * @param newPatchFiles - Files affected by the new patch
73
+ * @param excludeFilename - Filename to exclude from results (the new patch itself)
74
+ * @returns Superseded patches
75
+ */
76
+ export declare function findSupersededPatches(patchesDir: string, newPatchFiles: string[], excludeFilename?: string): Promise<PatchInfo[]>;
77
+ /**
78
+ * Deletes a patch file and removes it from the manifest.
79
+ * @param patchesDir - Path to the patches directory
80
+ * @param filename - Patch filename to delete
81
+ */
82
+ export declare function deletePatch(patchesDir: string, filename: string): Promise<void>;
83
+ /**
84
+ * Checks whether a patch is fully covered by a new export.
85
+ * A patch is fully covered when every file it affects is present in the new export.
86
+ * @param patchFiles - Files affected by the existing patch
87
+ * @param targetFiles - Files affected by the new export
88
+ * @returns True when the existing patch is fully covered
89
+ */
90
+ export declare function isPatchFullyCovered(patchFiles: string[], targetFiles: string[]): boolean;
91
+ /**
92
+ * Finds patches whose filesAffected entries are fully covered by the specified files.
93
+ * Used for complete supersession when exporting full-file patches.
94
+ * @param patchesDir - Path to the patches directory
95
+ * @param targetFiles - Files affected by the new export
96
+ * @param excludeFilename - Filename to exclude from results (the new patch itself)
97
+ * @returns Patches that are fully covered by the new export
98
+ */
99
+ export declare function findAllPatchesForFiles(patchesDir: string, targetFiles: string[], excludeFilename?: string): Promise<PatchInfo[]>;