@hominis/fireforge 0.43.0 → 0.44.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.
- package/CHANGELOG.md +127 -0
- package/README.md +10 -1
- 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 +4 -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-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.js +35 -53
- 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 +9 -9
- 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 +9 -16
- 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 +42 -35
- 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 +27 -33
- 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 +62 -19
- package/dist/src/core/build-prepare.d.ts +2 -2
- package/dist/src/core/build-prepare.js +8 -9
- 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 +12 -12
- package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
- package/dist/src/core/furnace-apply-helpers.js +19 -16
- 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 +7 -9
- package/dist/src/core/git-base.js +7 -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 +54 -70
- 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 +16 -16
- 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 +2 -1
- package/dist/src/core/patch-manifest-consistency.js +25 -27
- 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-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 +5 -7
- 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 +9 -11
- package/dist/src/core/token-docs.d.ts +2 -0
- package/dist/src/core/token-docs.js +2 -0
- 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 +4 -4
- 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 +63 -42
- 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 +8 -8
- 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 +88 -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 +2 -2
- 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
|
@@ -17,12 +17,11 @@ import { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs, resolvePatchOw
|
|
|
17
17
|
// Cross-patch lint re-exports
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
//
|
|
20
|
-
// The cross-patch lint infrastructure (queue context builder,
|
|
21
|
-
// creation and forward-import rules, ignore marker) lives in
|
|
22
|
-
// `patch-lint-cross.ts` so the per-patch and cross-patch rule bodies
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// import from a single module.
|
|
20
|
+
// The cross-patch lint infrastructure (queue context builder,
|
|
21
|
+
// duplicate-creation and forward-import rules, ignore marker) lives in
|
|
22
|
+
// `patch-lint-cross.ts` so the per-patch and cross-patch rule bodies each
|
|
23
|
+
// stay within the per-file line budget. The public surface is re-exported
|
|
24
|
+
// from `patch-lint-reexports.ts` so callers keep importing from one module.
|
|
26
25
|
export * from './patch-lint-reexports.js';
|
|
27
26
|
// The CSS rule bodies live in `patch-lint-css.ts` (same per-file-budget
|
|
28
27
|
// split as the other rule families); re-export the imported binding so
|
|
@@ -38,10 +37,10 @@ const FILE_SIZE_THRESHOLDS = {
|
|
|
38
37
|
};
|
|
39
38
|
/**
|
|
40
39
|
* Counts the lines of `content` the way `wc -l` reports them: a trailing
|
|
41
|
-
* newline terminates the last line rather than starting an empty extra
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
40
|
+
* newline terminates the last line rather than starting an empty extra one.
|
|
41
|
+
* A naive `split('\n').length` over-counts by one for any content ending in
|
|
42
|
+
* `\n` (the common case), making the size rules fire one line early against
|
|
43
|
+
* the operator's own `wc -l` measurement.
|
|
45
44
|
*/
|
|
46
45
|
function countContentLines(content) {
|
|
47
46
|
const lines = content.split('\n');
|
|
@@ -94,50 +93,39 @@ const PATCH_LINE_THRESHOLDS = {
|
|
|
94
93
|
* Branding patches have a legitimate reason to be large: they include
|
|
95
94
|
* every locale's `brand.ftl`, copied upstream CSS/PNG assets, and the
|
|
96
95
|
* fork-specific `configure.sh` / `brand.properties` under a single
|
|
97
|
-
* `browser/branding/<name>/` subtree.
|
|
96
|
+
* `browser/branding/<name>/` subtree.
|
|
98
97
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* Both data points need to surface as a soft `notice` rather than a
|
|
107
|
-
* `warning`, since they represent the *minimum* branding diff. The
|
|
108
|
-
* pre-2026-04-25 calibration {3000/8000/20000} put 15904 firmly in the
|
|
109
|
-
* `warning` band, contradicting the docstring's "loud but not
|
|
110
|
-
* actionable" intent. The current calibration moves the warning band
|
|
111
|
-
* above the eval baseline (with ~13% headroom) and the error band to
|
|
112
|
-
* roughly 2× the baseline — reaching `error` strongly suggests
|
|
113
|
-
* non-branding work is bundled in.
|
|
98
|
+
* Calibrated against a fresh-fork branding export, which lands around
|
|
99
|
+
* 15–16k lines (localized brand.ftl across many locales + SVG path data +
|
|
100
|
+
* copied upstream CSS). That is the MINIMUM branding diff, so it must
|
|
101
|
+
* surface as a soft `notice` rather than a `warning`: the warning band
|
|
102
|
+
* sits above it with ~13% headroom, and the error band at roughly 2× the
|
|
103
|
+
* baseline, where non-branding work is probably bundled in.
|
|
114
104
|
*
|
|
115
105
|
* Permissive thresholds are safe because the *gate* into this tier is
|
|
116
|
-
* narrow
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* cannot accidentally land here.
|
|
106
|
+
* narrow: auto-detect requires every file under `browser/branding/` plus a
|
|
107
|
+
* tight registration allowlist, or an explicit `PatchMetadata.tier:
|
|
108
|
+
* "branding"` opt-in. A non-branding patch cannot accidentally land here.
|
|
120
109
|
*/
|
|
121
110
|
branding: { notice: 8000, warning: 18000, error: 30000 },
|
|
122
111
|
};
|
|
123
112
|
/**
|
|
124
|
-
* File-count thresholds for the `large-patch-files` rule, mirroring the
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
113
|
+
* File-count thresholds for the `large-patch-files` rule, mirroring the tier
|
|
114
|
+
* shape of {@link PATCH_LINE_THRESHOLDS}. A single warning-only threshold
|
|
115
|
+
* per tier is intentional — file count expresses scope, not blast radius,
|
|
116
|
+
* and there is no error band that would block export.
|
|
128
117
|
*
|
|
129
118
|
* The branding tier sits well above the typical floor because branding
|
|
130
|
-
* patches inherently span many files: PNG/ICO icon assets in 7+ sizes,
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* on a genuinely bloated patch.
|
|
119
|
+
* patches inherently span many files: PNG/ICO icon assets in 7+ sizes, MSIX
|
|
120
|
+
* manifests, channel-specific configs, locale `.ftl` files, Windows/macOS
|
|
121
|
+
* launcher resources. A fresh-fork branding bundle is around 56 files, so 60
|
|
122
|
+
* leaves headroom for additional channels/locales while still firing on a
|
|
123
|
+
* genuinely bloated patch.
|
|
136
124
|
*
|
|
137
|
-
* Test tier matches general because a test-only patch rarely touches
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
125
|
+
* Test tier matches general because a test-only patch rarely touches many
|
|
126
|
+
* files (a single regression test usually adds 1–3 fixtures); the elevation
|
|
127
|
+
* in {@link PATCH_LINE_THRESHOLDS.test} addresses big table-driven test
|
|
128
|
+
* bodies, not file fan-out.
|
|
141
129
|
*/
|
|
142
130
|
const PATCH_FILES_THRESHOLDS = {
|
|
143
131
|
general: 5,
|
|
@@ -145,29 +133,25 @@ const PATCH_FILES_THRESHOLDS = {
|
|
|
145
133
|
branding: 60,
|
|
146
134
|
};
|
|
147
135
|
/**
|
|
148
|
-
* Shared remedy sentence for the over-budget findings
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* emission sites — the wording cannot drift.
|
|
136
|
+
* Shared remedy sentence for the over-budget findings. Both places an
|
|
137
|
+
* operator stands when a patch outgrows its budget must name the sanctioned
|
|
138
|
+
* one-transaction way out, or they reach for the wrong tools first. One
|
|
139
|
+
* constant, four emission sites, so the wording cannot drift.
|
|
153
140
|
*/
|
|
154
141
|
const SPLIT_HINT = 'Consider splitting into smaller, focused patches — ' +
|
|
155
142
|
'"fireforge patch move-files <from> <to> --file <path> --create --order <n>" ' +
|
|
156
143
|
'moves files into a new patch in one transaction.';
|
|
157
144
|
/**
|
|
158
145
|
* Fixed allowlist of non-branding sibling paths that real-world Firefox
|
|
159
|
-
* branding patches legitimately need to touch to register the new
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* `browser/moz.configure`
|
|
163
|
-
*
|
|
164
|
-
* required every file to live under `browser/branding/`. This
|
|
165
|
-
* allowlist stays intentionally narrow — additions require a real
|
|
166
|
-
* operator data point, not a speculative expansion. Add new entries
|
|
167
|
-
* only when a genuine branding patch cannot be expressed without a
|
|
168
|
-
* specific registration sibling.
|
|
146
|
+
* branding patches legitimately need to touch to register the new branding
|
|
147
|
+
* flavor with the top-level configure. A predicate requiring every file to
|
|
148
|
+
* live under `browser/branding/` drops a branding patch that also touches
|
|
149
|
+
* `browser/moz.configure` — the canonical registration point — into the
|
|
150
|
+
* general lint tier.
|
|
169
151
|
*
|
|
170
|
-
*
|
|
152
|
+
* Intentionally narrow: add entries only when a genuine branding patch
|
|
153
|
+
* cannot be expressed without a specific registration sibling. Pinned
|
|
154
|
+
* against ESR 140.x conventions at time of writing.
|
|
171
155
|
*/
|
|
172
156
|
const BRANDING_REGISTRATION_FILES = new Set([
|
|
173
157
|
'browser/moz.configure',
|
|
@@ -235,33 +219,31 @@ function isBrowserChromeTestFile(file) {
|
|
|
235
219
|
/**
|
|
236
220
|
* Bare assertion-call detection for the `test-needs-assertion` floor.
|
|
237
221
|
*
|
|
238
|
-
* Each alternative must sit at a call boundary: preceded by something that
|
|
239
|
-
* not an identifier character, `.`, `#`, or `-`.
|
|
240
|
-
* `strippedContent.includes(tok)` over
|
|
241
|
-
* `
|
|
242
|
-
*
|
|
243
|
-
* `promiseIsDeeply(` contains `isDeeply(
|
|
244
|
-
* any such call
|
|
245
|
-
* catch.
|
|
222
|
+
* Each alternative must sit at a call boundary: preceded by something that
|
|
223
|
+
* is not an identifier character, `.`, `#`, or `-`. A plain
|
|
224
|
+
* `strippedContent.includes(tok)` over `Assert.`, `ok(`, `is(`, `isnot(`,
|
|
225
|
+
* `isDeeply(` lets any identifier merely *ending* in one satisfy the gate —
|
|
226
|
+
* `this.axis(3)` contains `is(`, `book(` contains `ok(`,
|
|
227
|
+
* `promiseIsDeeply(` contains `isDeeply(` — so an assertion-free smoke test
|
|
228
|
+
* using any such call clears the floor.
|
|
246
229
|
*
|
|
247
|
-
* The namespaced arm must name a member
|
|
248
|
-
* its dot alone
|
|
249
|
-
*
|
|
250
|
-
* `SimpleTest.
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
230
|
+
* The namespaced arm must name a member AND a call. Matching the namespace
|
|
231
|
+
* and its dot alone re-opens the same hole from the other side:
|
|
232
|
+
* `SimpleTest` is mostly *harness* API, so `SimpleTest.finish()`,
|
|
233
|
+
* `SimpleTest.waitForExplicitFinish()` and `SimpleTest.requestCompleteLog()`
|
|
234
|
+
* would each clear the floor on their own — the exact calls an
|
|
235
|
+
* assertion-free smoke test contains. The enumeration below is the
|
|
236
|
+
* mochitest/xpcshell assertion surface both namespaces share, and it is what
|
|
237
|
+
* the user-facing message names.
|
|
255
238
|
*
|
|
256
|
-
* Two qualified spellings of the bare four
|
|
257
|
-
* globals ARE window properties) and an optional-chained `win?.ok(...)
|
|
258
|
-
*
|
|
239
|
+
* Two qualified spellings of the bare four also count: `window.ok(...)` (the
|
|
240
|
+
* globals ARE window properties) and an optional-chained `win?.ok(...)`;
|
|
241
|
+
* blocking those flags tests whose only assertions are real as
|
|
259
242
|
* assertion-free. `SpecialPowers.ok(` stays excluded — that is API surface,
|
|
260
243
|
* not an assertion.
|
|
261
244
|
*
|
|
262
|
-
* Deliberately
|
|
263
|
-
*
|
|
264
|
-
* proof.
|
|
245
|
+
* Deliberately textual, not AST-based: this runs over patch bodies rather
|
|
246
|
+
* than resolvable sources, and the surrounding rule is a floor, not a proof.
|
|
265
247
|
*/
|
|
266
248
|
const ASSERTION_MEMBERS = [
|
|
267
249
|
'ok',
|
|
@@ -318,22 +300,18 @@ export function commentStyleForFile(file) {
|
|
|
318
300
|
* Accepted shapes, in order:
|
|
319
301
|
* - the project's own header for the file's comment style;
|
|
320
302
|
* - under `browser/branding/`, ANY recognised license header in the
|
|
321
|
-
* matching style: the setup-generated branding directory is copied
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* GPL-2.0-or-later;
|
|
303
|
+
* matching style: the setup-generated branding directory is copied from
|
|
304
|
+
* Firefox's `unofficial` template and arrives with Mozilla MPL-2.0
|
|
305
|
+
* headers — legitimate for copyright purposes (the assets are Mozilla's)
|
|
306
|
+
* even when the fork's own code is 0BSD / EUPL-1.2 / GPL-2.0-or-later;
|
|
326
307
|
* - on an MPL-2.0 project, any recognised header in the matching style;
|
|
327
308
|
* - the verbatim upstream MPL-2.0 block header (`/* … *\/`, optionally
|
|
328
|
-
* behind a leading editor-directive comment) REGARDLESS of the
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
* still needed a patch-level lintIgnore. Only the block form is
|
|
335
|
-
* accepted — the `// `-style MPL header is FireForge-generated, not
|
|
336
|
-
* upstream provenance — and `hash`-style files (FTL) stay excluded
|
|
309
|
+
* behind a leading editor-directive comment) REGARDLESS of the project
|
|
310
|
+
* license, on any file whose comment syntax is the block form (JS and
|
|
311
|
+
* CSS): a file copied from the Firefox tree legitimately keeps Mozilla's
|
|
312
|
+
* header for provenance in an EUPL/GPL/0BSD project too. Only the block
|
|
313
|
+
* form is accepted — the `// `-style MPL header is FireForge-generated,
|
|
314
|
+
* not upstream provenance — and `hash`-style files (FTL) stay excluded
|
|
337
315
|
* because `/* … *\/` is not a comment there.
|
|
338
316
|
*
|
|
339
317
|
* @param file - Engine-relative path of the new file
|
|
@@ -588,16 +566,15 @@ export function lintPatchSize(filesAffected, lineCount, patchTier) {
|
|
|
588
566
|
const issues = [];
|
|
589
567
|
// Tier selection: test > branding > general. Tests keep their elevated
|
|
590
568
|
// thresholds because a big regression test is legitimate (table-driven
|
|
591
|
-
// harnesses run into the thousands of lines). Branding patches get
|
|
592
|
-
//
|
|
593
|
-
//
|
|
594
|
-
//
|
|
595
|
-
//
|
|
596
|
-
//
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
// disagree about which tier applies.
|
|
569
|
+
// harnesses run into the thousands of lines). Branding patches get their
|
|
570
|
+
// own tier so a first export of setup-generated branding does not fire the
|
|
571
|
+
// general hard limit — see `PATCH_LINE_THRESHOLDS.branding` and
|
|
572
|
+
// `PATCH_FILES_THRESHOLDS.branding` above. An explicit `patchTier` opt-in
|
|
573
|
+
// forces branding even when `isBrandingOnlyPatch` cannot reach the patch's
|
|
574
|
+
// actual shape (a branding patch that also touches a non-allowlisted
|
|
575
|
+
// sibling like a vendor-specific icon resource). Both checks read off the
|
|
576
|
+
// same decision so the file-count and line-count rules cannot disagree
|
|
577
|
+
// about which tier applies.
|
|
601
578
|
const decision = resolvePatchSizeTier(filesAffected, patchTier);
|
|
602
579
|
const fileThreshold = decision.tier === 'test'
|
|
603
580
|
? PATCH_FILES_THRESHOLDS.test
|
|
@@ -687,23 +664,14 @@ export async function lintModifiedFileHeaders(repoDir, affectedFiles, newFiles)
|
|
|
687
664
|
* @param affectedFiles - File paths (relative to repoDir) affected by the patch
|
|
688
665
|
* @param diffContent - Raw unified diff string
|
|
689
666
|
* @param config - Project configuration
|
|
690
|
-
* @param
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
* manifest, etc.) can opt out of a specific rule without reaching for the
|
|
695
|
-
* blunt `--skip-lint` hammer. Not mutated by this function.
|
|
696
|
-
* @param patchTier - Optional explicit tier override, threaded from
|
|
697
|
-
* `PatchMetadata.tier`. When `"branding"` forces the branding
|
|
698
|
-
* thresholds on the `large-patch-lines` rule. Callers with a
|
|
699
|
-
* per-patch manifest context (re-export, per-patch lint) should
|
|
700
|
-
* pass this; aggregate-mode callers without a specific patch
|
|
701
|
-
* context skip it and fall through to auto-detection.
|
|
702
|
-
* @param options - Optional behaviour switches; see
|
|
703
|
-
* {@link LintExportedPatchOptions}.
|
|
667
|
+
* @param options - Optional context and behaviour switches; see
|
|
668
|
+
* {@link LintExportedPatchOptions}. Every optional input lives here
|
|
669
|
+
* rather than in a positional tail: four of the five call sites had to
|
|
670
|
+
* pass `undefined` placeholders to reach the one member they wanted.
|
|
704
671
|
* @returns Array of all lint issues found
|
|
705
672
|
*/
|
|
706
|
-
export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config,
|
|
673
|
+
export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config, options) {
|
|
674
|
+
const { patchQueueCtx, ignoreChecks, patchTier } = options ?? {};
|
|
707
675
|
const newFiles = detectNewFilesInDiff(diffContent);
|
|
708
676
|
const { textLines: lineCount } = countNonBinaryDiffLines(diffContent);
|
|
709
677
|
const patchOwnedFiles = resolvePatchOwnedSysMjs(newFiles, patchQueueCtx);
|
|
@@ -6,7 +6,7 @@ export interface PatchDirectoryLockOptions {
|
|
|
6
6
|
* Wait up to this many seconds for the patch directory lock instead of the
|
|
7
7
|
* default ~30 s budget. Enables exponential poll backoff (100 ms → 2 s)
|
|
8
8
|
* and a holder-identified progress line roughly every 5 s. `undefined`
|
|
9
|
-
*
|
|
9
|
+
* keeps the default.
|
|
10
10
|
*/
|
|
11
11
|
waitLockSeconds?: number | undefined;
|
|
12
12
|
/** Command name recorded in the lock's owner metadata for holder diagnostics. */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Filesystem-based lock for serializing patch directory mutations.
|
|
4
4
|
*/
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import { FireForgeError } from '../errors/base.js';
|
|
6
|
+
import { CommandError, FireForgeError } from '../errors/base.js';
|
|
7
7
|
import { PatchError } from '../errors/patch.js';
|
|
8
8
|
import { assert } from '../utils/assert.js';
|
|
9
9
|
import { toError } from '../utils/errors.js';
|
|
@@ -56,7 +56,24 @@ export async function assertPatchDirectoryLockHeld(patchesDir, context) {
|
|
|
56
56
|
export async function withPatchDirectoryLock(patchesDir, operation, options = {}) {
|
|
57
57
|
const lockDir = join(patchesDir, PATCH_DIRECTORY_LOCK);
|
|
58
58
|
const { waitLockSeconds, command } = options;
|
|
59
|
-
|
|
59
|
+
// Tag whatever the BODY throws so the catch below can tell it apart from a
|
|
60
|
+
// lock-acquire/release failure. Narrowing by origin rather than by error
|
|
61
|
+
// shape is what "the rewrap is for lock I/O" actually means: an errno test
|
|
62
|
+
// would still reclassify an EACCES raised while the body writes a .patch
|
|
63
|
+
// file, and a class test cannot see a bare `new Error` from a body at all.
|
|
64
|
+
let bodyFailure;
|
|
65
|
+
let bodyThrew = false;
|
|
66
|
+
const trackedOperation = async () => {
|
|
67
|
+
try {
|
|
68
|
+
return await operation();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
bodyThrew = true;
|
|
72
|
+
bodyFailure = error;
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return withFileLock(lockDir, trackedOperation, {
|
|
60
77
|
...(waitLockSeconds !== undefined
|
|
61
78
|
? {
|
|
62
79
|
timeoutMs: waitLockSeconds * 1000,
|
|
@@ -71,12 +88,12 @@ export async function withPatchDirectoryLock(patchesDir, operation, options = {}
|
|
|
71
88
|
...(command !== undefined
|
|
72
89
|
? { ownerMetadata: [`command=${command}`, `started=${new Date().toISOString()}`] }
|
|
73
90
|
: {}),
|
|
74
|
-
// Reason first, remedy second. The remedy is waiting, not
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
91
|
+
// Reason first, remedy second. The remedy is waiting, not deleting: bulk
|
|
92
|
+
// exports/re-exports legitimately hold this lock for minutes, and
|
|
93
|
+
// FireForge reaps genuinely stale locks on its own — advice to `rm -rf`
|
|
94
|
+
// while a holder is alive destroys a live lock. The leading sentence is
|
|
95
|
+
// a message contract; extend, do not reword. withFileLock appends the
|
|
96
|
+
// holder identification.
|
|
80
97
|
onTimeoutMessage: `Timed out waiting for another FireForge command mutating ${patchesDir}.\n` +
|
|
81
98
|
`Pass --wait-lock [seconds] to wait longer (bare --wait-lock waits up to 60 seconds). ` +
|
|
82
99
|
`FireForge removes genuinely stale locks automatically; only if no other fireforge ` +
|
|
@@ -84,9 +101,28 @@ export async function withPatchDirectoryLock(patchesDir, operation, options = {}
|
|
|
84
101
|
onStaleLockMessage: (ageMs) => `Removing stale patch lock (age: ${Math.round(ageMs / 1000)}s). ` +
|
|
85
102
|
'A previous fireforge process may have crashed.',
|
|
86
103
|
}).catch((error) => {
|
|
104
|
+
// The body's own failures are never ours to reclassify. Rewrapping them
|
|
105
|
+
// reports an EACCES writing patches.json, a postcondition failure from
|
|
106
|
+
// renumberPatchesInManifest, or a plain TypeError from a FireForge bug
|
|
107
|
+
// as a "Patch Error" carrying three fixed remedies about Firefox-version
|
|
108
|
+
// compatibility and `fireforge reset` — none of which apply. Worse,
|
|
109
|
+
// src/cli.ts prints a stack only for non-FireForgeError throwables, so
|
|
110
|
+
// the rewrap moves real bugs into the branch that discards their stack.
|
|
111
|
+
if (bodyThrew && error === bodyFailure) {
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
87
114
|
if (error instanceof FireForgeError) {
|
|
88
115
|
throw error;
|
|
89
116
|
}
|
|
90
|
-
|
|
117
|
+
// CommandError is the one class in src/errors/ that does NOT extend
|
|
118
|
+
// FireForgeError: it is the sentinel carrying an already-rendered exit
|
|
119
|
+
// code to the entrypoint. The `instanceof FireForgeError` guard above
|
|
120
|
+
// misses it, so without this the exit code was replaced by PATCH_ERROR.
|
|
121
|
+
if (error instanceof CommandError) {
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
// What remains is a genuine failure to acquire or release the lock.
|
|
125
|
+
const cause = toError(error);
|
|
126
|
+
throw new PatchError(cause.message, undefined, cause);
|
|
91
127
|
});
|
|
92
128
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Manifest consistency checks and rebuild/recovery operations.
|
|
3
3
|
*/
|
|
4
4
|
import type { PatchesManifest } from '../types/commands/index.js';
|
|
5
|
+
import type { PatchDirectoryLockOptions } from './patch-lock.js';
|
|
5
6
|
/** Consistency issue codes for manifest validation. */
|
|
6
7
|
export interface PatchManifestConsistencyIssue {
|
|
7
8
|
code: 'manifest-invalid' | 'manifest-missing' | 'missing-patch-file' | 'untracked-patch-file' | 'files-affected-mismatch' | 'duplicate-manifest-entry';
|
|
@@ -41,4 +42,4 @@ export interface RebuildPatchesManifestResult {
|
|
|
41
42
|
* @returns {@link RebuildPatchesManifestResult} — the persisted manifest
|
|
42
43
|
* plus the filenames that were reconstructed from generic defaults.
|
|
43
44
|
*/
|
|
44
|
-
export declare function rebuildPatchesManifest(patchesDir: string, fallbackSourceEsrVersion: string): Promise<RebuildPatchesManifestResult>;
|
|
45
|
+
export declare function rebuildPatchesManifest(patchesDir: string, fallbackSourceEsrVersion: string, lockOptions?: PatchDirectoryLockOptions): Promise<RebuildPatchesManifestResult>;
|
|
@@ -89,13 +89,13 @@ export async function validatePatchesManifestConsistency(patchesDir) {
|
|
|
89
89
|
* @returns {@link RebuildPatchesManifestResult} — the persisted manifest
|
|
90
90
|
* plus the filenames that were reconstructed from generic defaults.
|
|
91
91
|
*/
|
|
92
|
-
export async function rebuildPatchesManifest(patchesDir, fallbackSourceEsrVersion) {
|
|
92
|
+
export async function rebuildPatchesManifest(patchesDir, fallbackSourceEsrVersion, lockOptions = {}) {
|
|
93
93
|
// The whole read → discover → rebuild → save cycle runs under the shared
|
|
94
94
|
// patch-directory lock (invariant 2, docs/lifecycle-invariants.md):
|
|
95
|
-
// manifest writes serialize on this lock,
|
|
96
|
-
// concurrent export/reorder
|
|
97
|
-
//
|
|
98
|
-
return withPatchDirectoryLock(patchesDir, () => rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVersion));
|
|
95
|
+
// manifest writes serialize on this lock, so without it a repair racing a
|
|
96
|
+
// concurrent export/reorder can clobber the other writer's manifest. Not
|
|
97
|
+
// reentrant — callers must not already hold it.
|
|
98
|
+
return withPatchDirectoryLock(patchesDir, () => rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVersion), lockOptions);
|
|
99
99
|
}
|
|
100
100
|
async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVersion) {
|
|
101
101
|
const manifestState = await loadPatchesManifestState(patchesDir);
|
|
@@ -113,11 +113,10 @@ async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVers
|
|
|
113
113
|
}, 0);
|
|
114
114
|
let nextRecoveredOrder = highestFiniteOrder + 1;
|
|
115
115
|
// Prefetch the two independent per-patch reads concurrently. This runs
|
|
116
|
-
// while the patch-directory lock is held, and
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
// ordinals.
|
|
116
|
+
// while the patch-directory lock is held, and as two serialised awaits per
|
|
117
|
+
// patch it costs 58 round-trips on a 29-patch queue. The fold below stays
|
|
118
|
+
// sequential because `nextRecoveredOrder` is loop-carried: parallelising
|
|
119
|
+
// the assignment would scramble the recovered ordinals.
|
|
121
120
|
const prefetched = await Promise.all(patches.map(async (patch) => ({
|
|
122
121
|
filesAffected: normalizeFiles(await getAllTargetFilesFromPatch(patch.path)),
|
|
123
122
|
patchStats: await stat(patch.path),
|
|
@@ -131,25 +130,24 @@ async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVers
|
|
|
131
130
|
const inferred = inferPatchMetadataFromFilename(patch.filename);
|
|
132
131
|
const recoveredOrder = Number.isFinite(patch.order) ? patch.order : nextRecoveredOrder++;
|
|
133
132
|
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.
|
|
133
|
+
// Track every filename that had no pre-existing manifest entry so
|
|
134
|
+
// callers can warn the operator per-patch. A missing entry means every
|
|
135
|
+
// descriptive field (`description`, `createdAt`, `category`) was
|
|
136
|
+
// invented rather than preserved, and FireForge patch files carry no
|
|
137
|
+
// header metadata that could carry a human description forward — so
|
|
138
|
+
// visibility is the best available, and silent overwrites of
|
|
139
|
+
// human-written descriptions during a recovery run are the failure to
|
|
140
|
+
// avoid.
|
|
143
141
|
recoveredFilenames.push(patch.filename);
|
|
144
142
|
}
|
|
145
|
-
// Preserve optional fields the operator declared on the existing
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
// `doctor --repair-patches-manifest` run silently strips both
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
143
|
+
// Preserve optional fields the operator declared on the existing entry —
|
|
144
|
+
// `lintIgnore` (per-patch lint suppression) and `tier` (explicit
|
|
145
|
+
// branding-threshold override). Without this, a
|
|
146
|
+
// `doctor --repair-patches-manifest` run silently strips both from every
|
|
147
|
+
// entry that had them, and the next `lint` or `re-export` pass fires
|
|
148
|
+
// rules the operator had intentionally quieted. Mirrors how other
|
|
149
|
+
// descriptive fields fall back to existing values when the entry is
|
|
150
|
+
// known.
|
|
153
151
|
const rebuilt = {
|
|
154
152
|
filename: patch.filename,
|
|
155
153
|
order: recoveredOrder,
|
|
@@ -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)) {
|