@hominis/fireforge 0.49.2 → 0.51.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 (400) hide show
  1. package/CHANGELOG.md +87 -1
  2. package/README.md +1 -1
  3. package/dist/bin/fireforge.js +6 -0
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/commands/build-prelock.d.ts +20 -0
  6. package/dist/src/commands/build-prelock.js +50 -0
  7. package/dist/src/commands/build.d.ts +3 -1
  8. package/dist/src/commands/build.js +13 -6
  9. package/dist/src/commands/check-report.d.ts +41 -0
  10. package/dist/src/commands/check-report.js +91 -0
  11. package/dist/src/commands/check-run.d.ts +33 -0
  12. package/dist/src/commands/check-run.js +112 -0
  13. package/dist/src/commands/check-steps.d.ts +75 -0
  14. package/dist/src/commands/check-steps.js +200 -0
  15. package/dist/src/commands/check.d.ts +32 -0
  16. package/dist/src/commands/check.js +160 -0
  17. package/dist/src/commands/config.js +5 -6
  18. package/dist/src/commands/declarations-report.d.ts +43 -0
  19. package/dist/src/commands/declarations-report.js +76 -0
  20. package/dist/src/commands/doctor-engine-eslint.js +6 -1
  21. package/dist/src/commands/doctor-source-pin.js +4 -1
  22. package/dist/src/commands/export-adopt-check.d.ts +38 -0
  23. package/dist/src/commands/export-adopt-check.js +98 -0
  24. package/dist/src/commands/export-flow.js +29 -14
  25. package/dist/src/commands/export-placement-gate.d.ts +6 -2
  26. package/dist/src/commands/export-placement-gate.js +8 -0
  27. package/dist/src/commands/export-shared.d.ts +2 -1
  28. package/dist/src/commands/export-staged-registration.d.ts +46 -0
  29. package/dist/src/commands/export-staged-registration.js +91 -0
  30. package/dist/src/commands/forward-import-remedy.d.ts +32 -0
  31. package/dist/src/commands/forward-import-remedy.js +63 -0
  32. package/dist/src/commands/furnace/apply.js +3 -3
  33. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
  34. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  35. package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
  36. package/dist/src/commands/furnace/chrome-doc.js +2 -2
  37. package/dist/src/commands/furnace/create.js +2 -2
  38. package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
  39. package/dist/src/commands/furnace/deploy-named.js +107 -0
  40. package/dist/src/commands/furnace/deploy.d.ts +3 -2
  41. package/dist/src/commands/furnace/deploy.js +50 -86
  42. package/dist/src/commands/furnace/diff.js +26 -3
  43. package/dist/src/commands/furnace/index.js +66 -41
  44. package/dist/src/commands/furnace/init.js +13 -6
  45. package/dist/src/commands/furnace/override.js +4 -2
  46. package/dist/src/commands/furnace/preview.js +4 -3
  47. package/dist/src/commands/furnace/refresh.js +2 -2
  48. package/dist/src/commands/furnace/remove.js +4 -3
  49. package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
  50. package/dist/src/commands/furnace/rename-dry-run.js +127 -0
  51. package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
  52. package/dist/src/commands/furnace/rename-helpers.js +15 -1
  53. package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
  54. package/dist/src/commands/furnace/rename-registrations.js +98 -0
  55. package/dist/src/commands/furnace/rename.d.ts +2 -1
  56. package/dist/src/commands/furnace/rename.js +28 -66
  57. package/dist/src/commands/furnace/scan.d.ts +9 -4
  58. package/dist/src/commands/furnace/scan.js +11 -9
  59. package/dist/src/commands/furnace/sync.js +3 -2
  60. package/dist/src/commands/furnace/validate-report.d.ts +45 -0
  61. package/dist/src/commands/furnace/validate-report.js +91 -0
  62. package/dist/src/commands/furnace/validate.d.ts +1 -1
  63. package/dist/src/commands/furnace/validate.js +151 -113
  64. package/dist/src/commands/furnace/validation-output.js +14 -3
  65. package/dist/src/commands/lint-per-patch.js +36 -61
  66. package/dist/src/commands/lint-per-run-checkjs.js +2 -2
  67. package/dist/src/commands/lint-report.d.ts +8 -4
  68. package/dist/src/commands/lint-report.js +20 -5
  69. package/dist/src/commands/lint.js +15 -34
  70. package/dist/src/commands/manifest.js +4 -0
  71. package/dist/src/commands/park.d.ts +22 -0
  72. package/dist/src/commands/park.js +232 -0
  73. package/dist/src/commands/patch/delete-gap-notice.js +3 -1
  74. package/dist/src/commands/patch/delete.d.ts +1 -1
  75. package/dist/src/commands/patch/delete.js +24 -104
  76. package/dist/src/commands/patch/index.d.ts +3 -3
  77. package/dist/src/commands/patch/index.js +10 -4
  78. package/dist/src/commands/patch/lint-ignore-from.d.ts +44 -0
  79. package/dist/src/commands/patch/lint-ignore-from.js +142 -0
  80. package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
  81. package/dist/src/commands/patch/lint-ignore.js +78 -31
  82. package/dist/src/commands/patch/move-files-create.js +34 -18
  83. package/dist/src/commands/patch/move-files-into.js +15 -7
  84. package/dist/src/commands/patch/move-files.js +6 -2
  85. package/dist/src/commands/patch/removal-created-files.d.ts +48 -0
  86. package/dist/src/commands/patch/removal-created-files.js +83 -0
  87. package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
  88. package/dist/src/commands/patch/removal-dependents.js +133 -0
  89. package/dist/src/commands/patch/rename.js +20 -1
  90. package/dist/src/commands/patch/reserve.d.ts +33 -0
  91. package/dist/src/commands/patch/reserve.js +152 -0
  92. package/dist/src/commands/patch/retire.d.ts +34 -0
  93. package/dist/src/commands/patch/retire.js +133 -0
  94. package/dist/src/commands/patch/split-plan.d.ts +17 -8
  95. package/dist/src/commands/patch/split-plan.js +60 -28
  96. package/dist/src/commands/patch/split-staged.d.ts +108 -0
  97. package/dist/src/commands/patch/split-staged.js +202 -0
  98. package/dist/src/commands/patch/split.d.ts +5 -0
  99. package/dist/src/commands/patch/split.js +62 -17
  100. package/dist/src/commands/patch/stage.d.ts +34 -0
  101. package/dist/src/commands/patch/stage.js +153 -0
  102. package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
  103. package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
  104. package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
  105. package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
  106. package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
  107. package/dist/src/commands/patch/staged-dependency-from.js +108 -0
  108. package/dist/src/commands/patch/staged-dependency-remove.d.ts +31 -0
  109. package/dist/src/commands/patch/staged-dependency-remove.js +80 -0
  110. package/dist/src/commands/patch/staged-dependency.js +54 -139
  111. package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
  112. package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
  113. package/dist/src/commands/re-export-adjacent.d.ts +50 -2
  114. package/dist/src/commands/re-export-adjacent.js +160 -40
  115. package/dist/src/commands/re-export-bulk-scan.d.ts +2 -1
  116. package/dist/src/commands/re-export-bulk-scan.js +22 -7
  117. package/dist/src/commands/re-export-drift.d.ts +36 -8
  118. package/dist/src/commands/re-export-drift.js +73 -49
  119. package/dist/src/commands/re-export-files.js +11 -8
  120. package/dist/src/commands/re-export-hunks.d.ts +35 -0
  121. package/dist/src/commands/re-export-hunks.js +220 -0
  122. package/dist/src/commands/re-export-lint.d.ts +2 -1
  123. package/dist/src/commands/re-export-lint.js +23 -10
  124. package/dist/src/commands/re-export-options.js +74 -6
  125. package/dist/src/commands/re-export-preflight.d.ts +23 -0
  126. package/dist/src/commands/re-export-preflight.js +160 -0
  127. package/dist/src/commands/re-export-purity-scope.d.ts +18 -0
  128. package/dist/src/commands/re-export-purity-scope.js +131 -0
  129. package/dist/src/commands/re-export-register.js +56 -17
  130. package/dist/src/commands/re-export-removed.d.ts +13 -2
  131. package/dist/src/commands/re-export-removed.js +57 -12
  132. package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
  133. package/dist/src/commands/re-export-scan-owner.js +116 -0
  134. package/dist/src/commands/re-export-scan.d.ts +19 -9
  135. package/dist/src/commands/re-export-scan.js +60 -24
  136. package/dist/src/commands/re-export-single.js +122 -46
  137. package/dist/src/commands/re-export-stale-registrations.d.ts +18 -0
  138. package/dist/src/commands/re-export-stale-registrations.js +62 -0
  139. package/dist/src/commands/re-export-summary.d.ts +28 -0
  140. package/dist/src/commands/re-export-summary.js +36 -0
  141. package/dist/src/commands/re-export.js +71 -57
  142. package/dist/src/commands/rebase/index.js +17 -17
  143. package/dist/src/commands/run.js +16 -2
  144. package/dist/src/commands/status-check.d.ts +55 -16
  145. package/dist/src/commands/status-check.js +92 -37
  146. package/dist/src/commands/status-json.d.ts +22 -9
  147. package/dist/src/commands/status-json.js +47 -32
  148. package/dist/src/commands/status-output.js +3 -1
  149. package/dist/src/commands/status-scope.d.ts +37 -0
  150. package/dist/src/commands/status-scope.js +54 -0
  151. package/dist/src/commands/status.js +66 -23
  152. package/dist/src/commands/test-browser-preflight.js +19 -3
  153. package/dist/src/commands/test-build-furnace.d.ts +22 -0
  154. package/dist/src/commands/test-build-furnace.js +54 -0
  155. package/dist/src/commands/test-build-phase.js +64 -41
  156. package/dist/src/commands/test-checkjs.js +10 -2
  157. package/dist/src/commands/test-diagnose.js +7 -0
  158. package/dist/src/commands/test-dispatch.d.ts +39 -0
  159. package/dist/src/commands/test-dispatch.js +211 -0
  160. package/dist/src/commands/test-furnace-reach.d.ts +37 -0
  161. package/dist/src/commands/test-furnace-reach.js +132 -0
  162. package/dist/src/commands/test-modes.d.ts +16 -1
  163. package/dist/src/commands/test-modes.js +24 -1
  164. package/dist/src/commands/test-multi-verdict.d.ts +61 -0
  165. package/dist/src/commands/test-multi-verdict.js +111 -0
  166. package/dist/src/commands/test-orchestrate.d.ts +52 -0
  167. package/dist/src/commands/test-orchestrate.js +240 -0
  168. package/dist/src/commands/test-perf-scope.d.ts +50 -0
  169. package/dist/src/commands/test-perf-scope.js +138 -0
  170. package/dist/src/commands/test-prelock.d.ts +51 -0
  171. package/dist/src/commands/test-prelock.js +273 -0
  172. package/dist/src/commands/test-register.js +69 -10
  173. package/dist/src/commands/test-results.d.ts +62 -0
  174. package/dist/src/commands/test-results.js +62 -0
  175. package/dist/src/commands/test-run.d.ts +13 -55
  176. package/dist/src/commands/test-run.js +74 -99
  177. package/dist/src/commands/test-shards.d.ts +110 -0
  178. package/dist/src/commands/test-shards.js +255 -0
  179. package/dist/src/commands/test-stale-gate.d.ts +5 -1
  180. package/dist/src/commands/test-stale-gate.js +60 -28
  181. package/dist/src/commands/test-verdict.d.ts +11 -1
  182. package/dist/src/commands/test-verdict.js +28 -1
  183. package/dist/src/commands/test.d.ts +6 -1
  184. package/dist/src/commands/test.js +120 -124
  185. package/dist/src/commands/token-lint.d.ts +14 -0
  186. package/dist/src/commands/token-lint.js +151 -0
  187. package/dist/src/commands/token.js +10 -0
  188. package/dist/src/commands/verify-scope.d.ts +28 -0
  189. package/dist/src/commands/verify-scope.js +67 -0
  190. package/dist/src/commands/verify.d.ts +10 -2
  191. package/dist/src/commands/verify.js +67 -36
  192. package/dist/src/core/build-baseline-types.d.ts +46 -0
  193. package/dist/src/core/build-baseline-validate.js +19 -0
  194. package/dist/src/core/build-baseline.d.ts +5 -1
  195. package/dist/src/core/build-baseline.js +3 -1
  196. package/dist/src/core/build-overwrite-guard.d.ts +50 -0
  197. package/dist/src/core/build-overwrite-guard.js +84 -4
  198. package/dist/src/core/build-prepare.d.ts +13 -0
  199. package/dist/src/core/build-prepare.js +42 -10
  200. package/dist/src/core/build-run-record.d.ts +74 -0
  201. package/dist/src/core/build-run-record.js +136 -0
  202. package/dist/src/core/checkjs-support-heads.d.ts +41 -0
  203. package/dist/src/core/checkjs-support-heads.js +104 -0
  204. package/dist/src/core/config-paths.d.ts +2 -2
  205. package/dist/src/core/config-paths.js +10 -0
  206. package/dist/src/core/config-validate-check.d.ts +8 -0
  207. package/dist/src/core/config-validate-check.js +219 -0
  208. package/dist/src/core/config-validate-file-size.d.ts +9 -6
  209. package/dist/src/core/config-validate-file-size.js +91 -2
  210. package/dist/src/core/config-validate-history.d.ts +11 -0
  211. package/dist/src/core/config-validate-history.js +38 -0
  212. package/dist/src/core/config-validate-patch-policy.js +100 -0
  213. package/dist/src/core/config-validate-test-toolchains.js +21 -0
  214. package/dist/src/core/config-validate-token-scales.d.ts +9 -0
  215. package/dist/src/core/config-validate-token-scales.js +92 -0
  216. package/dist/src/core/config-validate.js +20 -5
  217. package/dist/src/core/config.d.ts +5 -0
  218. package/dist/src/core/config.js +16 -0
  219. package/dist/src/core/coverage-dispatch.d.ts +24 -0
  220. package/dist/src/core/coverage-dispatch.js +44 -0
  221. package/dist/src/core/coverage-extend.d.ts +70 -45
  222. package/dist/src/core/coverage-extend.js +199 -144
  223. package/dist/src/core/coverage-records.d.ts +108 -0
  224. package/dist/src/core/coverage-records.js +384 -0
  225. package/dist/src/core/destructive.d.ts +7 -4
  226. package/dist/src/core/destructive.js +10 -10
  227. package/dist/src/core/engine-session-lock.d.ts +16 -1
  228. package/dist/src/core/engine-session-lock.js +38 -10
  229. package/dist/src/core/export-moved-code-shape.d.ts +49 -11
  230. package/dist/src/core/export-moved-code-shape.js +135 -44
  231. package/dist/src/core/file-lock-queue.d.ts +67 -0
  232. package/dist/src/core/file-lock-queue.js +221 -0
  233. package/dist/src/core/file-lock-shared.d.ts +57 -0
  234. package/dist/src/core/file-lock-shared.js +117 -0
  235. package/dist/src/core/file-lock.d.ts +13 -25
  236. package/dist/src/core/file-lock.js +163 -140
  237. package/dist/src/core/furnace-apply-output.d.ts +10 -0
  238. package/dist/src/core/furnace-apply-output.js +31 -1
  239. package/dist/src/core/furnace-apply.js +22 -12
  240. package/dist/src/core/furnace-composed-source.d.ts +76 -0
  241. package/dist/src/core/furnace-composed-source.js +167 -0
  242. package/dist/src/core/furnace-config-validate-exceptions.d.ts +16 -0
  243. package/dist/src/core/furnace-config-validate-exceptions.js +102 -0
  244. package/dist/src/core/furnace-config-validate.js +14 -0
  245. package/dist/src/core/furnace-css-fragments.d.ts +10 -0
  246. package/dist/src/core/furnace-css-fragments.js +26 -0
  247. package/dist/src/core/furnace-operation.d.ts +3 -3
  248. package/dist/src/core/furnace-registration-jar-inplace.d.ts +12 -0
  249. package/dist/src/core/furnace-registration-jar-inplace.js +80 -0
  250. package/dist/src/core/furnace-registration-remove.d.ts +22 -1
  251. package/dist/src/core/furnace-registration-remove.js +53 -17
  252. package/dist/src/core/furnace-registration-rename.d.ts +26 -0
  253. package/dist/src/core/furnace-registration-rename.js +77 -0
  254. package/dist/src/core/furnace-registration.d.ts +14 -4
  255. package/dist/src/core/furnace-registration.js +44 -10
  256. package/dist/src/core/furnace-staleness.d.ts +7 -0
  257. package/dist/src/core/furnace-staleness.js +34 -24
  258. package/dist/src/core/furnace-validate-accessibility.d.ts +8 -4
  259. package/dist/src/core/furnace-validate-accessibility.js +71 -28
  260. package/dist/src/core/furnace-validate-exceptions.d.ts +48 -0
  261. package/dist/src/core/furnace-validate-exceptions.js +80 -0
  262. package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
  263. package/dist/src/core/furnace-validate-icon-button.js +78 -0
  264. package/dist/src/core/furnace-validate.js +1 -1
  265. package/dist/src/core/harness-port-fallback.d.ts +60 -0
  266. package/dist/src/core/harness-port-fallback.js +96 -0
  267. package/dist/src/core/history-log-file.d.ts +61 -0
  268. package/dist/src/core/history-log-file.js +164 -0
  269. package/dist/src/core/history-log.d.ts +2 -1
  270. package/dist/src/core/history-log.js +2 -1
  271. package/dist/src/core/json-preserving-edit.d.ts +22 -0
  272. package/dist/src/core/json-preserving-edit.js +199 -0
  273. package/dist/src/core/line-prefixer.d.ts +18 -0
  274. package/dist/src/core/line-prefixer.js +34 -0
  275. package/dist/src/core/mach.d.ts +7 -0
  276. package/dist/src/core/mach.js +32 -10
  277. package/dist/src/core/module-resolution-preflight.d.ts +19 -3
  278. package/dist/src/core/module-resolution-preflight.js +24 -6
  279. package/dist/src/core/negative-control-compiled.d.ts +27 -0
  280. package/dist/src/core/negative-control-compiled.js +73 -0
  281. package/dist/src/core/negative-control-diff.d.ts +51 -0
  282. package/dist/src/core/negative-control-diff.js +179 -0
  283. package/dist/src/core/negative-control.d.ts +150 -0
  284. package/dist/src/core/negative-control.js +399 -0
  285. package/dist/src/core/objdir-install-manifests.d.ts +73 -0
  286. package/dist/src/core/objdir-install-manifests.js +226 -0
  287. package/dist/src/core/park-store.d.ts +63 -0
  288. package/dist/src/core/park-store.js +216 -0
  289. package/dist/src/core/patch-export-update.d.ts +6 -0
  290. package/dist/src/core/patch-export-update.js +13 -3
  291. package/dist/src/core/patch-export.d.ts +3 -2
  292. package/dist/src/core/patch-export.js +22 -16
  293. package/dist/src/core/patch-files.js +26 -8
  294. package/dist/src/core/patch-lint-checkjs-any-import.d.ts +39 -0
  295. package/dist/src/core/patch-lint-checkjs-any-import.js +63 -0
  296. package/dist/src/core/patch-lint-checkjs-diagnostics.d.ts +17 -0
  297. package/dist/src/core/patch-lint-checkjs-diagnostics.js +32 -0
  298. package/dist/src/core/patch-lint-checkjs.d.ts +10 -1
  299. package/dist/src/core/patch-lint-checkjs.js +20 -13
  300. package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
  301. package/dist/src/core/patch-lint-code-lines.js +105 -0
  302. package/dist/src/core/patch-lint-cross.d.ts +3 -1
  303. package/dist/src/core/patch-lint-cross.js +17 -4
  304. package/dist/src/core/patch-lint-css.d.ts +8 -2
  305. package/dist/src/core/patch-lint-css.js +24 -14
  306. package/dist/src/core/patch-lint-file-size.d.ts +12 -5
  307. package/dist/src/core/patch-lint-file-size.js +22 -11
  308. package/dist/src/core/patch-lint-forward-registration-extended.d.ts +23 -0
  309. package/dist/src/core/patch-lint-forward-registration-extended.js +36 -11
  310. package/dist/src/core/patch-lint-forward-registration.js +10 -0
  311. package/dist/src/core/patch-lint-ignore.d.ts +196 -0
  312. package/dist/src/core/patch-lint-ignore.js +429 -0
  313. package/dist/src/core/patch-lint-ownership.d.ts +10 -0
  314. package/dist/src/core/patch-lint-ownership.js +26 -0
  315. package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
  316. package/dist/src/core/patch-lint-size-thresholds.js +110 -0
  317. package/dist/src/core/patch-lint-size.d.ts +117 -0
  318. package/dist/src/core/patch-lint-size.js +331 -0
  319. package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
  320. package/dist/src/core/patch-lint-staged-registration.js +20 -0
  321. package/dist/src/core/patch-lint-token-scale.d.ts +57 -0
  322. package/dist/src/core/patch-lint-token-scale.js +286 -0
  323. package/dist/src/core/patch-lint.d.ts +11 -83
  324. package/dist/src/core/patch-lint.js +66 -288
  325. package/dist/src/core/patch-lock.js +7 -6
  326. package/dist/src/core/patch-manifest-consistency.js +7 -6
  327. package/dist/src/core/patch-manifest-io.d.ts +6 -3
  328. package/dist/src/core/patch-manifest-io.js +27 -9
  329. package/dist/src/core/patch-manifest-key-order.d.ts +24 -0
  330. package/dist/src/core/patch-manifest-key-order.js +61 -0
  331. package/dist/src/core/patch-manifest-stage.d.ts +46 -0
  332. package/dist/src/core/patch-manifest-stage.js +96 -0
  333. package/dist/src/core/patch-manifest-validate.js +61 -3
  334. package/dist/src/core/patch-order-legal.d.ts +26 -0
  335. package/dist/src/core/patch-order-legal.js +88 -0
  336. package/dist/src/core/patch-policy-content.d.ts +40 -0
  337. package/dist/src/core/patch-policy-content.js +216 -0
  338. package/dist/src/core/patch-policy.d.ts +22 -5
  339. package/dist/src/core/patch-policy.js +26 -12
  340. package/dist/src/core/patch-retired.d.ts +57 -0
  341. package/dist/src/core/patch-retired.js +92 -0
  342. package/dist/src/core/patch-subset.d.ts +16 -0
  343. package/dist/src/core/patch-subset.js +66 -0
  344. package/dist/src/core/preflight-wait.d.ts +20 -0
  345. package/dist/src/core/preflight-wait.js +38 -0
  346. package/dist/src/core/rebase-from-version.d.ts +46 -0
  347. package/dist/src/core/rebase-from-version.js +71 -0
  348. package/dist/src/core/run-log.d.ts +26 -0
  349. package/dist/src/core/run-log.js +106 -4
  350. package/dist/src/core/run-profile.d.ts +7 -0
  351. package/dist/src/core/run-profile.js +7 -1
  352. package/dist/src/core/smoke-patterns.d.ts +5 -0
  353. package/dist/src/core/smoke-patterns.js +13 -0
  354. package/dist/src/core/test-harness-crash.d.ts +19 -1
  355. package/dist/src/core/test-harness-crash.js +8 -9
  356. package/dist/src/core/test-harness-signature.d.ts +33 -0
  357. package/dist/src/core/test-harness-signature.js +23 -1
  358. package/dist/src/core/test-raw-log.d.ts +66 -0
  359. package/dist/src/core/test-raw-log.js +122 -0
  360. package/dist/src/core/test-run-completeness.d.ts +90 -0
  361. package/dist/src/core/test-run-completeness.js +184 -0
  362. package/dist/src/core/test-stale-check.d.ts +27 -1
  363. package/dist/src/core/test-stale-check.js +39 -49
  364. package/dist/src/core/test-stale-exempt.d.ts +40 -0
  365. package/dist/src/core/test-stale-exempt.js +70 -0
  366. package/dist/src/core/test-xpcshell-retry.d.ts +2 -0
  367. package/dist/src/core/test-xpcshell-retry.js +2 -1
  368. package/dist/src/core/token-lint.d.ts +53 -0
  369. package/dist/src/core/token-lint.js +131 -0
  370. package/dist/src/core/token-sibling-sheets.d.ts +46 -0
  371. package/dist/src/core/token-sibling-sheets.js +119 -0
  372. package/dist/src/core/tree-guard.js +5 -0
  373. package/dist/src/core/typecheck-shim.d.ts +6 -2
  374. package/dist/src/core/typecheck-shim.js +205 -4
  375. package/dist/src/core/typecheck.js +3 -1
  376. package/dist/src/index.d.ts +14 -3
  377. package/dist/src/index.js +6 -1
  378. package/dist/src/types/commands/index.d.ts +2 -2
  379. package/dist/src/types/commands/options.d.ts +212 -24
  380. package/dist/src/types/commands/patches.d.ts +118 -1
  381. package/dist/src/types/config.d.ts +264 -0
  382. package/dist/src/types/config.js +9 -0
  383. package/dist/src/types/furnace.d.ts +63 -1
  384. package/dist/src/types/furnace.js +7 -0
  385. package/dist/src/types/index.d.ts +1 -1
  386. package/dist/src/utils/options.d.ts +36 -7
  387. package/dist/src/utils/options.js +94 -8
  388. package/dist/src/utils/path-glob.d.ts +15 -0
  389. package/dist/src/utils/path-glob.js +47 -0
  390. package/dist/src/utils/regex.d.ts +15 -3
  391. package/dist/src/utils/regex.js +37 -16
  392. package/docs/configuration.md +369 -11
  393. package/docs/exit-codes.md +18 -1
  394. package/docs/furnace.md +139 -3
  395. package/docs/lifecycle-invariants.md +65 -30
  396. package/docs/machine-output.md +120 -12
  397. package/docs/patch-workflow.md +458 -22
  398. package/docs/run-logs.md +20 -1
  399. package/docs/testing.md +346 -21
  400. package/package.json +3 -3
@@ -20,8 +20,8 @@
20
20
  * exactly the behaviour that existed before this module.
21
21
  */
22
22
  import { createWriteStream } from 'node:fs';
23
- import { mkdir, readdir, unlink } from 'node:fs/promises';
24
- import { join } from 'node:path';
23
+ import { mkdir, readdir, stat, unlink } from 'node:fs/promises';
24
+ import { basename as basenameOf, dirname, join, resolve } from 'node:path';
25
25
  import { Writable } from 'node:stream';
26
26
  import { toError } from '../utils/errors.js';
27
27
  import { verbose } from '../utils/logger.js';
@@ -107,11 +107,27 @@ async function pruneRunLogs(dir, command) {
107
107
  */
108
108
  export async function openRunLog(projectRoot, command, now = new Date()) {
109
109
  const dir = getRunLogDir(projectRoot);
110
- let stream;
111
110
  const path = join(dir, `${command}-${fileSafeTimestamp(now)}.log`);
112
111
  try {
113
112
  await mkdir(dir, { recursive: true });
114
113
  await pruneRunLogs(dir, command);
114
+ }
115
+ catch (error) {
116
+ verbose(`Run log unavailable (${path}): ${toError(error).message}`);
117
+ return undefined;
118
+ }
119
+ return openRunLogSink(path);
120
+ }
121
+ /**
122
+ * Opens one redacting log file at `path`, creating its directory.
123
+ *
124
+ * @param path - Absolute file path
125
+ * @returns The sink, or undefined when the file could not be opened
126
+ */
127
+ async function openRunLogSink(path) {
128
+ let stream;
129
+ try {
130
+ await mkdir(dirname(path), { recursive: true });
115
131
  stream = createWriteStream(path, { flags: 'a' });
116
132
  }
117
133
  catch (error) {
@@ -172,6 +188,76 @@ export async function openRunLog(projectRoot, command, now = new Date()) {
172
188
  },
173
189
  };
174
190
  }
191
+ /** Environment variable naming where every run's log is also kept. */
192
+ const LOG_KEEP_ENV_VAR = 'FIREFORGE_LOG_KEEP';
193
+ /**
194
+ * Where a run's kept copy goes: `--keep-log <path>` when given, else
195
+ * {@link LOG_KEEP_ENV_VAR}, else nowhere. A value that ends in a path
196
+ * separator or names an existing directory receives the log under its own
197
+ * `.fireforge/logs/` basename, so a session-wide directory collects one
198
+ * file per run. Any other value is the file itself. Relative values resolve
199
+ * against the caller's working directory.
200
+ *
201
+ * @param flag - The `--keep-log` value, if any
202
+ * @param basename - The run log's own file name
203
+ * @returns The absolute destination, or undefined when none was asked for
204
+ */
205
+ async function resolveRunLogKeepPath(flag, basename) {
206
+ const raw = flag ?? process.env[LOG_KEEP_ENV_VAR]?.trim();
207
+ if (raw === undefined || raw.length === 0)
208
+ return undefined;
209
+ const target = resolve(raw);
210
+ let directory = /[\\/]$/.test(raw);
211
+ if (!directory) {
212
+ try {
213
+ directory = (await stat(target)).isDirectory();
214
+ }
215
+ catch {
216
+ directory = false;
217
+ }
218
+ }
219
+ return directory ? join(target, basename) : target;
220
+ }
221
+ /**
222
+ * Tees a run log into a caller-chosen copy (`--keep-log`,
223
+ * {@link LOG_KEEP_ENV_VAR}) that retention never prunes.
224
+ *
225
+ * `.fireforge/logs/` keeps twenty logs per command kind for EVERY session
226
+ * on the checkout, so on a shared checkout a log named by a verdict line
227
+ * can be gone within the hour, and a failure recorded only by its log path
228
+ * cannot be read later. The copy is written live, line for line with the
229
+ * same redaction, rather than copied at the end: a killed run keeps what it
230
+ * had written, like the primary log does.
231
+ *
232
+ * The primary path stays the one `log=` names. When the primary could not
233
+ * be opened the copy stands in for it.
234
+ *
235
+ * @param log - The primary run log, if one opened
236
+ * @param flag - The `--keep-log` value, if any
237
+ * @param command - Command kind, naming a copy that has no primary
238
+ * @returns A sink writing to both, or whichever one opened
239
+ */
240
+ export async function keepRunLogCopy(log, flag, command) {
241
+ const basename = log === undefined ? `${command}-${fileSafeTimestamp(new Date())}.log` : basenameOf(log.path);
242
+ const keepPath = await resolveRunLogKeepPath(flag, basename);
243
+ if (keepPath === undefined || keepPath === log?.path)
244
+ return log;
245
+ const copy = await openRunLogSink(keepPath);
246
+ if (copy === undefined)
247
+ return log;
248
+ if (log === undefined)
249
+ return copy;
250
+ return {
251
+ path: log.path,
252
+ write: (chunk) => {
253
+ log.write(chunk);
254
+ copy.write(chunk);
255
+ },
256
+ close: async () => {
257
+ await Promise.all([log.close(), copy.close()]);
258
+ },
259
+ };
260
+ }
175
261
  /**
176
262
  * The run log for the command currently executing, if any.
177
263
  *
@@ -185,9 +271,20 @@ export async function openRunLog(projectRoot, command, now = new Date()) {
185
271
  * One command runs per process, so a single slot is the whole lifetime.
186
272
  */
187
273
  let activeRunLog;
274
+ /** Newlines written to the active log so far, for line references into it. */
275
+ let activeRunLogNewlines = 0;
188
276
  /** Installs (or clears, with `undefined`) the run log for this process. */
189
277
  export function setActiveRunLog(log) {
190
278
  activeRunLog = log;
279
+ activeRunLogNewlines = 0;
280
+ }
281
+ /**
282
+ * The 1-based line of the active run log the next write starts on.
283
+ * Redaction rewrites within a line and never adds or drops one, so the
284
+ * number holds for the file on disk.
285
+ */
286
+ export function activeRunLogLine() {
287
+ return activeRunLogNewlines + 1;
191
288
  }
192
289
  /** Absolute path of the active run log, or undefined when none is open. */
193
290
  export function getActiveRunLogPath() {
@@ -195,7 +292,12 @@ export function getActiveRunLogPath() {
195
292
  }
196
293
  /** Appends to the active run log. A no-op when none is open. */
197
294
  export function writeToActiveRunLog(chunk) {
198
- activeRunLog?.write(chunk);
295
+ if (activeRunLog === undefined)
296
+ return;
297
+ for (let index = chunk.indexOf('\n'); index !== -1; index = chunk.indexOf('\n', index + 1)) {
298
+ activeRunLogNewlines += 1;
299
+ }
300
+ activeRunLog.write(chunk);
199
301
  }
200
302
  /**
201
303
  * Closes and clears the active run log. Resolves even when none is open.
@@ -17,6 +17,13 @@ export interface RunProfileRequest {
17
17
  profile?: string | undefined;
18
18
  /** `--temp-profile`: launch with a fresh directory removed afterwards. */
19
19
  tempProfile?: boolean | undefined;
20
+ /**
21
+ * Whether a named `profile` launches with `-purgecaches` (default `true`).
22
+ * A UI build does not change the build id the profile's startup cache is
23
+ * keyed on, so without it a kept profile runs the chrome scripts an
24
+ * earlier build cached. Temporary profiles start without a cache.
25
+ */
26
+ purgeCaches?: boolean | undefined;
20
27
  /**
21
28
  * Whether this is a `--smoke-exit` run. A smoke run with neither flag gets
22
29
  * a temporary profile: the step exists to prove what the build does on a
@@ -68,7 +68,13 @@ export async function resolveRunProfile(request) {
68
68
  }
69
69
  if (request.profile !== undefined) {
70
70
  const dir = resolve(request.profile);
71
- return { args: ['-profile', dir], dir, temporary: false, dispose: NO_PROFILE.dispose };
71
+ const purge = request.purgeCaches === false ? [] : ['-purgecaches'];
72
+ return {
73
+ args: ['-profile', dir, ...purge],
74
+ dir,
75
+ temporary: false,
76
+ dispose: NO_PROFILE.dispose,
77
+ };
72
78
  }
73
79
  if (request.tempProfile !== true && !request.smoke) {
74
80
  return NO_PROFILE;
@@ -17,6 +17,11 @@
17
17
  * allowlisted hits separately from raw error matches for its summary.
18
18
  */
19
19
  export declare function matchesSmokeError(line: string): boolean;
20
+ /**
21
+ * Returns `true` when `line` is console output from the browser rather than
22
+ * mach's own timestamped log. A blank line is neither.
23
+ */
24
+ export declare function isBrowserConsoleLine(line: string): boolean;
20
25
  /**
21
26
  * One compiled allowlist entry with its provenance retained so the smoke
22
27
  * summary can attribute hits per entry. An entry that silently
@@ -56,6 +56,19 @@ export function matchesSmokeError(line) {
56
56
  }
57
57
  return false;
58
58
  }
59
+ /**
60
+ * Mach's own log lines carry an elapsed-time prefix (` 0:00.42 <message>`),
61
+ * the first of which echoes the browser command line. They prove mach ran,
62
+ * not that the browser printed anything.
63
+ */
64
+ const MACH_LOG_LINE = /^\s*\d+:\d{2}\.\d{2} /;
65
+ /**
66
+ * Returns `true` when `line` is console output from the browser rather than
67
+ * mach's own timestamped log. A blank line is neither.
68
+ */
69
+ export function isBrowserConsoleLine(line) {
70
+ return line.trim() !== '' && !MACH_LOG_LINE.test(line);
71
+ }
59
72
  /** `# max-hits: N` on its own comment line, case-insensitive. */
60
73
  const MAX_HITS_DIRECTIVE = /^#\s*max-hits:\s*(\S+)\s*$/i;
61
74
  function parseMaxHitsDirective(raw, at) {
@@ -22,7 +22,7 @@
22
22
  * reporting phantom test failures (or phantom passes).
23
23
  */
24
24
  export type { HarnessCrashSignature } from './test-harness-signature.js';
25
- import type { HarnessCrashSignature } from './test-harness-signature.js';
25
+ import { type HarnessCrashSignature, type IncompleteRunEvidence } from './test-harness-signature.js';
26
26
  export { headedDisplayAsleepVerdictNote, headedNoOutputTimeoutHint } from './test-stall-triage.js';
27
27
  /** How a completed harness run should be interpreted. */
28
28
  export type HarnessRunClassification = 'tests-ran-ok' | 'test-failures' | 'harness-crash' | 'harness-arguments' | 'no-tests';
@@ -71,6 +71,12 @@ export interface HarnessRunVerdict extends HarnessSummaryCounts {
71
71
  * only. The status and reason keys are unaffected.
72
72
  */
73
73
  note?: string;
74
+ /**
75
+ * Set when a mach-exit-0 run was refused its pass because it is
76
+ * incomplete or reported unexpected results. See
77
+ * `test-run-completeness.ts`.
78
+ */
79
+ incomplete?: IncompleteRunEvidence;
74
80
  }
75
81
  /** Why a green-looking embedded summary was not trusted. */
76
82
  export interface GreenSummaryRejection {
@@ -81,6 +87,7 @@ export interface GreenSummaryRejection {
81
87
  /** Files whose `TEST_START` has no matching end marker. */
82
88
  neverEnded: string[];
83
89
  }
90
+ export declare const SHUTDOWN_REENTRY_PATTERN: RegExp;
84
91
  /**
85
92
  * Strips non-signal noise from captured output before crash-signature
86
93
  * matching: resource-monitor degradation warnings, and traceback blocks
@@ -108,6 +115,16 @@ export declare function findCrashMarkerLine(output: string): string | undefined;
108
115
  * Exported for direct unit testing.
109
116
  */
110
117
  export declare function hasCompletedGreenSummary(output: string): boolean;
118
+ /** Basename shape of a mozbuild-manifest test implementation file. */
119
+ export declare const TEST_FILE_BASENAME_PATTERN: RegExp;
120
+ /**
121
+ * Start/end execution markers, both log dialects: the structured mozlog
122
+ * `TEST_START`/`TEST_END` pair and the human browser-chrome
123
+ * `TEST-START`/`TEST-OK` pair. `TEST-UNEXPECTED-*` end shapes are
124
+ * irrelevant here, since any such line already fails the green-summary check
125
+ * before completeness is consulted.
126
+ */
127
+ export declare const START_MARKER_PATTERN: RegExp;
111
128
  /**
112
129
  * Cross-checks run completeness against the requested test files: every
113
130
  * requested file must produce a start marker, and every started test must
@@ -181,6 +198,7 @@ export declare function extractSummaryCounts(output: string): HarnessSummaryCoun
181
198
  export declare function formatFireforgeVerdictLine(verdict: HarnessRunVerdict, shards?: {
182
199
  passed: number;
183
200
  total: number;
201
+ failed?: readonly string[];
184
202
  }): string;
185
203
  /**
186
204
  * Builds the operator-facing failure message for a green-looking summary
@@ -25,6 +25,7 @@
25
25
  import { hasKnownTeardownNoise } from './mach-known-noise-filter.js';
26
26
  import { truncateEvidence, unmarkedFailureEvidenceNote, } from './test-harness-verdict-notes.js';
27
27
  import { classifyProfileArgumentFailure } from './test-profile-files.js';
28
+ import { formatIncompleteSuffix, formatShardsSuffix, } from './test-harness-signature.js';
28
29
  // The no-output-stall triage text lives in `test-stall-triage.ts` (it is
29
30
  // operator advice, not classification) and is re-exported here so the
30
31
  // command layer keeps importing its harness diagnostics from one module.
@@ -76,7 +77,7 @@ const FAIL_LINE_PATTERN = /^.*\bTEST-(?!KNOWN-FAIL\b)[A-Z-]*FAIL\b.*$/gm;
76
77
  * test failure, pointing the reader at the wrong line.
77
78
  */
78
79
  const ASSERTION_LINE_PATTERN = /^.*(?:Assertion failure:|MOZ_ASSERT\(|###!!! ASSERTION).*$/gm;
79
- const SHUTDOWN_REENTRY_PATTERN = /Application shut down \(without crashing\) in the middle of a test/i;
80
+ export const SHUTDOWN_REENTRY_PATTERN = /Application shut down \(without crashing\) in the middle of a test/i;
80
81
  const FOCUS_STALL_PATTERN = /must wait for focus/i;
81
82
  const TRACEBACK_PATTERN = /Traceback \(most recent call last\)/;
82
83
  const NO_OUTPUT_TIMEOUT_PATTERN = /timed out after \d+ seconds with no output/i;
@@ -248,7 +249,7 @@ export function hasCompletedGreenSummary(output) {
248
249
  return hasGreenShapedSummary(output) && findCrashMarkerLine(output) === undefined;
249
250
  }
250
251
  /** Basename shape of a mozbuild-manifest test implementation file. */
251
- const TEST_FILE_BASENAME_PATTERN = /^(?:browser|test)_.*\.m?js$/;
252
+ export const TEST_FILE_BASENAME_PATTERN = /^(?:browser|test)_.*\.m?js$/;
252
253
  /**
253
254
  * Start/end execution markers, both log dialects: the structured mozlog
254
255
  * `TEST_START`/`TEST_END` pair and the human browser-chrome
@@ -256,7 +257,7 @@ const TEST_FILE_BASENAME_PATTERN = /^(?:browser|test)_.*\.m?js$/;
256
257
  * irrelevant here, since any such line already fails the green-summary check
257
258
  * before completeness is consulted.
258
259
  */
259
- const START_MARKER_PATTERN = /\bTEST[-_]START\b[:| ]*\s*(\S+)?/;
260
+ export const START_MARKER_PATTERN = /\bTEST[-_]START\b[:| ]*\s*(\S+)?/;
260
261
  const END_MARKER_PATTERN = /\bTEST[-_]END\b|\bTEST-OK\b/;
261
262
  /**
262
263
  * Cross-checks run completeness against the requested test files: every
@@ -659,13 +660,11 @@ function lastCapturedCount(output, pattern) {
659
660
  export function formatFireforgeVerdictLine(verdict, shards) {
660
661
  const counts = (verdict.checks !== undefined ? ` checks=${verdict.checks}` : '') +
661
662
  (verdict.unexpected !== undefined ? ` unexpected=${verdict.unexpected}` : '');
662
- const shardSuffix = shards ? ` shards=${shards.passed}/${shards.total}` : '';
663
- const note = verdict.note !== undefined ? ` (${verdict.note})` : '';
664
- if (verdict.kind === 'tests-ran-ok') {
665
- return `FIREFORGE-VERDICT: PASS${counts}${shardSuffix}${note}`;
666
- }
663
+ const tail = `${formatShardsSuffix(shards)}${verdict.note !== undefined ? ` (${verdict.note})` : ''}`;
664
+ if (verdict.kind === 'tests-ran-ok')
665
+ return `FIREFORGE-VERDICT: PASS${counts}${tail}`;
667
666
  const reason = verdict.kind === 'harness-crash' ? 'crash' : verdict.kind;
668
- return `FIREFORGE-VERDICT: FAIL reason=${reason}${counts}${shardSuffix}${note}`;
667
+ return `FIREFORGE-VERDICT: FAIL reason=${reason}${counts}${formatIncompleteSuffix(verdict.incomplete)}${tail}`;
669
668
  }
670
669
  /**
671
670
  * Builds the operator-facing failure message for a green-looking summary
@@ -8,6 +8,10 @@
8
8
  * graph rather than the emitted code, and a shared leaf is a truer
9
9
  * description of the relationship than either direction of the cycle was:
10
10
  * neither module owns this type more than the other.
11
+ *
12
+ * {@link IncompleteRunEvidence} lives here for the same reason:
13
+ * `test-run-completeness.ts` produces it from helpers in
14
+ * `test-harness-crash.ts`, whose verdict type carries it.
11
15
  */
12
16
  /** A recognized harness-crash shape with its evidence line. */
13
17
  export interface HarnessCrashSignature {
@@ -16,3 +20,32 @@ export interface HarnessCrashSignature {
16
20
  /** The concrete output line the recognition was based on. */
17
21
  line: string;
18
22
  }
23
+ /**
24
+ * The verdict line's `started=<n>/<m> first-unstarted=<path>
25
+ * [started-source=<src>]` keys, or nothing when every file started.
26
+ */
27
+ export declare function formatIncompleteSuffix(evidence: IncompleteRunEvidence | undefined): string;
28
+ /** The verdict line's `shards=<p>/<t> [failed-shards=<labels>]` keys of a sharded run. */
29
+ export declare function formatShardsSuffix(shards: {
30
+ passed: number;
31
+ total: number;
32
+ failed?: readonly string[];
33
+ } | undefined): string;
34
+ /** Why an exit-0 run that would have read as a pass was failed instead. */
35
+ export interface IncompleteRunEvidence {
36
+ /** Requested test files that produced a start (or skip) marker. */
37
+ started: number;
38
+ /** Requested test files the run could account for (test-file-shaped paths). */
39
+ requested: number;
40
+ /** First requested file, in dispatch order, that never started. */
41
+ firstUnstarted?: string;
42
+ /**
43
+ * Which evidence the start count came from, stamped with a short count:
44
+ * the structured raw log, the streamed output, or the capped capture.
45
+ */
46
+ startedSource?: 'raw' | 'stream' | 'capture';
47
+ /** The harness's "shut down in the middle of a test" line, when present. */
48
+ shutdownLine?: string;
49
+ /** The summary's non-zero `Unexpected results:` count, when it printed one. */
50
+ unexpected?: number;
51
+ }
@@ -9,5 +9,27 @@
9
9
  * graph rather than the emitted code, and a shared leaf is a truer
10
10
  * description of the relationship than either direction of the cycle was:
11
11
  * neither module owns this type more than the other.
12
+ *
13
+ * {@link IncompleteRunEvidence} lives here for the same reason:
14
+ * `test-run-completeness.ts` produces it from helpers in
15
+ * `test-harness-crash.ts`, whose verdict type carries it.
16
+ */
17
+ /**
18
+ * The verdict line's `started=<n>/<m> first-unstarted=<path>
19
+ * [started-source=<src>]` keys, or nothing when every file started.
12
20
  */
13
- export {};
21
+ export function formatIncompleteSuffix(evidence) {
22
+ if (evidence?.firstUnstarted === undefined)
23
+ return '';
24
+ const source = evidence.startedSource === undefined ? '' : ` started-source=${evidence.startedSource}`;
25
+ return ` started=${evidence.started}/${evidence.requested} first-unstarted=${evidence.firstUnstarted}${source}`;
26
+ }
27
+ /** The verdict line's `shards=<p>/<t> [failed-shards=<labels>]` keys of a sharded run. */
28
+ export function formatShardsSuffix(shards) {
29
+ if (shards === undefined)
30
+ return '';
31
+ const failed = shards.failed !== undefined && shards.failed.length > 0
32
+ ? ` failed-shards=${shards.failed.join(',')}`
33
+ : '';
34
+ return ` shards=${shards.passed}/${shards.total}${failed}`;
35
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The mozlog structured ("raw") log of one mach test dispatch.
3
+ *
4
+ * Everything else FireForge reads from a run is human text: the
5
+ * `TEST_START`/`TEST_END` lines and the `Unexpected results:` summary, in two
6
+ * dialects, out of a capture that keeps only its last 2 MB. A gate that
7
+ * wants to name the files a run failed had to regex that text, and agent-
8
+ * quiet mozbuild drops `TEST_START` from it altogether. The raw log is the
9
+ * record the summary itself is computed from: one JSON object per line,
10
+ * with `test_start`, `test_status` and `test_end` actions naming each test
11
+ * by its manifest id.
12
+ *
13
+ * It is requested per dispatch with mozlog's `--log-raw=<path>`, which
14
+ * `mach test`, `mach mochitest` and `mach xpcshell-test` all accept, only
15
+ * when a run asks for per-file results (`--results`, `--shard-size`).
16
+ */
17
+ import type { StartMarkers } from './test-run-completeness.js';
18
+ /** One test file's outcome, from its `test_end` and failing `test_status` records. */
19
+ export interface RawTestResult {
20
+ /** The test id mozlog reports (a manifest-relative path). */
21
+ test: string;
22
+ /** `test_end` status (`OK`, `PASS`, `FAIL`, `ERROR`, `TIMEOUT`, `CRASH`, `SKIP`). */
23
+ status: string;
24
+ /** Expected status; equal to `status` when mozlog omitted it. */
25
+ expected: string;
26
+ /** True when the file ended unexpectedly or any subtest did. */
27
+ unexpected: boolean;
28
+ /** Subtests that reported an unexpected status. */
29
+ unexpectedSubtests: number;
30
+ /** Wall time between `test_start` and `test_end`, when both carried a time. */
31
+ durationMs?: number;
32
+ }
33
+ /** Everything one raw log says about the run. */
34
+ export interface RawTestLog {
35
+ /** Files in `test_end` order. A started file with no end is not listed. */
36
+ results: RawTestResult[];
37
+ /** Start markers for the completeness veto: every `test_start` id. */
38
+ markers: StartMarkers;
39
+ /** Started tests that never ended (a crash or a cut-off run). */
40
+ unfinished: string[];
41
+ }
42
+ /**
43
+ * Parses a mozlog raw log. Unknown actions and malformed lines are skipped,
44
+ * so a log truncated by a crash still yields the files that finished.
45
+ *
46
+ * @param text - The raw log's contents
47
+ */
48
+ export declare function parseRawTestLog(text: string): RawTestLog;
49
+ /**
50
+ * Reads and parses a raw log. Returns undefined when the file is missing or
51
+ * unreadable (the harness never started, or the engine's mach ignored the
52
+ * flag), so callers fall back to the streamed text.
53
+ *
54
+ * @param path - The `--log-raw` destination
55
+ */
56
+ export declare function readRawTestLog(path: string): Promise<RawTestLog | undefined>;
57
+ /**
58
+ * The `--log-raw` destination the operator already forwarded with
59
+ * `--mach-arg`, when it names a file. FireForge then reads that file instead
60
+ * of adding its own flag, so the operator's log is not replaced.
61
+ *
62
+ * @param args - The dispatch's extra mach arguments
63
+ */
64
+ export declare function forwardedRawLogPath(args: readonly string[]): string | undefined;
65
+ /** True when the dispatch arguments already carry any `--log-raw`. */
66
+ export declare function hasForwardedRawLog(args: readonly string[]): boolean;
@@ -0,0 +1,122 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * The mozlog structured ("raw") log of one mach test dispatch.
4
+ *
5
+ * Everything else FireForge reads from a run is human text: the
6
+ * `TEST_START`/`TEST_END` lines and the `Unexpected results:` summary, in two
7
+ * dialects, out of a capture that keeps only its last 2 MB. A gate that
8
+ * wants to name the files a run failed had to regex that text, and agent-
9
+ * quiet mozbuild drops `TEST_START` from it altogether. The raw log is the
10
+ * record the summary itself is computed from: one JSON object per line,
11
+ * with `test_start`, `test_status` and `test_end` actions naming each test
12
+ * by its manifest id.
13
+ *
14
+ * It is requested per dispatch with mozlog's `--log-raw=<path>`, which
15
+ * `mach test`, `mach mochitest` and `mach xpcshell-test` all accept, only
16
+ * when a run asks for per-file results (`--results`, `--shard-size`).
17
+ */
18
+ import { readFile } from 'node:fs/promises';
19
+ function parseRecord(line) {
20
+ if (!line.startsWith('{'))
21
+ return undefined;
22
+ try {
23
+ const parsed = JSON.parse(line);
24
+ return typeof parsed === 'object' && parsed !== null ? parsed : undefined;
25
+ }
26
+ catch {
27
+ // A line cut off by a killed harness is not a record.
28
+ return undefined;
29
+ }
30
+ }
31
+ /**
32
+ * Parses a mozlog raw log. Unknown actions and malformed lines are skipped,
33
+ * so a log truncated by a crash still yields the files that finished.
34
+ *
35
+ * @param text - The raw log's contents
36
+ */
37
+ export function parseRawTestLog(text) {
38
+ const results = [];
39
+ const started = [];
40
+ const startTimes = new Map();
41
+ const subtestFailures = new Map();
42
+ for (const line of text.split(/\r?\n/)) {
43
+ const record = parseRecord(line.trim());
44
+ if (record === undefined || typeof record.test !== 'string')
45
+ continue;
46
+ const test = record.test;
47
+ if (record.action === 'test_start') {
48
+ started.push(test);
49
+ if (typeof record.time === 'number')
50
+ startTimes.set(test, record.time);
51
+ continue;
52
+ }
53
+ if (record.action === 'test_status' && typeof record.expected === 'string') {
54
+ // mozlog sets `expected` on a status record only when it differs.
55
+ subtestFailures.set(test, (subtestFailures.get(test) ?? 0) + 1);
56
+ continue;
57
+ }
58
+ if (record.action !== 'test_end' || typeof record.status !== 'string')
59
+ continue;
60
+ const expected = typeof record.expected === 'string' ? record.expected : record.status;
61
+ const unexpectedSubtests = subtestFailures.get(test) ?? 0;
62
+ const start = startTimes.get(test);
63
+ results.push({
64
+ test,
65
+ status: record.status,
66
+ expected,
67
+ unexpected: expected !== record.status || unexpectedSubtests > 0,
68
+ unexpectedSubtests,
69
+ ...(start !== undefined && typeof record.time === 'number'
70
+ ? { durationMs: record.time - start }
71
+ : {}),
72
+ });
73
+ }
74
+ const ended = new Set(results.map((result) => result.test));
75
+ return {
76
+ results,
77
+ markers: {
78
+ started,
79
+ skipped: results.filter((r) => r.status === 'SKIP').map((r) => r.test),
80
+ },
81
+ unfinished: started.filter((test) => !ended.has(test)),
82
+ };
83
+ }
84
+ /**
85
+ * Reads and parses a raw log. Returns undefined when the file is missing or
86
+ * unreadable (the harness never started, or the engine's mach ignored the
87
+ * flag), so callers fall back to the streamed text.
88
+ *
89
+ * @param path - The `--log-raw` destination
90
+ */
91
+ export async function readRawTestLog(path) {
92
+ try {
93
+ return parseRawTestLog(await readFile(path, 'utf8'));
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ }
99
+ /**
100
+ * The `--log-raw` destination the operator already forwarded with
101
+ * `--mach-arg`, when it names a file. FireForge then reads that file instead
102
+ * of adding its own flag, so the operator's log is not replaced.
103
+ *
104
+ * @param args - The dispatch's extra mach arguments
105
+ */
106
+ export function forwardedRawLogPath(args) {
107
+ for (let index = 0; index < args.length; index += 1) {
108
+ const arg = args[index] ?? '';
109
+ const value = arg.startsWith('--log-raw=')
110
+ ? arg.slice('--log-raw='.length)
111
+ : arg === '--log-raw'
112
+ ? args[index + 1]
113
+ : undefined;
114
+ if (value !== undefined)
115
+ return value === '-' ? undefined : value;
116
+ }
117
+ return undefined;
118
+ }
119
+ /** True when the dispatch arguments already carry any `--log-raw`. */
120
+ export function hasForwardedRawLog(args) {
121
+ return args.some((arg) => arg === '--log-raw' || arg.startsWith('--log-raw='));
122
+ }