@hominis/fireforge 0.43.1 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +153 -0
- package/README.md +3 -3
- package/dist/bin/fireforge.d.ts +4 -9
- package/dist/bin/fireforge.js +42 -40
- package/dist/build-info.json +4 -4
- package/dist/src/cli.js +74 -8
- package/dist/src/commands/bootstrap.js +17 -20
- package/dist/src/commands/build.js +25 -32
- package/dist/src/commands/config.js +34 -41
- package/dist/src/commands/discard.js +8 -9
- package/dist/src/commands/doctor-check-core.d.ts +13 -18
- package/dist/src/commands/doctor-check-core.js +8 -35
- package/dist/src/commands/doctor-external-toolchains.js +2 -2
- package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
- package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
- package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
- package/dist/src/commands/doctor-furnace-jar.js +7 -9
- package/dist/src/commands/doctor-furnace.js +21 -24
- package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
- package/dist/src/commands/doctor-orphaned-harness.js +18 -44
- package/dist/src/commands/doctor-patch-manifest.d.ts +8 -0
- package/dist/src/commands/doctor-patch-manifest.js +155 -0
- package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
- package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
- package/dist/src/commands/doctor-working-tree.d.ts +10 -12
- package/dist/src/commands/doctor-working-tree.js +11 -18
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +64 -96
- package/dist/src/commands/download.js +32 -48
- package/dist/src/commands/export-all.js +29 -33
- package/dist/src/commands/export-flow.js +6 -6
- package/dist/src/commands/export-placement-conflicts.js +10 -9
- package/dist/src/commands/export-placement-gate.js +5 -5
- package/dist/src/commands/export-shared.d.ts +30 -33
- package/dist/src/commands/export-shared.js +45 -43
- package/dist/src/commands/export.js +21 -27
- package/dist/src/commands/furnace/apply.js +14 -23
- package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
- package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
- package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
- package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
- package/dist/src/commands/furnace/chrome-doc.js +31 -41
- package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
- package/dist/src/commands/furnace/create-browser-test.js +8 -8
- package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
- package/dist/src/commands/furnace/create-mochikit.js +8 -9
- package/dist/src/commands/furnace/create-readback.d.ts +12 -13
- package/dist/src/commands/furnace/create-readback.js +12 -13
- package/dist/src/commands/furnace/create-templates.d.ts +22 -28
- package/dist/src/commands/furnace/create-templates.js +35 -30
- package/dist/src/commands/furnace/create-xpcshell.js +2 -2
- package/dist/src/commands/furnace/create.js +33 -23
- package/dist/src/commands/furnace/deploy.js +16 -24
- package/dist/src/commands/furnace/diff.js +15 -10
- package/dist/src/commands/furnace/init.js +33 -36
- package/dist/src/commands/furnace/override.js +23 -25
- package/dist/src/commands/furnace/preview.js +47 -67
- package/dist/src/commands/furnace/refresh.js +12 -16
- package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
- package/dist/src/commands/furnace/remove-mutations.js +14 -17
- package/dist/src/commands/furnace/remove-state.js +1 -1
- package/dist/src/commands/furnace/remove.js +33 -38
- package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
- package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
- package/dist/src/commands/furnace/rename.js +59 -69
- package/dist/src/commands/furnace/scan.js +19 -33
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/sync.js +9 -16
- package/dist/src/commands/furnace/validate.js +20 -20
- package/dist/src/commands/import.js +48 -37
- package/dist/src/commands/lint-per-patch.js +15 -10
- package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
- package/dist/src/commands/lint-per-run-checkjs.js +13 -13
- package/dist/src/commands/lint-report.js +6 -7
- package/dist/src/commands/lint.d.ts +15 -16
- package/dist/src/commands/lint.js +86 -102
- package/dist/src/commands/package.js +6 -11
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/delete.js +24 -33
- package/dist/src/commands/patch/index.d.ts +5 -6
- package/dist/src/commands/patch/index.js +10 -12
- package/dist/src/commands/patch/lint-ignore.js +14 -13
- package/dist/src/commands/patch/move-files-create.d.ts +6 -6
- package/dist/src/commands/patch/move-files-create.js +8 -8
- package/dist/src/commands/patch/move-files-into.d.ts +6 -6
- package/dist/src/commands/patch/move-files-into.js +8 -8
- package/dist/src/commands/patch/patch-context.d.ts +2 -3
- package/dist/src/commands/patch/patch-context.js +2 -3
- package/dist/src/commands/patch/rename.d.ts +10 -11
- package/dist/src/commands/patch/rename.js +11 -12
- package/dist/src/commands/patch/reorder.js +8 -9
- package/dist/src/commands/patch/split-plan.js +1 -1
- package/dist/src/commands/patch/split.d.ts +8 -10
- package/dist/src/commands/patch/split.js +9 -11
- package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
- package/dist/src/commands/patch/staged-dependency.js +7 -3
- package/dist/src/commands/patch/tier.d.ts +9 -9
- package/dist/src/commands/patch/tier.js +18 -15
- package/dist/src/commands/re-export-adjacent.js +9 -8
- package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
- package/dist/src/commands/re-export-bulk-scan.js +22 -21
- package/dist/src/commands/re-export-drift.js +10 -10
- package/dist/src/commands/re-export-files.d.ts +6 -7
- package/dist/src/commands/re-export-files.js +34 -44
- package/dist/src/commands/re-export-lint.d.ts +10 -13
- package/dist/src/commands/re-export-lint.js +16 -14
- package/dist/src/commands/re-export-options.d.ts +6 -6
- package/dist/src/commands/re-export-options.js +6 -6
- package/dist/src/commands/re-export-register.d.ts +4 -10
- package/dist/src/commands/re-export-register.js +5 -11
- package/dist/src/commands/re-export-scan.js +1 -1
- package/dist/src/commands/re-export-single.d.ts +3 -3
- package/dist/src/commands/re-export-single.js +33 -25
- package/dist/src/commands/re-export.js +13 -21
- package/dist/src/commands/rebase/abort.js +3 -3
- package/dist/src/commands/rebase/confirm.js +2 -1
- package/dist/src/commands/rebase/continue.d.ts +1 -1
- package/dist/src/commands/rebase/continue.js +14 -9
- package/dist/src/commands/rebase/index.js +11 -11
- package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
- package/dist/src/commands/rebase/patch-loop.js +60 -39
- package/dist/src/commands/rebase/summary.js +1 -1
- package/dist/src/commands/register.js +5 -7
- package/dist/src/commands/reset.js +2 -1
- package/dist/src/commands/resolve.d.ts +16 -13
- package/dist/src/commands/resolve.js +30 -33
- package/dist/src/commands/run.js +33 -35
- package/dist/src/commands/setup-support.js +15 -19
- package/dist/src/commands/setup.js +13 -9
- package/dist/src/commands/status-check.d.ts +6 -6
- package/dist/src/commands/status-json.d.ts +6 -6
- package/dist/src/commands/status-json.js +5 -0
- package/dist/src/commands/status-output.d.ts +5 -5
- package/dist/src/commands/status-output.js +6 -6
- package/dist/src/commands/status-ownership.d.ts +16 -14
- package/dist/src/commands/status-ownership.js +16 -14
- package/dist/src/commands/status.js +42 -34
- package/dist/src/commands/test-build-phase.js +26 -13
- package/dist/src/commands/test-diagnose.js +44 -44
- package/dist/src/commands/test-register.js +1 -0
- package/dist/src/commands/test-run.d.ts +14 -16
- package/dist/src/commands/test-run.js +21 -13
- package/dist/src/commands/test-stale-gate.d.ts +15 -16
- package/dist/src/commands/test-stale-gate.js +15 -16
- package/dist/src/commands/test-verdict.d.ts +20 -3
- package/dist/src/commands/test-verdict.js +30 -0
- package/dist/src/commands/test.js +83 -76
- package/dist/src/commands/token-coverage.js +3 -4
- package/dist/src/commands/token.js +16 -18
- package/dist/src/commands/tree.js +47 -31
- package/dist/src/commands/typecheck.d.ts +10 -12
- package/dist/src/commands/typecheck.js +24 -27
- package/dist/src/commands/verify.js +13 -15
- package/dist/src/commands/watch.js +21 -23
- package/dist/src/commands/wire.js +52 -62
- package/dist/src/core/ast-utils.d.ts +8 -7
- package/dist/src/core/ast-utils.js +8 -7
- package/dist/src/core/branding.d.ts +9 -14
- package/dist/src/core/branding.js +7 -11
- package/dist/src/core/browser-wire.d.ts +1 -1
- package/dist/src/core/browser-wire.js +14 -19
- package/dist/src/core/build-audit-platform.js +25 -33
- package/dist/src/core/build-audit-registration.js +17 -25
- package/dist/src/core/build-audit-resolve.d.ts +15 -17
- package/dist/src/core/build-audit-resolve.js +39 -36
- package/dist/src/core/build-audit-transforms.js +18 -19
- package/dist/src/core/build-audit.js +28 -42
- package/dist/src/core/build-baseline-types.d.ts +29 -35
- package/dist/src/core/build-baseline.d.ts +5 -4
- package/dist/src/core/build-baseline.js +14 -14
- package/dist/src/core/build-overwrite-guard.d.ts +7 -14
- package/dist/src/core/build-overwrite-guard.js +17 -27
- package/dist/src/core/build-prepare.d.ts +2 -2
- package/dist/src/core/build-prepare.js +8 -9
- package/dist/src/core/config-mutate.js +20 -19
- package/dist/src/core/config-state.js +12 -4
- package/dist/src/core/config-validate.js +8 -5
- package/dist/src/core/config.d.ts +5 -8
- package/dist/src/core/config.js +7 -16
- package/dist/src/core/coverage-extend.d.ts +15 -13
- package/dist/src/core/coverage-extend.js +15 -13
- package/dist/src/core/destructive.d.ts +31 -9
- package/dist/src/core/destructive.js +29 -26
- package/dist/src/core/diff-hunks.js +10 -12
- package/dist/src/core/discard-baseline.d.ts +8 -8
- package/dist/src/core/discard-baseline.js +16 -15
- package/dist/src/core/display-state.d.ts +12 -14
- package/dist/src/core/display-state.js +12 -14
- package/dist/src/core/engine-changes.d.ts +2 -4
- package/dist/src/core/engine-changes.js +2 -4
- package/dist/src/core/engine-precondition.d.ts +28 -13
- package/dist/src/core/engine-precondition.js +25 -10
- package/dist/src/core/engine-session-lock.d.ts +47 -21
- package/dist/src/core/engine-session-lock.js +168 -33
- package/dist/src/core/export-moved-code-shape.d.ts +10 -10
- package/dist/src/core/export-moved-code-shape.js +10 -10
- package/dist/src/core/file-lock.d.ts +62 -5
- package/dist/src/core/file-lock.js +196 -59
- package/dist/src/core/firefox-cache.js +1 -3
- package/dist/src/core/firefox-extract.js +10 -14
- package/dist/src/core/firefox.d.ts +7 -5
- package/dist/src/core/firefox.js +4 -5
- package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
- package/dist/src/core/furnace-apply-dry-run.js +3 -2
- package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
- package/dist/src/core/furnace-apply-ftl.js +16 -13
- package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
- package/dist/src/core/furnace-apply-helpers.js +30 -21
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
- package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
- package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
- package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
- package/dist/src/core/furnace-apply.d.ts +5 -8
- package/dist/src/core/furnace-apply.js +19 -24
- package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
- package/dist/src/core/furnace-checksum-utils.js +16 -0
- package/dist/src/core/furnace-config.d.ts +21 -30
- package/dist/src/core/furnace-config.js +32 -54
- package/dist/src/core/furnace-constants.d.ts +8 -0
- package/dist/src/core/furnace-constants.js +10 -2
- package/dist/src/core/furnace-css-fragments.d.ts +5 -5
- package/dist/src/core/furnace-css-fragments.js +5 -5
- package/dist/src/core/furnace-jsconfig.d.ts +11 -12
- package/dist/src/core/furnace-jsconfig.js +11 -12
- package/dist/src/core/furnace-marker.d.ts +7 -8
- package/dist/src/core/furnace-marker.js +7 -8
- package/dist/src/core/furnace-operation.d.ts +31 -0
- package/dist/src/core/furnace-operation.js +54 -17
- package/dist/src/core/furnace-precondition.d.ts +61 -0
- package/dist/src/core/furnace-precondition.js +67 -0
- package/dist/src/core/furnace-registration-remove.d.ts +5 -6
- package/dist/src/core/furnace-registration-remove.js +6 -7
- package/dist/src/core/furnace-registration-validate.d.ts +13 -0
- package/dist/src/core/furnace-registration-validate.js +20 -1
- package/dist/src/core/furnace-registration.d.ts +5 -6
- package/dist/src/core/furnace-registration.js +10 -12
- package/dist/src/core/furnace-rollback.js +7 -7
- package/dist/src/core/furnace-scanner.d.ts +1 -0
- package/dist/src/core/furnace-scanner.js +1 -2
- package/dist/src/core/furnace-stale-export.d.ts +10 -10
- package/dist/src/core/furnace-stale-export.js +10 -10
- package/dist/src/core/furnace-state-persist.d.ts +6 -8
- package/dist/src/core/furnace-state-persist.js +6 -8
- package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
- package/dist/src/core/furnace-validate-accessibility.js +310 -1
- package/dist/src/core/furnace-validate-compatibility.js +141 -1
- package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
- package/dist/src/core/furnace-validate-helpers.js +0 -491
- package/dist/src/core/furnace-validate-registration.d.ts +7 -0
- package/dist/src/core/furnace-validate-registration.js +115 -41
- package/dist/src/core/furnace-validate.d.ts +16 -0
- package/dist/src/core/furnace-validate.js +188 -145
- package/dist/src/core/furnace-version-drift.d.ts +10 -11
- package/dist/src/core/furnace-version-drift.js +10 -11
- package/dist/src/core/git-base.d.ts +21 -9
- package/dist/src/core/git-base.js +24 -9
- package/dist/src/core/git-diff.d.ts +20 -9
- package/dist/src/core/git-diff.js +148 -63
- package/dist/src/core/git-file-ops.d.ts +19 -19
- package/dist/src/core/git-file-ops.js +19 -19
- package/dist/src/core/git-readonly-index.d.ts +53 -19
- package/dist/src/core/git-readonly-index.js +75 -31
- package/dist/src/core/git-status.d.ts +5 -6
- package/dist/src/core/git-status.js +5 -6
- package/dist/src/core/git.d.ts +9 -10
- package/dist/src/core/git.js +57 -71
- package/dist/src/core/license-headers.d.ts +17 -23
- package/dist/src/core/license-headers.js +17 -23
- package/dist/src/core/mach-build-artifacts.d.ts +9 -9
- package/dist/src/core/mach-build-artifacts.js +57 -19
- package/dist/src/core/mach-error-hints.js +42 -29
- package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
- package/dist/src/core/mach-known-noise-filter.js +18 -20
- package/dist/src/core/mach-mozconfig.d.ts +14 -0
- package/dist/src/core/mach-mozconfig.js +30 -0
- package/dist/src/core/mach-resource-shim.d.ts +43 -58
- package/dist/src/core/mach-resource-shim.js +50 -67
- package/dist/src/core/mach.d.ts +37 -41
- package/dist/src/core/mach.js +50 -51
- package/dist/src/core/marionette-port.d.ts +36 -1
- package/dist/src/core/marionette-port.js +96 -31
- package/dist/src/core/marionette-preflight.d.ts +8 -12
- package/dist/src/core/marionette-preflight.js +86 -52
- package/dist/src/core/mochitest-server-port.d.ts +42 -0
- package/dist/src/core/mochitest-server-port.js +119 -0
- package/dist/src/core/module-resolution-preflight.d.ts +9 -11
- package/dist/src/core/module-resolution-preflight.js +9 -11
- package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
- package/dist/src/core/moz-manifest-helpers.js +163 -0
- package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
- package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
- package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
- package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
- package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
- package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
- package/dist/src/core/ownership-table.d.ts +14 -16
- package/dist/src/core/ownership-table.js +14 -16
- package/dist/src/core/parser-fallback.d.ts +15 -1
- package/dist/src/core/parser-fallback.js +22 -3
- package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
- package/dist/src/core/patch-apply-fuzz.js +15 -18
- package/dist/src/core/patch-apply.d.ts +13 -34
- package/dist/src/core/patch-apply.js +16 -76
- package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
- package/dist/src/core/patch-artifact-normalize.js +13 -15
- package/dist/src/core/patch-export-metadata.d.ts +12 -2
- package/dist/src/core/patch-export-metadata.js +13 -4
- package/dist/src/core/patch-export-update.d.ts +13 -1
- package/dist/src/core/patch-export-update.js +25 -8
- package/dist/src/core/patch-export.d.ts +15 -8
- package/dist/src/core/patch-export.js +10 -11
- package/dist/src/core/patch-files.d.ts +5 -6
- package/dist/src/core/patch-files.js +5 -6
- package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
- package/dist/src/core/patch-identifier-suggest.js +14 -19
- package/dist/src/core/patch-lint-binary.d.ts +51 -0
- package/dist/src/core/patch-lint-binary.js +39 -0
- package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
- package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
- package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
- package/dist/src/core/patch-lint-checkjs.js +41 -20
- package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
- package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
- package/dist/src/core/patch-lint-creators.d.ts +8 -8
- package/dist/src/core/patch-lint-creators.js +8 -8
- package/dist/src/core/patch-lint-cross.d.ts +17 -20
- package/dist/src/core/patch-lint-cross.js +16 -16
- package/dist/src/core/patch-lint-css.d.ts +3 -5
- package/dist/src/core/patch-lint-css.js +17 -22
- package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
- package/dist/src/core/patch-lint-diff-tag.js +14 -16
- package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
- package/dist/src/core/patch-lint-jsdoc.js +26 -19
- package/dist/src/core/patch-lint-mozbuild.js +20 -20
- package/dist/src/core/patch-lint-observer.d.ts +7 -7
- package/dist/src/core/patch-lint-observer.js +7 -7
- package/dist/src/core/patch-lint.d.ts +38 -29
- package/dist/src/core/patch-lint.js +89 -121
- package/dist/src/core/patch-lock.d.ts +1 -1
- package/dist/src/core/patch-lock.js +45 -9
- package/dist/src/core/patch-manifest-consistency.d.ts +57 -6
- package/dist/src/core/patch-manifest-consistency.js +111 -40
- package/dist/src/core/patch-manifest-files-affected.d.ts +43 -0
- package/dist/src/core/patch-manifest-files-affected.js +77 -0
- package/dist/src/core/patch-manifest-io.js +14 -18
- package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
- package/dist/src/core/patch-manifest-resolve.js +9 -12
- package/dist/src/core/patch-manifest-validate.d.ts +6 -1
- package/dist/src/core/patch-manifest-validate.js +25 -13
- package/dist/src/core/patch-manifest.d.ts +2 -1
- package/dist/src/core/patch-manifest.js +2 -1
- package/dist/src/core/patch-parse.d.ts +24 -13
- package/dist/src/core/patch-parse.js +19 -14
- package/dist/src/core/patch-policy.d.ts +2 -2
- package/dist/src/core/patch-policy.js +2 -2
- package/dist/src/core/patch-registration-refs.d.ts +7 -8
- package/dist/src/core/patch-registration-refs.js +9 -9
- package/dist/src/core/process-cpu-time.d.ts +12 -0
- package/dist/src/core/process-cpu-time.js +52 -0
- package/dist/src/core/rebase-session.d.ts +38 -26
- package/dist/src/core/rebase-session.js +55 -28
- package/dist/src/core/register-browser-content.js +9 -74
- package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
- package/dist/src/core/register-module.js +10 -43
- package/dist/src/core/register-scaffold.d.ts +8 -8
- package/dist/src/core/register-scaffold.js +10 -10
- package/dist/src/core/register-shared-css.d.ts +9 -12
- package/dist/src/core/register-shared-css.js +17 -87
- package/dist/src/core/register-xpcshell-test.d.ts +6 -7
- package/dist/src/core/register-xpcshell-test.js +7 -8
- package/dist/src/core/shared-ftl.js +12 -4
- package/dist/src/core/state-file.d.ts +45 -0
- package/dist/src/core/state-file.js +34 -0
- package/dist/src/core/status-binary.d.ts +18 -11
- package/dist/src/core/status-binary.js +19 -12
- package/dist/src/core/status-classify.d.ts +30 -36
- package/dist/src/core/status-classify.js +21 -26
- package/dist/src/core/test-changed-prefs.d.ts +30 -0
- package/dist/src/core/test-changed-prefs.js +78 -0
- package/dist/src/core/test-harness-crash.d.ts +25 -63
- package/dist/src/core/test-harness-crash.js +121 -142
- package/dist/src/core/test-harness-signature.d.ts +18 -0
- package/dist/src/core/test-harness-signature.js +13 -0
- package/dist/src/core/test-path-scope.d.ts +12 -15
- package/dist/src/core/test-path-scope.js +15 -18
- package/dist/src/core/test-stale-check.d.ts +17 -17
- package/dist/src/core/test-stale-check.js +50 -43
- package/dist/src/core/test-stall-triage.d.ts +51 -0
- package/dist/src/core/test-stall-triage.js +126 -0
- package/dist/src/core/token-category.js +66 -14
- package/dist/src/core/token-coverage.js +9 -9
- package/dist/src/core/token-dark-mode.d.ts +9 -11
- package/dist/src/core/token-dark-mode.js +29 -13
- package/dist/src/core/token-docs.d.ts +2 -0
- package/dist/src/core/token-docs.js +3 -1
- package/dist/src/core/token-manager.d.ts +10 -2
- package/dist/src/core/token-manager.js +17 -8
- package/dist/src/core/token-scaffold.d.ts +7 -7
- package/dist/src/core/token-scaffold.js +7 -7
- package/dist/src/core/token-variant.d.ts +4 -4
- package/dist/src/core/token-variant.js +28 -6
- package/dist/src/core/toolchain-preflight.d.ts +20 -21
- package/dist/src/core/toolchain-preflight.js +28 -31
- package/dist/src/core/tree-guard.js +12 -2
- package/dist/src/core/tree-store.d.ts +41 -27
- package/dist/src/core/tree-store.js +68 -43
- package/dist/src/core/typecheck-shim.d.ts +23 -24
- package/dist/src/core/typecheck-shim.js +23 -24
- package/dist/src/core/typecheck.js +15 -9
- package/dist/src/core/wire-destroy.d.ts +3 -3
- package/dist/src/core/wire-destroy.js +7 -15
- package/dist/src/core/wire-dom-fragment.d.ts +9 -17
- package/dist/src/core/wire-dom-fragment.js +11 -44
- package/dist/src/core/wire-init.d.ts +2 -3
- package/dist/src/core/wire-init.js +8 -19
- package/dist/src/core/wire-subscript.d.ts +2 -3
- package/dist/src/core/wire-subscript.js +4 -13
- package/dist/src/core/wire-utils.d.ts +7 -8
- package/dist/src/core/wire-utils.js +7 -8
- package/dist/src/core/xpcshell-appdir.d.ts +38 -45
- package/dist/src/core/xpcshell-appdir.js +38 -45
- package/dist/src/errors/base.d.ts +37 -16
- package/dist/src/errors/base.js +37 -16
- package/dist/src/errors/build.d.ts +13 -1
- package/dist/src/errors/build.js +14 -2
- package/dist/src/errors/codes.d.ts +16 -0
- package/dist/src/errors/codes.js +16 -0
- package/dist/src/errors/furnace.d.ts +4 -4
- package/dist/src/errors/git.d.ts +6 -10
- package/dist/src/errors/git.js +6 -10
- package/dist/src/errors/patch.d.ts +7 -7
- package/dist/src/errors/patch.js +7 -7
- package/dist/src/errors/rebase.d.ts +4 -4
- package/dist/src/errors/rebase.js +4 -4
- package/dist/src/index.d.ts +7 -1
- package/dist/src/index.js +5 -0
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +111 -70
- package/dist/src/types/commands/patches.d.ts +24 -34
- package/dist/src/types/commands/project.d.ts +5 -34
- package/dist/src/types/config.d.ts +25 -23
- package/dist/src/types/config.js +6 -1
- package/dist/src/types/furnace.d.ts +37 -13
- package/dist/src/types/furnace.js +19 -1
- package/dist/src/types/index.d.ts +3 -3
- package/dist/src/utils/build-info.d.ts +7 -8
- package/dist/src/utils/build-info.js +12 -11
- package/dist/src/utils/errors.d.ts +6 -8
- package/dist/src/utils/errors.js +6 -8
- package/dist/src/utils/logger.d.ts +27 -10
- package/dist/src/utils/logger.js +28 -16
- package/dist/src/utils/machine-output.d.ts +12 -0
- package/dist/src/utils/machine-output.js +33 -0
- package/dist/src/utils/options.d.ts +31 -14
- package/dist/src/utils/options.js +86 -30
- package/dist/src/utils/paths.d.ts +10 -8
- package/dist/src/utils/paths.js +10 -8
- package/dist/src/utils/platform.d.ts +0 -25
- package/dist/src/utils/platform.js +1 -42
- package/dist/src/utils/process-group.d.ts +9 -4
- package/dist/src/utils/process-group.js +19 -5
- package/dist/src/utils/process.d.ts +42 -7
- package/dist/src/utils/process.js +44 -17
- package/dist/src/utils/ps-duration.d.ts +31 -0
- package/dist/src/utils/ps-duration.js +70 -0
- package/dist/src/utils/regex.d.ts +9 -10
- package/dist/src/utils/regex.js +9 -10
- package/dist/src/utils/validation.d.ts +58 -28
- package/dist/src/utils/validation.js +60 -33
- package/package.json +3 -3
- package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
- package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
- package/dist/src/core/manifest-helpers.d.ts +0 -38
- package/dist/src/core/manifest-helpers.js +0 -120
- /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
- /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
- /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Manifest consistency checks and rebuild/recovery operations.
|
|
4
4
|
*/
|
|
5
5
|
import { stat } from 'node:fs/promises';
|
|
6
|
+
import { GeneralError } from '../errors/base.js';
|
|
6
7
|
import { discoverPatches, getAllTargetFilesFromPatch } from './patch-files.js';
|
|
7
8
|
import { withPatchDirectoryLock } from './patch-lock.js';
|
|
8
9
|
import { loadPatchesManifestState, PATCHES_MANIFEST, savePatchesManifest, } from './patch-manifest-io.js';
|
|
@@ -59,8 +60,8 @@ export async function validatePatchesManifestConsistency(patchesDir) {
|
|
|
59
60
|
});
|
|
60
61
|
continue;
|
|
61
62
|
}
|
|
62
|
-
const declaredFiles =
|
|
63
|
-
const actualFiles =
|
|
63
|
+
const declaredFiles = normalizeAffectedFiles(metadata.filesAffected);
|
|
64
|
+
const actualFiles = normalizeAffectedFiles(await getAllTargetFilesFromPatch(patch.path));
|
|
64
65
|
if (!sameStringArray(declaredFiles, actualFiles)) {
|
|
65
66
|
issues.push({
|
|
66
67
|
code: 'files-affected-mismatch',
|
|
@@ -80,26 +81,78 @@ export async function validatePatchesManifestConsistency(patchesDir) {
|
|
|
80
81
|
}
|
|
81
82
|
return issues;
|
|
82
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Recovery hint for an unrepaired consistency failure.
|
|
86
|
+
*
|
|
87
|
+
* Drift that is only in `filesAffected` is a derived value disagreeing with
|
|
88
|
+
* the diff it describes, and the narrow repair fixes exactly that. Naming the
|
|
89
|
+
* whole-manifest rebuild for it — as this hint used to, unconditionally — puts
|
|
90
|
+
* an operator one keystroke from rewriting every row in the manifest to
|
|
91
|
+
* correct one list.
|
|
92
|
+
*/
|
|
93
|
+
export function recommendManifestRepair(issues) {
|
|
94
|
+
const filesAffectedOnly = issues.length > 0 && issues.every((issue) => issue.code === 'files-affected-mismatch');
|
|
95
|
+
return filesAffectedOnly
|
|
96
|
+
? 'Run "fireforge doctor --repair-files-affected" to recompute filesAffected from the patch ' +
|
|
97
|
+
'bodies (add --dry-run to preview). It rewrites only the drifted lists and leaves every ' +
|
|
98
|
+
'other manifest field untouched.'
|
|
99
|
+
: 'Run "fireforge doctor --repair-patches-manifest" to rebuild patches.json from the patch ' +
|
|
100
|
+
'files (add --dry-run to preview). Only filesAffected and order are recomputed; every ' +
|
|
101
|
+
'other field on an existing entry is preserved.';
|
|
102
|
+
}
|
|
83
103
|
/**
|
|
84
104
|
* Rebuilds patches.json from the patch files currently present on disk.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
105
|
+
*
|
|
106
|
+
* This is a MERGE, not a from-scratch rebuild: only `filesAffected` and
|
|
107
|
+
* `order` are recomputed from the patch files, and every other field on an
|
|
108
|
+
* existing entry is carried forward untouched. Entries with no pre-existing
|
|
109
|
+
* metadata are recovered from filename structure, patch contents, and file
|
|
110
|
+
* mtimes.
|
|
111
|
+
*
|
|
112
|
+
* Refuses when patches.json exists but cannot be parsed, because that is the
|
|
113
|
+
* one case where nothing can be carried forward and the "merge" silently
|
|
114
|
+
* becomes a whole-manifest reinvention. Pass `allowMetadataLoss` to accept it.
|
|
115
|
+
*
|
|
87
116
|
* @param patchesDir - Path to the patches directory
|
|
88
117
|
* @param fallbackSourceEsrVersion - source version to use for recovered legacy entries
|
|
89
|
-
* @
|
|
90
|
-
*
|
|
118
|
+
* @param options - Lock options plus `allowMetadataLoss` / `dryRun`
|
|
119
|
+
* @returns {@link RebuildPatchesManifestResult} — the rebuilt manifest, the
|
|
120
|
+
* filenames that were reconstructed from generic defaults, and the rows
|
|
121
|
+
* dropped because their patch file is gone.
|
|
91
122
|
*/
|
|
92
|
-
export async function rebuildPatchesManifest(patchesDir, fallbackSourceEsrVersion) {
|
|
123
|
+
export async function rebuildPatchesManifest(patchesDir, fallbackSourceEsrVersion, options = {}) {
|
|
93
124
|
// The whole read → discover → rebuild → save cycle runs under the shared
|
|
94
125
|
// patch-directory lock (invariant 2, docs/lifecycle-invariants.md):
|
|
95
|
-
// manifest writes serialize on this lock,
|
|
96
|
-
// concurrent export/reorder
|
|
97
|
-
//
|
|
98
|
-
|
|
126
|
+
// manifest writes serialize on this lock, so without it a repair racing a
|
|
127
|
+
// concurrent export/reorder can clobber the other writer's manifest. Not
|
|
128
|
+
// reentrant — callers must not already hold it.
|
|
129
|
+
//
|
|
130
|
+
// A dry run takes the lock too: it reads the same manifest and patch bodies
|
|
131
|
+
// a real run would, and a projection computed against a queue being
|
|
132
|
+
// rewritten underneath is worth less than no projection at all.
|
|
133
|
+
return withPatchDirectoryLock(patchesDir, () => rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVersion, options), options);
|
|
99
134
|
}
|
|
100
|
-
async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVersion) {
|
|
135
|
+
async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVersion, options) {
|
|
101
136
|
const manifestState = await loadPatchesManifestState(patchesDir);
|
|
102
137
|
const existingEntries = new Map();
|
|
138
|
+
// An unparseable manifest yields `manifest: null`, which the merge below
|
|
139
|
+
// cannot distinguish from "no manifest at all" — every entry would be
|
|
140
|
+
// treated as new and every descriptive field reinvented. `filesAffected`
|
|
141
|
+
// is the only field a .patch body can yield; `stagedDependencies`,
|
|
142
|
+
// `lintIgnore`, `tier`, `description`, `category`, `createdAt` and the
|
|
143
|
+
// source version cannot be reconstructed from a diff at all. Refuse rather
|
|
144
|
+
// than write that, since the write is what makes it unrecoverable on a
|
|
145
|
+
// queue that is not under version control.
|
|
146
|
+
if (manifestState.exists && manifestState.parseError && options.allowMetadataLoss !== true) {
|
|
147
|
+
throw new GeneralError(`Refusing to rebuild ${PATCHES_MANIFEST}: it exists but could not be parsed ` +
|
|
148
|
+
`(${manifestState.parseError.message}). None of its metadata can be carried ` +
|
|
149
|
+
'forward, so every entry would be rebuilt with an invented description, an ' +
|
|
150
|
+
'mtime-based createdAt and an inferred category, and any stagedDependencies, ' +
|
|
151
|
+
'lintIgnore or tier declarations would be lost — a patch body carries none of ' +
|
|
152
|
+
'them.\n' +
|
|
153
|
+
`Fix the JSON in ${PATCHES_MANIFEST} (restore it from version control if you ` +
|
|
154
|
+
'can) and re-run, or pass --allow-metadata-loss to accept the reinvention.');
|
|
155
|
+
}
|
|
103
156
|
if (manifestState.manifest) {
|
|
104
157
|
for (const entry of manifestState.manifest.patches) {
|
|
105
158
|
existingEntries.set(entry.filename, entry);
|
|
@@ -113,13 +166,12 @@ async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVers
|
|
|
113
166
|
}, 0);
|
|
114
167
|
let nextRecoveredOrder = highestFiniteOrder + 1;
|
|
115
168
|
// Prefetch the two independent per-patch reads concurrently. This runs
|
|
116
|
-
// while the patch-directory lock is held, and
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
// ordinals.
|
|
169
|
+
// while the patch-directory lock is held, and as two serialised awaits per
|
|
170
|
+
// patch it costs 58 round-trips on a 29-patch queue. The fold below stays
|
|
171
|
+
// sequential because `nextRecoveredOrder` is loop-carried: parallelising
|
|
172
|
+
// the assignment would scramble the recovered ordinals.
|
|
121
173
|
const prefetched = await Promise.all(patches.map(async (patch) => ({
|
|
122
|
-
filesAffected:
|
|
174
|
+
filesAffected: normalizeAffectedFiles(await getAllTargetFilesFromPatch(patch.path)),
|
|
123
175
|
patchStats: await stat(patch.path),
|
|
124
176
|
})));
|
|
125
177
|
for (const [index, patch] of patches.entries()) {
|
|
@@ -131,26 +183,27 @@ async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVers
|
|
|
131
183
|
const inferred = inferPatchMetadataFromFilename(patch.filename);
|
|
132
184
|
const recoveredOrder = Number.isFinite(patch.order) ? patch.order : nextRecoveredOrder++;
|
|
133
185
|
if (!existing) {
|
|
134
|
-
// Track every filename that had no pre-existing manifest entry
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// human-written descriptions during a recovery run.
|
|
186
|
+
// Track every filename that had no pre-existing manifest entry so
|
|
187
|
+
// callers can warn the operator per-patch. A missing entry means every
|
|
188
|
+
// descriptive field (`description`, `createdAt`, `category`) was
|
|
189
|
+
// invented rather than preserved, and FireForge patch files carry no
|
|
190
|
+
// header metadata that could carry a human description forward — so
|
|
191
|
+
// visibility is the best available, and silent overwrites of
|
|
192
|
+
// human-written descriptions during a recovery run are the failure to
|
|
193
|
+
// avoid.
|
|
143
194
|
recoveredFilenames.push(patch.filename);
|
|
144
195
|
}
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
196
|
+
// The rebuild OWNS exactly two fields — `filesAffected` (recomputed from
|
|
197
|
+
// the patch body) and `order` (from the filename ordinal). Everything
|
|
198
|
+
// else on an existing entry is carried forward by the spread, so a field
|
|
199
|
+
// a .patch cannot express — `stagedDependencies`, `lintIgnore`, `tier`,
|
|
200
|
+
// and whatever is added to PatchMetadata next — survives a repair by
|
|
201
|
+
// construction rather than by someone remembering to list it here. It was
|
|
202
|
+
// an enumerated literal until `stagedDependencies` was dropped from every
|
|
203
|
+
// entry that had one, which is the failure this shape prevents from
|
|
204
|
+
// recurring.
|
|
153
205
|
const rebuilt = {
|
|
206
|
+
...(existing ?? {}),
|
|
154
207
|
filename: patch.filename,
|
|
155
208
|
order: recoveredOrder,
|
|
156
209
|
category: existing?.category ?? inferred.category,
|
|
@@ -160,24 +213,42 @@ async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVers
|
|
|
160
213
|
createdAt: existing?.createdAt ?? new Date(patchStats.mtimeMs).toISOString(),
|
|
161
214
|
sourceEsrVersion: existing?.sourceEsrVersion ?? fallbackSourceEsrVersion,
|
|
162
215
|
sourceVersion: existing?.sourceVersion ?? existing?.sourceEsrVersion ?? fallbackSourceEsrVersion,
|
|
163
|
-
...(existing?.sourceProduct !== undefined ? { sourceProduct: existing.sourceProduct } : {}),
|
|
164
216
|
filesAffected,
|
|
165
217
|
};
|
|
218
|
+
// Copy the arrays rather than aliasing the loaded manifest's, so a caller
|
|
219
|
+
// mutating the rebuilt entry cannot reach back into `manifestState`.
|
|
166
220
|
if (existing?.lintIgnore !== undefined)
|
|
167
221
|
rebuilt.lintIgnore = [...existing.lintIgnore];
|
|
168
|
-
if (existing?.tier !== undefined)
|
|
169
|
-
rebuilt.tier = existing.tier;
|
|
170
222
|
rebuiltPatches.push(rebuilt);
|
|
223
|
+
existingEntries.delete(patch.filename);
|
|
171
224
|
}
|
|
172
225
|
rebuiltPatches.sort((left, right) => left.order - right.order || left.filename.localeCompare(right.filename));
|
|
173
226
|
const rebuiltManifest = {
|
|
174
227
|
version: 1,
|
|
175
228
|
patches: rebuiltPatches,
|
|
176
229
|
};
|
|
230
|
+
// Whatever is left in the map had a manifest row but no patch file, so the
|
|
231
|
+
// rebuild drops it. That is the correct outcome — the file is the source of
|
|
232
|
+
// truth — but dropping rows without naming them is indistinguishable from
|
|
233
|
+
// never having had them.
|
|
234
|
+
const droppedFilenames = [...existingEntries.keys()].sort((left, right) => left.localeCompare(right));
|
|
235
|
+
if (options.dryRun === true) {
|
|
236
|
+
return {
|
|
237
|
+
manifest: rebuiltManifest,
|
|
238
|
+
recoveredFilenames,
|
|
239
|
+
droppedFilenames,
|
|
240
|
+
written: false,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
177
243
|
await savePatchesManifest(patchesDir, rebuiltManifest);
|
|
178
|
-
return { manifest: rebuiltManifest, recoveredFilenames };
|
|
244
|
+
return { manifest: rebuiltManifest, recoveredFilenames, droppedFilenames, written: true };
|
|
179
245
|
}
|
|
180
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Canonical form of a `filesAffected` list: deduplicated and sorted, so a
|
|
248
|
+
* comparison between a declared list and one extracted from a patch body is
|
|
249
|
+
* about membership rather than ordering.
|
|
250
|
+
*/
|
|
251
|
+
export function normalizeAffectedFiles(files) {
|
|
181
252
|
return Array.from(new Set(files)).sort((left, right) => left.localeCompare(right));
|
|
182
253
|
}
|
|
183
254
|
function sameStringArray(left, right) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow repair for `filesAffected` — the one manifest field a `.patch` body
|
|
3
|
+
* can actually yield.
|
|
4
|
+
*
|
|
5
|
+
* A `files-affected-mismatch` is pure derived-value drift: the manifest's list
|
|
6
|
+
* disagrees with the diff it describes, and the diff is right by definition.
|
|
7
|
+
* Rebuilding the whole manifest to fix it is a much larger operation than the
|
|
8
|
+
* failure calls for, so this repair recomputes one field on the drifted rows
|
|
9
|
+
* and leaves every other row byte-identical.
|
|
10
|
+
*/
|
|
11
|
+
import type { PatchDirectoryLockOptions } from './patch-lock.js';
|
|
12
|
+
/** One row whose `filesAffected` list disagreed with its patch body. */
|
|
13
|
+
export interface FilesAffectedRepair {
|
|
14
|
+
/** Patch filename. */
|
|
15
|
+
filename: string;
|
|
16
|
+
/** The list the manifest declared, normalized for comparison. */
|
|
17
|
+
before: string[];
|
|
18
|
+
/** The list the patch body actually targets. */
|
|
19
|
+
after: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Summary of a {@link repairPatchesFilesAffected} run. */
|
|
22
|
+
export interface RepairFilesAffectedResult {
|
|
23
|
+
/** Rows that drifted and were (or would be) corrected. */
|
|
24
|
+
repairs: FilesAffectedRepair[];
|
|
25
|
+
/** Requested filenames with no manifest row, or no patch file on disk. */
|
|
26
|
+
skippedFilenames: string[];
|
|
27
|
+
/** False when the run was a dry run and patches.json was left untouched. */
|
|
28
|
+
written: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Options for {@link repairPatchesFilesAffected}. */
|
|
31
|
+
export interface RepairFilesAffectedOptions extends PatchDirectoryLockOptions {
|
|
32
|
+
/** Compute the corrections without writing them. */
|
|
33
|
+
dryRun?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Recomputes `filesAffected` from the patch body for the named patches.
|
|
37
|
+
*
|
|
38
|
+
* @param patchesDir - Path to the patches directory
|
|
39
|
+
* @param filenames - Patch filenames to repair
|
|
40
|
+
* @param options - Lock options plus `dryRun`
|
|
41
|
+
* @returns The corrections applied (or projected) and the filenames skipped
|
|
42
|
+
*/
|
|
43
|
+
export declare function repairPatchesFilesAffected(patchesDir: string, filenames: readonly string[], options?: RepairFilesAffectedOptions): Promise<RepairFilesAffectedResult>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Narrow repair for `filesAffected` — the one manifest field a `.patch` body
|
|
4
|
+
* can actually yield.
|
|
5
|
+
*
|
|
6
|
+
* A `files-affected-mismatch` is pure derived-value drift: the manifest's list
|
|
7
|
+
* disagrees with the diff it describes, and the diff is right by definition.
|
|
8
|
+
* Rebuilding the whole manifest to fix it is a much larger operation than the
|
|
9
|
+
* failure calls for, so this repair recomputes one field on the drifted rows
|
|
10
|
+
* and leaves every other row byte-identical.
|
|
11
|
+
*/
|
|
12
|
+
import { GeneralError } from '../errors/base.js';
|
|
13
|
+
import { discoverPatches, getAllTargetFilesFromPatch } from './patch-files.js';
|
|
14
|
+
import { withPatchDirectoryLock } from './patch-lock.js';
|
|
15
|
+
import { normalizeAffectedFiles } from './patch-manifest-consistency.js';
|
|
16
|
+
import { loadPatchesManifestForWrite, mutatePatchRowsInManifest, PATCHES_MANIFEST, } from './patch-manifest-io.js';
|
|
17
|
+
/**
|
|
18
|
+
* Recomputes `filesAffected` from the patch body for the named patches.
|
|
19
|
+
*
|
|
20
|
+
* @param patchesDir - Path to the patches directory
|
|
21
|
+
* @param filenames - Patch filenames to repair
|
|
22
|
+
* @param options - Lock options plus `dryRun`
|
|
23
|
+
* @returns The corrections applied (or projected) and the filenames skipped
|
|
24
|
+
*/
|
|
25
|
+
export async function repairPatchesFilesAffected(patchesDir, filenames, options = {}) {
|
|
26
|
+
// Same lock contract as the full rebuild (invariant 2,
|
|
27
|
+
// docs/lifecycle-invariants.md): a manifest write serializes on the
|
|
28
|
+
// patch-directory lock, and a dry run reads under it so its projection
|
|
29
|
+
// cannot be computed against a queue being rewritten underneath.
|
|
30
|
+
return withPatchDirectoryLock(patchesDir, () => repairPatchesFilesAffectedUnderLock(patchesDir, filenames, options), options);
|
|
31
|
+
}
|
|
32
|
+
async function repairPatchesFilesAffectedUnderLock(patchesDir, filenames, options) {
|
|
33
|
+
// ForWrite: a corrupt manifest must abort here rather than be treated as
|
|
34
|
+
// empty. This repair exists precisely so that a one-field drift never
|
|
35
|
+
// reaches a whole-manifest rewrite, and silently writing over unparseable
|
|
36
|
+
// metadata would be a larger version of what it avoids.
|
|
37
|
+
const manifest = await loadPatchesManifestForWrite(patchesDir);
|
|
38
|
+
if (!manifest) {
|
|
39
|
+
throw new GeneralError(`Cannot repair filesAffected: ${PATCHES_MANIFEST} does not exist in ${patchesDir}.`);
|
|
40
|
+
}
|
|
41
|
+
const patchPathByFilename = new Map((await discoverPatches(patchesDir)).map((patch) => [patch.filename, patch.path]));
|
|
42
|
+
const rowByFilename = new Map(manifest.patches.map((entry) => [entry.filename, entry]));
|
|
43
|
+
const repairs = [];
|
|
44
|
+
const skippedFilenames = [];
|
|
45
|
+
for (const filename of new Set(filenames)) {
|
|
46
|
+
const patchPath = patchPathByFilename.get(filename);
|
|
47
|
+
const row = rowByFilename.get(filename);
|
|
48
|
+
if (patchPath === undefined || !row) {
|
|
49
|
+
// A missing patch file or a missing row is a different consistency
|
|
50
|
+
// issue with a different remedy; this repair reports it rather than
|
|
51
|
+
// inventing a row or deleting one.
|
|
52
|
+
skippedFilenames.push(filename);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const before = normalizeAffectedFiles(row.filesAffected);
|
|
56
|
+
const after = normalizeAffectedFiles(await getAllTargetFilesFromPatch(patchPath));
|
|
57
|
+
if (sameFiles(before, after))
|
|
58
|
+
continue;
|
|
59
|
+
repairs.push({ filename, before, after });
|
|
60
|
+
}
|
|
61
|
+
skippedFilenames.sort((left, right) => left.localeCompare(right));
|
|
62
|
+
if (repairs.length === 0 || options.dryRun === true) {
|
|
63
|
+
return { repairs, skippedFilenames, written: false };
|
|
64
|
+
}
|
|
65
|
+
const correctedByFilename = new Map(repairs.map((repair) => [repair.filename, repair.after]));
|
|
66
|
+
// Row-scoped mutation, so every untouched entry keeps its exact JSON shape —
|
|
67
|
+
// no re-sorted `filesAffected` lists and no reader-only fallback fields
|
|
68
|
+
// serialized into rows this repair has no business touching.
|
|
69
|
+
await mutatePatchRowsInManifest(patchesDir, [...correctedByFilename.keys()], (existing) => {
|
|
70
|
+
const corrected = correctedByFilename.get(existing.filename);
|
|
71
|
+
return corrected ? { set: { filesAffected: corrected } } : null;
|
|
72
|
+
});
|
|
73
|
+
return { repairs, skippedFilenames, written: true };
|
|
74
|
+
}
|
|
75
|
+
function sameFiles(left, right) {
|
|
76
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
77
|
+
}
|
|
@@ -12,7 +12,7 @@ import { rename } from 'node:fs/promises';
|
|
|
12
12
|
import { join } from 'node:path';
|
|
13
13
|
import { FireForgeError } from '../errors/base.js';
|
|
14
14
|
import { ExitCode } from '../errors/codes.js';
|
|
15
|
-
import { PatchManifestCorruptError } from '../errors/patch.js';
|
|
15
|
+
import { PatchError, PatchManifestCorruptError } from '../errors/patch.js';
|
|
16
16
|
import { assert } from '../utils/assert.js';
|
|
17
17
|
import { toError } from '../utils/errors.js';
|
|
18
18
|
import { pathExistsStrict, readJson, removeFile, writeJson } from '../utils/fs.js';
|
|
@@ -315,7 +315,7 @@ export async function renumberPatchesInManifest(patchesDir, renameMap) {
|
|
|
315
315
|
await assertPatchDirectoryLockHeld(patchesDir, 'renumbering the patch manifest');
|
|
316
316
|
const manifest = await loadPatchesManifestForWrite(patchesDir);
|
|
317
317
|
if (!manifest) {
|
|
318
|
-
throw new
|
|
318
|
+
throw new PatchError('Cannot renumber patches: patches.json is missing.');
|
|
319
319
|
}
|
|
320
320
|
// Phase 1: rename each old filename to a unique temp staging name so the
|
|
321
321
|
// later rename to the final target cannot collide with another entry
|
|
@@ -326,7 +326,7 @@ export async function renumberPatchesInManifest(patchesDir, renameMap) {
|
|
|
326
326
|
for (const [oldFilename, entry] of renameMap) {
|
|
327
327
|
const oldPath = join(patchesDir, oldFilename);
|
|
328
328
|
if (!(await pathExistsStrict(oldPath))) {
|
|
329
|
-
throw new
|
|
329
|
+
throw new PatchError(`Cannot renumber: patch file is missing on disk: ${oldFilename}`, oldFilename);
|
|
330
330
|
}
|
|
331
331
|
const stagedName = `.fireforge-renumber-${stagingId}-${oldFilename}`;
|
|
332
332
|
const stagedPath = join(patchesDir, stagedName);
|
|
@@ -354,18 +354,16 @@ export async function renumberPatchesInManifest(patchesDir, renameMap) {
|
|
|
354
354
|
const { staged, toEntry } = stagedEntry;
|
|
355
355
|
const targetPath = join(patchesDir, toEntry.newFilename);
|
|
356
356
|
if (await pathExistsStrict(targetPath)) {
|
|
357
|
-
throw new
|
|
357
|
+
throw new PatchError(`Cannot renumber: target patch filename already exists on disk: ${toEntry.newFilename}`, toEntry.newFilename);
|
|
358
358
|
}
|
|
359
359
|
await rename(join(patchesDir, staged), targetPath);
|
|
360
|
-
// Postcondition assert: confirm the target actually exists on
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
// files stayed on disk. If the assert ever fires, the Phase 2
|
|
366
|
-
// rollback will undo prior moves before re-throwing.
|
|
360
|
+
// Postcondition assert: confirm the target actually exists on disk
|
|
361
|
+
// before marking the rename complete. A silent rename failure leaves
|
|
362
|
+
// the manifest and the filesystem disagreeing — manifest rewritten to
|
|
363
|
+
// new filenames while the old files stay on disk. If the assert fires,
|
|
364
|
+
// the Phase 2 rollback undoes prior moves before re-throwing.
|
|
367
365
|
if (!(await pathExistsStrict(targetPath))) {
|
|
368
|
-
throw new
|
|
366
|
+
throw new PatchError(`Rename postcondition failed: expected ${toEntry.newFilename} to exist after rename, but it was not found on disk.`, toEntry.newFilename);
|
|
369
367
|
}
|
|
370
368
|
completedFinalRenames.push(stagedEntry);
|
|
371
369
|
}
|
|
@@ -525,12 +523,10 @@ export async function removePatchFileAndManifest(patchesDir, filename) {
|
|
|
525
523
|
await savePatchesManifest(patchesDir, originalManifest);
|
|
526
524
|
}
|
|
527
525
|
catch (rollbackError) {
|
|
528
|
-
// Compound failure: both the delete and the rollback failed,
|
|
529
|
-
//
|
|
530
|
-
//
|
|
531
|
-
//
|
|
532
|
-
// original delete error with a warning about the rollback
|
|
533
|
-
// buried in stderr.
|
|
526
|
+
// Compound failure: both the delete and the rollback failed, so the
|
|
527
|
+
// directory is in a known-inconsistent state. A dedicated error type
|
|
528
|
+
// carrying both causes shows the complete picture instead of the
|
|
529
|
+
// original delete error with a rollback warning buried in stderr.
|
|
534
530
|
throw new PatchDeleteRollbackError(filename, deleteError, toError(rollbackError));
|
|
535
531
|
}
|
|
536
532
|
}
|
|
@@ -7,18 +7,15 @@ import type { PatchMetadata } from '../types/commands/index.js';
|
|
|
7
7
|
* matches `PatchMetadata.filename`.
|
|
8
8
|
* 3. A filename without the `.patch` suffix — the command appends it
|
|
9
9
|
* before matching (e.g. `002-ui-foo`).
|
|
10
|
-
* 4. The manifest `name` field (e.g. `
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* even though the CLI help said `<name>`, forcing the operator
|
|
16
|
-
* to copy the full filename from `patches.json` before every
|
|
17
|
-
* queue mutation.
|
|
10
|
+
* 4. The manifest `name` field (e.g. `furnace-token-override`) — the short
|
|
11
|
+
* logical handle the export workflow stamps onto the patch and the
|
|
12
|
+
* natural identifier an operator keeps in their notes. The CLI help
|
|
13
|
+
* says `<name>`, so rejecting it forces the operator to copy the full
|
|
14
|
+
* filename out of `patches.json` before every queue mutation.
|
|
18
15
|
*
|
|
19
|
-
* Resolution order is strict: numeric ordinals first, then filename
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
16
|
+
* Resolution order is strict: numeric ordinals first, then filename lookup
|
|
17
|
+
* (with and without the `.patch` suffix), then name-field lookup. The
|
|
18
|
+
* filename lookup beats the name lookup when the two collide, so scripts
|
|
19
|
+
* that pass filenames keep working.
|
|
23
20
|
*/
|
|
24
21
|
export declare function resolvePatchIdentifier(identifier: string, patches: PatchMetadata[]): PatchMetadata | null;
|
|
@@ -6,19 +6,16 @@
|
|
|
6
6
|
* matches `PatchMetadata.filename`.
|
|
7
7
|
* 3. A filename without the `.patch` suffix — the command appends it
|
|
8
8
|
* before matching (e.g. `002-ui-foo`).
|
|
9
|
-
* 4. The manifest `name` field (e.g. `
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* even though the CLI help said `<name>`, forcing the operator
|
|
15
|
-
* to copy the full filename from `patches.json` before every
|
|
16
|
-
* queue mutation.
|
|
9
|
+
* 4. The manifest `name` field (e.g. `furnace-token-override`) — the short
|
|
10
|
+
* logical handle the export workflow stamps onto the patch and the
|
|
11
|
+
* natural identifier an operator keeps in their notes. The CLI help
|
|
12
|
+
* says `<name>`, so rejecting it forces the operator to copy the full
|
|
13
|
+
* filename out of `patches.json` before every queue mutation.
|
|
17
14
|
*
|
|
18
|
-
* Resolution order is strict: numeric ordinals first, then filename
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* Resolution order is strict: numeric ordinals first, then filename lookup
|
|
16
|
+
* (with and without the `.patch` suffix), then name-field lookup. The
|
|
17
|
+
* filename lookup beats the name lookup when the two collide, so scripts
|
|
18
|
+
* that pass filenames keep working.
|
|
22
19
|
*/
|
|
23
20
|
export function resolvePatchIdentifier(identifier, patches) {
|
|
24
21
|
if (/^\d+$/.test(identifier)) {
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
* Schema validation for patches.json manifest data.
|
|
3
3
|
*/
|
|
4
4
|
import type { PatchCategory, PatchesManifest } from '../types/commands/index.js';
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Validates raw patches.json data and returns the typed manifest shape.
|
|
7
|
+
*
|
|
8
|
+
* @param data - Parsed patches.json content
|
|
9
|
+
* @returns The validated manifest
|
|
10
|
+
*/
|
|
6
11
|
export declare function validatePatchesManifest(data: unknown): PatchesManifest;
|
|
7
12
|
/**
|
|
8
13
|
* Infers patch metadata from a filename pattern.
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { parseObject } from '../utils/parse.js';
|
|
6
6
|
import { isArray, isObject, isValidFirefoxProduct, isValidFirefoxVersion, PATCH_CATEGORIES, } from '../utils/validation.js';
|
|
7
|
+
import { checkDocumentVersion, describeNewerDocument } from './state-file.js';
|
|
7
8
|
function parseForwardImports(data, label) {
|
|
8
9
|
if (!isArray(data)) {
|
|
9
10
|
throw new Error(`${label} must be an array`);
|
|
@@ -91,16 +92,13 @@ function validatePatchMetadata(data, index) {
|
|
|
91
92
|
throw new Error(`patches[${index}].sourceEsrVersion must be a valid Firefox version string`);
|
|
92
93
|
}
|
|
93
94
|
const filesAffected = rec.stringArray('filesAffected');
|
|
94
|
-
// Optional fields. These
|
|
95
|
-
//
|
|
96
|
-
// `
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
// manifest validated. Preserve both the pre-existing `lintIgnore`
|
|
102
|
-
// and the new `tier` field here so future-added optional fields
|
|
103
|
-
// have a ready template to follow.
|
|
95
|
+
// Optional fields. These are easy to strip silently — an escape hatch
|
|
96
|
+
// that round-trips only through test fixtures mocking
|
|
97
|
+
// `loadPatchesManifest` looks fine while real operator edits to
|
|
98
|
+
// `patches.json` are dropped on every load, so a patch relying on
|
|
99
|
+
// `lintIgnore` to suppress a rule is quietly re-tripped the next time the
|
|
100
|
+
// manifest validates. Preserve both `lintIgnore` and `tier` here; future
|
|
101
|
+
// optional fields follow the same template.
|
|
104
102
|
const lintIgnore = rec.optionalStringArray('lintIgnore');
|
|
105
103
|
const rawTier = rec.raw('tier');
|
|
106
104
|
let tier;
|
|
@@ -133,13 +131,27 @@ function validatePatchMetadata(data, index) {
|
|
|
133
131
|
}
|
|
134
132
|
return result;
|
|
135
133
|
}
|
|
136
|
-
/**
|
|
134
|
+
/** Manifest schema version this build writes and understands. */
|
|
135
|
+
const PATCHES_MANIFEST_VERSION = 1;
|
|
136
|
+
/**
|
|
137
|
+
* Validates raw patches.json data and returns the typed manifest shape.
|
|
138
|
+
*
|
|
139
|
+
* @param data - Parsed patches.json content
|
|
140
|
+
* @returns The validated manifest
|
|
141
|
+
*/
|
|
137
142
|
export function validatePatchesManifest(data) {
|
|
138
143
|
if (!isObject(data)) {
|
|
139
144
|
throw new Error('patches.json must be a JSON object');
|
|
140
145
|
}
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
// A manifest from a NEWER FireForge is not corrupt. The bare "version must
|
|
147
|
+
// be 1" this replaced sent the operator looking for damage in a file that
|
|
148
|
+
// only needed a newer build to read it.
|
|
149
|
+
const version = checkDocumentVersion(data, 'version', PATCHES_MANIFEST_VERSION);
|
|
150
|
+
if (version.kind === 'newer') {
|
|
151
|
+
throw new Error(describeNewerDocument('patches manifest', version.found, PATCHES_MANIFEST_VERSION));
|
|
152
|
+
}
|
|
153
|
+
if (version.kind !== 'current') {
|
|
154
|
+
throw new Error(`patches.json version must be ${PATCHES_MANIFEST_VERSION}`);
|
|
143
155
|
}
|
|
144
156
|
if (!isArray(data['patches'])) {
|
|
145
157
|
throw new Error('patches.json field "patches" must be an array');
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Callers should continue to import from this module; the internal split
|
|
5
5
|
* is an implementation detail.
|
|
6
6
|
*/
|
|
7
|
-
export { rebuildPatchesManifest, validatePatchesManifestConsistency, } from './patch-manifest-consistency.js';
|
|
7
|
+
export { type PatchManifestConsistencyIssue, rebuildPatchesManifest, recommendManifestRepair, validatePatchesManifestConsistency, } from './patch-manifest-consistency.js';
|
|
8
|
+
export { type FilesAffectedRepair, repairPatchesFilesAffected, } from './patch-manifest-files-affected.js';
|
|
8
9
|
export { addPatchToManifest, loadPatchesManifest, loadPatchesManifestForWrite, mutatePatchRowsInManifest, PATCHES_MANIFEST, type PatchRenameEntry, removePatchFileAndManifest, renumberPatchesInManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from './patch-manifest-io.js';
|
|
9
10
|
export { checkVersionCompatibility, findPatchesAffectingFile, getClaimedFiles, stampPatchVersions, validatePatchIntegrity, } from './patch-manifest-query.js';
|
|
10
11
|
export { resolvePatchIdentifier } from './patch-manifest-resolve.js';
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Callers should continue to import from this module; the internal split
|
|
6
6
|
* is an implementation detail.
|
|
7
7
|
*/
|
|
8
|
-
export { rebuildPatchesManifest, validatePatchesManifestConsistency, } from './patch-manifest-consistency.js';
|
|
8
|
+
export { rebuildPatchesManifest, recommendManifestRepair, validatePatchesManifestConsistency, } from './patch-manifest-consistency.js';
|
|
9
|
+
export { repairPatchesFilesAffected, } from './patch-manifest-files-affected.js';
|
|
9
10
|
export { addPatchToManifest, loadPatchesManifest, loadPatchesManifestForWrite, mutatePatchRowsInManifest, PATCHES_MANIFEST, removePatchFileAndManifest, renumberPatchesInManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from './patch-manifest-io.js';
|
|
10
11
|
export { checkVersionCompatibility, findPatchesAffectingFile, getClaimedFiles, stampPatchVersions, validatePatchIntegrity, } from './patch-manifest-query.js';
|
|
11
12
|
export { resolvePatchIdentifier } from './patch-manifest-resolve.js';
|