@hominis/fireforge 0.46.0 → 0.46.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 +381 -498
- package/dist/bin/fireforge.d.ts +1 -1
- package/dist/bin/fireforge.js +17 -17
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +9 -9
- package/dist/src/cli-usage-error.js +12 -12
- package/dist/src/cli.d.ts +3 -2
- package/dist/src/cli.js +29 -28
- package/dist/src/commands/bootstrap-checks.js +2 -1
- package/dist/src/commands/bootstrap.js +2 -2
- package/dist/src/commands/build.js +12 -11
- package/dist/src/commands/config.js +9 -9
- package/dist/src/commands/destructive-decision.d.ts +4 -4
- package/dist/src/commands/destructive-decision.js +4 -4
- package/dist/src/commands/discard.js +5 -5
- package/dist/src/commands/doctor-check-core.d.ts +4 -4
- package/dist/src/commands/doctor-external-toolchains.js +2 -2
- package/dist/src/commands/doctor-furnace-config-sync.d.ts +2 -2
- package/dist/src/commands/doctor-furnace-config-sync.js +20 -20
- package/dist/src/commands/doctor-furnace-jar.d.ts +1 -1
- package/dist/src/commands/doctor-furnace-jar.js +1 -1
- package/dist/src/commands/doctor-furnace.js +22 -22
- package/dist/src/commands/doctor-orphaned-harness.d.ts +10 -11
- package/dist/src/commands/doctor-orphaned-harness.js +10 -11
- package/dist/src/commands/doctor-patch-manifest.d.ts +2 -1
- package/dist/src/commands/doctor-patch-manifest.js +7 -6
- package/dist/src/commands/doctor-post-rebase-audit.js +2 -2
- package/dist/src/commands/doctor-source-pin.d.ts +12 -12
- package/dist/src/commands/doctor-source-pin.js +14 -14
- package/dist/src/commands/doctor-working-tree.d.ts +3 -3
- package/dist/src/commands/doctor-working-tree.js +3 -3
- package/dist/src/commands/doctor.d.ts +2 -2
- package/dist/src/commands/doctor.js +9 -9
- package/dist/src/commands/download.js +15 -13
- package/dist/src/commands/export-all.js +8 -8
- package/dist/src/commands/export-flow.d.ts +5 -5
- package/dist/src/commands/export-flow.js +14 -14
- package/dist/src/commands/export-placement-conflicts.d.ts +3 -3
- package/dist/src/commands/export-placement-conflicts.js +9 -9
- package/dist/src/commands/export-placement-gate.d.ts +2 -2
- package/dist/src/commands/export-placement-gate.js +5 -5
- package/dist/src/commands/export-placement-policy.d.ts +1 -1
- package/dist/src/commands/export-placement-policy.js +1 -1
- package/dist/src/commands/export-shared.d.ts +10 -10
- package/dist/src/commands/export-shared.js +13 -13
- package/dist/src/commands/export.js +4 -4
- package/dist/src/commands/furnace/apply.js +16 -16
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +1 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +14 -14
- package/dist/src/commands/furnace/chrome-doc-templates.js +17 -17
- package/dist/src/commands/furnace/chrome-doc-tests.d.ts +2 -2
- package/dist/src/commands/furnace/chrome-doc-tests.js +4 -4
- package/dist/src/commands/furnace/chrome-doc.d.ts +8 -8
- package/dist/src/commands/furnace/chrome-doc.js +18 -18
- package/dist/src/commands/furnace/create-browser-test.d.ts +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +4 -4
- package/dist/src/commands/furnace/create-dry-run.d.ts +4 -4
- package/dist/src/commands/furnace/create-dry-run.js +4 -4
- package/dist/src/commands/furnace/create-features.d.ts +2 -2
- package/dist/src/commands/furnace/create-features.js +2 -2
- package/dist/src/commands/furnace/create-mochikit.d.ts +4 -4
- package/dist/src/commands/furnace/create-mochikit.js +7 -7
- package/dist/src/commands/furnace/create-templates.d.ts +14 -14
- package/dist/src/commands/furnace/create-templates.js +18 -18
- package/dist/src/commands/furnace/create-xpcshell.d.ts +5 -5
- package/dist/src/commands/furnace/create-xpcshell.js +8 -8
- package/dist/src/commands/furnace/create.d.ts +1 -1
- package/dist/src/commands/furnace/create.js +10 -10
- package/dist/src/commands/furnace/deploy.js +4 -4
- package/dist/src/commands/furnace/diff.js +7 -7
- package/dist/src/commands/furnace/index.js +1 -1
- package/dist/src/commands/furnace/init.js +15 -15
- package/dist/src/commands/furnace/list.js +2 -2
- package/dist/src/commands/furnace/override.d.ts +1 -1
- package/dist/src/commands/furnace/override.js +15 -15
- package/dist/src/commands/furnace/preview.js +17 -17
- package/dist/src/commands/furnace/refresh.js +6 -6
- package/dist/src/commands/furnace/remove-mutations.d.ts +3 -3
- package/dist/src/commands/furnace/remove-mutations.js +19 -18
- package/dist/src/commands/furnace/remove-state.d.ts +1 -1
- package/dist/src/commands/furnace/remove-state.js +1 -1
- package/dist/src/commands/furnace/remove.js +2 -2
- package/dist/src/commands/furnace/rename-browser-test.js +9 -8
- package/dist/src/commands/furnace/rename-xpcshell.d.ts +2 -2
- package/dist/src/commands/furnace/rename-xpcshell.js +3 -3
- package/dist/src/commands/furnace/rename.js +9 -9
- package/dist/src/commands/furnace/scan.js +3 -3
- package/dist/src/commands/furnace/status.js +7 -7
- package/dist/src/commands/furnace/sync.js +6 -6
- package/dist/src/commands/furnace/test-file-name.d.ts +2 -2
- package/dist/src/commands/furnace/test-file-name.js +2 -2
- package/dist/src/commands/furnace/validate.js +11 -12
- package/dist/src/commands/import.js +20 -20
- package/dist/src/commands/lint-per-patch.js +8 -8
- package/dist/src/commands/lint-per-run-checkjs.d.ts +12 -13
- package/dist/src/commands/lint-per-run-checkjs.js +14 -15
- package/dist/src/commands/lint-report.d.ts +3 -3
- package/dist/src/commands/lint-report.js +4 -4
- package/dist/src/commands/lint.d.ts +3 -3
- package/dist/src/commands/lint.js +35 -35
- package/dist/src/commands/manifest.d.ts +4 -4
- package/dist/src/commands/package.js +2 -2
- package/dist/src/commands/patch/compact.d.ts +2 -2
- package/dist/src/commands/patch/compact.js +8 -8
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +8 -8
- package/dist/src/commands/patch/lint-ignore.d.ts +3 -4
- package/dist/src/commands/patch/lint-ignore.js +14 -15
- package/dist/src/commands/patch/move-files-create.d.ts +2 -2
- package/dist/src/commands/patch/move-files-create.js +5 -5
- package/dist/src/commands/patch/move-files-into.d.ts +2 -2
- package/dist/src/commands/patch/move-files-into.js +3 -3
- package/dist/src/commands/patch/move-files.js +1 -1
- package/dist/src/commands/patch/patch-context.d.ts +1 -1
- package/dist/src/commands/patch/rename.d.ts +4 -4
- package/dist/src/commands/patch/rename.js +8 -8
- package/dist/src/commands/patch/reorder.js +10 -10
- package/dist/src/commands/patch/split-plan.d.ts +3 -3
- package/dist/src/commands/patch/split-plan.js +6 -5
- package/dist/src/commands/patch/split.d.ts +2 -2
- package/dist/src/commands/patch/split.js +5 -5
- package/dist/src/commands/patch/staged-dependency-validate.d.ts +1 -1
- package/dist/src/commands/patch/staged-dependency-validate.js +3 -3
- package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
- package/dist/src/commands/patch/staged-dependency.js +6 -6
- package/dist/src/commands/patch/tier.d.ts +2 -2
- package/dist/src/commands/patch/tier.js +5 -5
- package/dist/src/commands/patch-rename-map.d.ts +1 -1
- package/dist/src/commands/patch-rename-map.js +1 -1
- package/dist/src/commands/pending-resolution.d.ts +5 -5
- package/dist/src/commands/pending-resolution.js +5 -5
- package/dist/src/commands/re-export-adjacent.d.ts +3 -3
- package/dist/src/commands/re-export-adjacent.js +8 -8
- package/dist/src/commands/re-export-bulk-scan.d.ts +3 -3
- package/dist/src/commands/re-export-bulk-scan.js +17 -17
- package/dist/src/commands/re-export-drift.d.ts +10 -10
- package/dist/src/commands/re-export-drift.js +29 -29
- package/dist/src/commands/re-export-files.js +14 -15
- package/dist/src/commands/re-export-lint.d.ts +26 -26
- package/dist/src/commands/re-export-lint.js +27 -27
- package/dist/src/commands/re-export-options.d.ts +1 -1
- package/dist/src/commands/re-export-options.js +1 -1
- package/dist/src/commands/re-export-register.d.ts +1 -1
- package/dist/src/commands/re-export-register.js +1 -1
- package/dist/src/commands/re-export-scan.d.ts +1 -1
- package/dist/src/commands/re-export-scan.js +7 -7
- package/dist/src/commands/re-export-single.d.ts +1 -1
- package/dist/src/commands/re-export-single.js +20 -20
- package/dist/src/commands/re-export.js +7 -6
- package/dist/src/commands/rebase/abort.js +6 -6
- package/dist/src/commands/rebase/continue.js +8 -7
- package/dist/src/commands/rebase/index.d.ts +1 -1
- package/dist/src/commands/rebase/index.js +3 -3
- package/dist/src/commands/rebase/patch-loop.js +18 -17
- package/dist/src/commands/register.js +5 -5
- package/dist/src/commands/reset.js +1 -1
- package/dist/src/commands/resolve.d.ts +2 -2
- package/dist/src/commands/resolve.js +3 -3
- package/dist/src/commands/run.js +26 -26
- package/dist/src/commands/setup-support.js +4 -4
- package/dist/src/commands/setup.js +1 -1
- package/dist/src/commands/source.js +1 -1
- package/dist/src/commands/status-check.d.ts +3 -3
- package/dist/src/commands/status-check.js +4 -4
- package/dist/src/commands/status-json.d.ts +14 -13
- package/dist/src/commands/status-json.js +9 -8
- package/dist/src/commands/status-output.d.ts +3 -3
- package/dist/src/commands/status-output.js +3 -3
- package/dist/src/commands/status-ownership.d.ts +3 -3
- package/dist/src/commands/status-ownership.js +3 -3
- package/dist/src/commands/status.js +22 -22
- package/dist/src/commands/test-build-phase.d.ts +1 -1
- package/dist/src/commands/test-build-phase.js +17 -17
- package/dist/src/commands/test-diagnose.d.ts +1 -1
- package/dist/src/commands/test-diagnose.js +20 -20
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +4 -4
- package/dist/src/commands/test-run.d.ts +11 -11
- package/dist/src/commands/test-run.js +15 -15
- package/dist/src/commands/test-stale-gate.d.ts +12 -12
- package/dist/src/commands/test-stale-gate.js +13 -13
- package/dist/src/commands/test-verdict.d.ts +6 -6
- package/dist/src/commands/test-verdict.js +11 -11
- package/dist/src/commands/test.d.ts +3 -3
- package/dist/src/commands/test.js +36 -36
- package/dist/src/commands/token-coverage.js +3 -3
- package/dist/src/commands/token-list.js +5 -5
- package/dist/src/commands/token.js +9 -9
- package/dist/src/commands/tree.js +19 -19
- package/dist/src/commands/typecheck.d.ts +6 -6
- package/dist/src/commands/typecheck.js +7 -7
- package/dist/src/commands/verify.d.ts +3 -3
- package/dist/src/commands/verify.js +10 -10
- package/dist/src/commands/watch.js +11 -11
- package/dist/src/commands/wire.js +13 -13
- package/dist/src/core/ast-utils.d.ts +7 -7
- package/dist/src/core/ast-utils.js +8 -8
- package/dist/src/core/branding.d.ts +5 -5
- package/dist/src/core/branding.js +3 -3
- package/dist/src/core/browser-wire.d.ts +1 -1
- package/dist/src/core/browser-wire.js +4 -4
- package/dist/src/core/build-audit-platform.d.ts +6 -6
- package/dist/src/core/build-audit-platform.js +26 -26
- package/dist/src/core/build-audit-registration.d.ts +5 -5
- package/dist/src/core/build-audit-registration.js +7 -7
- package/dist/src/core/build-audit-resolve.d.ts +5 -5
- package/dist/src/core/build-audit-resolve.js +6 -6
- package/dist/src/core/build-audit-skip.d.ts +8 -8
- package/dist/src/core/build-audit-skip.js +12 -12
- package/dist/src/core/build-audit-transforms.d.ts +2 -2
- package/dist/src/core/build-audit-transforms.js +3 -3
- package/dist/src/core/build-audit.d.ts +8 -8
- package/dist/src/core/build-audit.js +39 -37
- package/dist/src/core/build-baseline-types.d.ts +19 -18
- package/dist/src/core/build-baseline-types.js +1 -1
- package/dist/src/core/build-baseline.d.ts +8 -8
- package/dist/src/core/build-baseline.js +14 -14
- package/dist/src/core/build-jar-escalation.d.ts +8 -8
- package/dist/src/core/build-jar-escalation.js +15 -15
- package/dist/src/core/build-overwrite-guard.d.ts +4 -4
- package/dist/src/core/build-overwrite-guard.js +13 -13
- package/dist/src/core/build-prepare.d.ts +4 -4
- package/dist/src/core/build-prepare.js +16 -15
- package/dist/src/core/config-mutate.js +4 -4
- package/dist/src/core/config-state.js +2 -2
- package/dist/src/core/config-validate-build-audit.js +3 -3
- package/dist/src/core/config-validate-file-size.d.ts +1 -1
- package/dist/src/core/config-validate-file-size.js +2 -2
- package/dist/src/core/config-validate.js +8 -9
- package/dist/src/core/config.d.ts +10 -10
- package/dist/src/core/config.js +10 -10
- package/dist/src/core/coverage-extend.d.ts +16 -16
- package/dist/src/core/coverage-extend.js +16 -16
- package/dist/src/core/destructive.d.ts +14 -14
- package/dist/src/core/destructive.js +9 -9
- package/dist/src/core/diff-hunks.d.ts +2 -2
- package/dist/src/core/diff-hunks.js +7 -7
- package/dist/src/core/discard-baseline.d.ts +10 -10
- package/dist/src/core/discard-baseline.js +15 -16
- package/dist/src/core/display-state.d.ts +11 -11
- package/dist/src/core/display-state.js +12 -12
- package/dist/src/core/engine-changes.d.ts +2 -2
- package/dist/src/core/engine-changes.js +2 -2
- package/dist/src/core/engine-precondition.d.ts +4 -4
- package/dist/src/core/engine-precondition.js +2 -2
- package/dist/src/core/engine-session-lock.d.ts +18 -18
- package/dist/src/core/engine-session-lock.js +37 -37
- package/dist/src/core/export-moved-code-shape.d.ts +7 -7
- package/dist/src/core/export-moved-code-shape.js +8 -8
- package/dist/src/core/file-lock-owner.d.ts +11 -11
- package/dist/src/core/file-lock-owner.js +18 -18
- package/dist/src/core/file-lock.d.ts +12 -12
- package/dist/src/core/file-lock.js +52 -39
- package/dist/src/core/firefox-cache.d.ts +1 -1
- package/dist/src/core/firefox-cache.js +10 -10
- package/dist/src/core/firefox-download.d.ts +3 -3
- package/dist/src/core/firefox-download.js +6 -6
- package/dist/src/core/firefox-extract.d.ts +2 -2
- package/dist/src/core/firefox-extract.js +13 -13
- package/dist/src/core/firefox.d.ts +5 -5
- package/dist/src/core/firefox.js +8 -8
- package/dist/src/core/furnace-apply-dry-run.js +1 -1
- package/dist/src/core/furnace-apply-ftl.d.ts +5 -5
- package/dist/src/core/furnace-apply-ftl.js +8 -8
- package/dist/src/core/furnace-apply-helpers.d.ts +9 -9
- package/dist/src/core/furnace-apply-helpers.js +21 -21
- package/dist/src/core/furnace-apply-output.js +1 -1
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +9 -9
- package/dist/src/core/furnace-apply-overwrite-warn.js +9 -9
- package/dist/src/core/furnace-apply-undeploy-actions.d.ts +1 -1
- package/dist/src/core/furnace-apply-undeploy-actions.js +1 -1
- package/dist/src/core/furnace-apply.d.ts +3 -3
- package/dist/src/core/furnace-apply.js +22 -21
- package/dist/src/core/furnace-checksum-utils.d.ts +5 -5
- package/dist/src/core/furnace-checksum-utils.js +5 -5
- package/dist/src/core/furnace-config-custom.d.ts +1 -1
- package/dist/src/core/furnace-config-custom.js +2 -2
- package/dist/src/core/furnace-config-order.js +1 -1
- package/dist/src/core/furnace-config-tokens.d.ts +3 -3
- package/dist/src/core/furnace-config-tokens.js +3 -3
- package/dist/src/core/furnace-config-validate.js +2 -2
- package/dist/src/core/furnace-config.d.ts +4 -4
- package/dist/src/core/furnace-config.js +18 -18
- package/dist/src/core/furnace-constants.d.ts +6 -6
- package/dist/src/core/furnace-constants.js +6 -6
- package/dist/src/core/furnace-css-fragments.d.ts +6 -6
- package/dist/src/core/furnace-css-fragments.js +11 -11
- package/dist/src/core/furnace-dir-entry.d.ts +1 -1
- package/dist/src/core/furnace-dir-entry.js +1 -1
- package/dist/src/core/furnace-jsconfig.d.ts +7 -7
- package/dist/src/core/furnace-jsconfig.js +22 -20
- package/dist/src/core/furnace-operation.d.ts +21 -20
- package/dist/src/core/furnace-operation.js +36 -34
- package/dist/src/core/furnace-precondition.d.ts +10 -10
- package/dist/src/core/furnace-precondition.js +8 -8
- package/dist/src/core/furnace-refresh.js +2 -2
- package/dist/src/core/furnace-registration-ast.d.ts +2 -2
- package/dist/src/core/furnace-registration-ast.js +9 -9
- package/dist/src/core/furnace-registration-remove.d.ts +4 -4
- package/dist/src/core/furnace-registration-remove.js +6 -6
- package/dist/src/core/furnace-registration-validate.d.ts +2 -2
- package/dist/src/core/furnace-registration-validate.js +2 -2
- package/dist/src/core/furnace-registration.d.ts +5 -5
- package/dist/src/core/furnace-registration.js +7 -7
- package/dist/src/core/furnace-rollback.d.ts +1 -1
- package/dist/src/core/furnace-rollback.js +5 -5
- package/dist/src/core/furnace-stale-export.d.ts +17 -16
- package/dist/src/core/furnace-stale-export.js +16 -15
- package/dist/src/core/furnace-staleness.d.ts +2 -2
- package/dist/src/core/furnace-staleness.js +2 -2
- package/dist/src/core/furnace-state-persist.d.ts +10 -10
- package/dist/src/core/furnace-state-persist.js +10 -10
- package/dist/src/core/furnace-step-errors.d.ts +6 -6
- package/dist/src/core/furnace-step-errors.js +6 -6
- package/dist/src/core/furnace-validate-accessibility.js +14 -14
- package/dist/src/core/furnace-validate-compatibility.js +9 -9
- package/dist/src/core/furnace-validate-registration.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +13 -13
- package/dist/src/core/furnace-validate-structure.d.ts +1 -1
- package/dist/src/core/furnace-validate-structure.js +4 -4
- package/dist/src/core/furnace-validate.js +9 -9
- package/dist/src/core/furnace-version-drift.d.ts +5 -5
- package/dist/src/core/furnace-version-drift.js +5 -5
- package/dist/src/core/git-base.d.ts +4 -4
- package/dist/src/core/git-base.js +9 -9
- package/dist/src/core/git-diff-new-file.d.ts +4 -4
- package/dist/src/core/git-diff-new-file.js +8 -8
- package/dist/src/core/git-diff.d.ts +4 -4
- package/dist/src/core/git-diff.js +35 -34
- package/dist/src/core/git-file-ops.d.ts +6 -6
- package/dist/src/core/git-file-ops.js +7 -7
- package/dist/src/core/git-readonly-index.d.ts +12 -12
- package/dist/src/core/git-readonly-index.js +12 -12
- package/dist/src/core/git-status.d.ts +4 -4
- package/dist/src/core/git-status.js +4 -4
- package/dist/src/core/git.d.ts +3 -3
- package/dist/src/core/git.js +14 -14
- package/dist/src/core/harness-orphans.d.ts +17 -17
- package/dist/src/core/harness-orphans.js +20 -20
- package/dist/src/core/history-log.d.ts +3 -3
- package/dist/src/core/history-log.js +3 -3
- package/dist/src/core/license-headers.d.ts +19 -18
- package/dist/src/core/license-headers.js +29 -28
- package/dist/src/core/mach-build-artifacts.d.ts +21 -21
- package/dist/src/core/mach-build-artifacts.js +23 -23
- package/dist/src/core/mach-error-hints.d.ts +5 -5
- package/dist/src/core/mach-error-hints.js +13 -13
- package/dist/src/core/mach-known-noise-filter.d.ts +41 -38
- package/dist/src/core/mach-known-noise-filter.js +53 -50
- package/dist/src/core/mach-mozconfig.d.ts +6 -6
- package/dist/src/core/mach-mozconfig.js +15 -15
- package/dist/src/core/mach-python.d.ts +2 -1
- package/dist/src/core/mach-python.js +4 -3
- package/dist/src/core/mach-resource-shim.d.ts +12 -12
- package/dist/src/core/mach-resource-shim.js +20 -20
- package/dist/src/core/mach.d.ts +23 -23
- package/dist/src/core/mach.js +36 -36
- package/dist/src/core/marionette-port.d.ts +15 -13
- package/dist/src/core/marionette-port.js +34 -31
- package/dist/src/core/marionette-preflight.d.ts +6 -6
- package/dist/src/core/marionette-preflight.js +23 -23
- package/dist/src/core/markdown-table.d.ts +9 -9
- package/dist/src/core/markdown-table.js +14 -14
- package/dist/src/core/mochitest-server-port.d.ts +8 -8
- package/dist/src/core/mochitest-server-port.js +14 -14
- package/dist/src/core/module-resolution-preflight.d.ts +8 -8
- package/dist/src/core/module-resolution-preflight.js +11 -11
- package/dist/src/core/moz-manifest-helpers.d.ts +7 -7
- package/dist/src/core/moz-manifest-helpers.js +7 -7
- package/dist/src/core/moz-manifest-register.d.ts +1 -1
- package/dist/src/core/moz-manifest-register.js +2 -2
- package/dist/src/core/moz-manifest-rules.js +11 -11
- package/dist/src/core/moz-manifest-tokenizers.js +1 -1
- package/dist/src/core/ownership-table.d.ts +5 -5
- package/dist/src/core/ownership-table.js +7 -7
- package/dist/src/core/parser-fallback.d.ts +3 -3
- package/dist/src/core/parser-fallback.js +3 -3
- package/dist/src/core/patch-apply-fuzz.d.ts +5 -5
- package/dist/src/core/patch-apply-fuzz.js +9 -9
- package/dist/src/core/patch-apply.d.ts +9 -9
- package/dist/src/core/patch-apply.js +30 -30
- package/dist/src/core/patch-export-update.d.ts +4 -4
- package/dist/src/core/patch-export-update.js +3 -3
- package/dist/src/core/patch-export.d.ts +10 -10
- package/dist/src/core/patch-export.js +13 -13
- package/dist/src/core/patch-identifier-suggest.d.ts +1 -1
- package/dist/src/core/patch-identifier-suggest.js +4 -4
- package/dist/src/core/patch-lint-binary.d.ts +7 -7
- package/dist/src/core/patch-lint-cache.d.ts +2 -2
- package/dist/src/core/patch-lint-cache.js +13 -13
- package/dist/src/core/patch-lint-checkjs.d.ts +26 -26
- package/dist/src/core/patch-lint-checkjs.js +42 -41
- package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +8 -9
- package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -11
- package/dist/src/core/patch-lint-creators.d.ts +3 -3
- package/dist/src/core/patch-lint-creators.js +3 -3
- package/dist/src/core/patch-lint-cross.d.ts +13 -13
- package/dist/src/core/patch-lint-cross.js +21 -21
- package/dist/src/core/patch-lint-css.d.ts +1 -1
- package/dist/src/core/patch-lint-css.js +5 -5
- package/dist/src/core/patch-lint-diff-tag.d.ts +5 -5
- package/dist/src/core/patch-lint-diff-tag.js +5 -5
- package/dist/src/core/patch-lint-diff.d.ts +4 -4
- package/dist/src/core/patch-lint-diff.js +4 -4
- package/dist/src/core/patch-lint-file-size.d.ts +3 -3
- package/dist/src/core/patch-lint-file-size.js +3 -3
- package/dist/src/core/patch-lint-forward-registration.d.ts +4 -4
- package/dist/src/core/patch-lint-forward-registration.js +12 -12
- package/dist/src/core/patch-lint-jsdoc.d.ts +2 -2
- package/dist/src/core/patch-lint-jsdoc.js +11 -11
- package/dist/src/core/patch-lint-module-registration.d.ts +1 -1
- package/dist/src/core/patch-lint-module-registration.js +1 -1
- package/dist/src/core/patch-lint-mozbuild.d.ts +1 -1
- package/dist/src/core/patch-lint-mozbuild.js +10 -10
- package/dist/src/core/patch-lint-observer.d.ts +3 -3
- package/dist/src/core/patch-lint-observer.js +7 -7
- package/dist/src/core/patch-lint-ownership.d.ts +6 -6
- package/dist/src/core/patch-lint-ownership.js +6 -6
- package/dist/src/core/patch-lint-prettier.d.ts +6 -6
- package/dist/src/core/patch-lint-prettier.js +6 -6
- package/dist/src/core/patch-lint-queue-types.d.ts +1 -1
- package/dist/src/core/patch-lint-queue-types.js +1 -1
- package/dist/src/core/patch-lint-staged-registration.d.ts +2 -2
- package/dist/src/core/patch-lint-staged-registration.js +6 -6
- package/dist/src/core/patch-lint-unmanaged-companion.d.ts +23 -23
- package/dist/src/core/patch-lint-unmanaged-companion.js +26 -26
- package/dist/src/core/patch-lint.d.ts +27 -26
- package/dist/src/core/patch-lint.js +48 -46
- package/dist/src/core/patch-lock.d.ts +1 -1
- package/dist/src/core/patch-lock.js +8 -8
- package/dist/src/core/patch-manifest-consistency.d.ts +7 -7
- package/dist/src/core/patch-manifest-consistency.js +16 -17
- package/dist/src/core/patch-manifest-files-affected.d.ts +1 -1
- package/dist/src/core/patch-manifest-files-affected.js +3 -3
- package/dist/src/core/patch-manifest-io.d.ts +14 -14
- package/dist/src/core/patch-manifest-io.js +27 -27
- package/dist/src/core/patch-manifest-query.d.ts +1 -1
- package/dist/src/core/patch-manifest-query.js +4 -4
- package/dist/src/core/patch-manifest-resolve.d.ts +5 -5
- package/dist/src/core/patch-manifest-resolve.js +7 -7
- package/dist/src/core/patch-manifest-validate.js +3 -3
- package/dist/src/core/patch-manifest.d.ts +2 -2
- package/dist/src/core/patch-manifest.js +2 -2
- package/dist/src/core/patch-parse.d.ts +14 -14
- package/dist/src/core/patch-parse.js +16 -16
- package/dist/src/core/patch-registration-refs.d.ts +4 -4
- package/dist/src/core/patch-registration-refs.js +7 -7
- package/dist/src/core/patch-transform.d.ts +8 -8
- package/dist/src/core/patch-transform.js +13 -13
- package/dist/src/core/process-cpu-time.d.ts +1 -1
- package/dist/src/core/process-cpu-time.js +5 -5
- package/dist/src/core/rebase-session.d.ts +7 -7
- package/dist/src/core/rebase-session.js +8 -8
- package/dist/src/core/register-result.d.ts +1 -1
- package/dist/src/core/register-result.js +1 -1
- package/dist/src/core/register-scaffold.d.ts +2 -2
- package/dist/src/core/register-scaffold.js +3 -3
- package/dist/src/core/register-shared-css.d.ts +3 -3
- package/dist/src/core/register-shared-css.js +4 -4
- package/dist/src/core/register-xpcshell-test.d.ts +2 -2
- package/dist/src/core/register-xpcshell-test.js +2 -2
- package/dist/src/core/run-log-redact.d.ts +12 -12
- package/dist/src/core/run-log-redact.js +16 -16
- package/dist/src/core/run-log.d.ts +9 -9
- package/dist/src/core/run-log.js +14 -14
- package/dist/src/core/shared-ftl.d.ts +2 -2
- package/dist/src/core/shared-ftl.js +6 -6
- package/dist/src/core/signal-critical.d.ts +2 -2
- package/dist/src/core/signal-critical.js +2 -2
- package/dist/src/core/smoke-patterns.d.ts +5 -5
- package/dist/src/core/smoke-patterns.js +9 -9
- package/dist/src/core/state-file.d.ts +3 -3
- package/dist/src/core/status-binary.d.ts +8 -8
- package/dist/src/core/status-binary.js +12 -12
- package/dist/src/core/status-classify.d.ts +17 -17
- package/dist/src/core/status-classify.js +11 -11
- package/dist/src/core/test-changed-prefs.d.ts +12 -12
- package/dist/src/core/test-changed-prefs.js +15 -15
- package/dist/src/core/test-harness-crash.d.ts +39 -39
- package/dist/src/core/test-harness-crash.js +67 -67
- package/dist/src/core/test-harness-output.d.ts +7 -7
- package/dist/src/core/test-harness-output.js +7 -7
- package/dist/src/core/test-harness-verdict-notes.d.ts +13 -14
- package/dist/src/core/test-harness-verdict-notes.js +4 -4
- package/dist/src/core/test-path-scope.d.ts +8 -8
- package/dist/src/core/test-path-scope.js +11 -11
- package/dist/src/core/test-stale-check.d.ts +22 -21
- package/dist/src/core/test-stale-check.js +29 -28
- package/dist/src/core/test-stale-symlink.d.ts +1 -1
- package/dist/src/core/test-stale-symlink.js +1 -1
- package/dist/src/core/test-stall-triage.d.ts +9 -9
- package/dist/src/core/test-stall-triage.js +22 -22
- package/dist/src/core/token-category.d.ts +4 -4
- package/dist/src/core/token-category.js +13 -12
- package/dist/src/core/token-coverage.js +4 -5
- package/dist/src/core/token-dark-mode.d.ts +9 -9
- package/dist/src/core/token-dark-mode.js +11 -11
- package/dist/src/core/token-docs.d.ts +1 -1
- package/dist/src/core/token-inventory.d.ts +6 -6
- package/dist/src/core/token-inventory.js +7 -7
- package/dist/src/core/token-manager.d.ts +3 -3
- package/dist/src/core/token-manager.js +21 -21
- package/dist/src/core/token-scaffold.d.ts +4 -4
- package/dist/src/core/token-scaffold.js +4 -4
- package/dist/src/core/token-variant.d.ts +14 -14
- package/dist/src/core/token-variant.js +32 -32
- package/dist/src/core/toolchain-preflight.d.ts +15 -15
- package/dist/src/core/toolchain-preflight.js +22 -22
- package/dist/src/core/tree-cow.d.ts +3 -3
- package/dist/src/core/tree-cow.js +6 -6
- package/dist/src/core/tree-guard.d.ts +3 -3
- package/dist/src/core/tree-guard.js +23 -23
- package/dist/src/core/tree-store.d.ts +15 -15
- package/dist/src/core/tree-store.js +35 -33
- package/dist/src/core/typecheck-shim.d.ts +16 -15
- package/dist/src/core/typecheck-shim.js +26 -25
- package/dist/src/core/typecheck.d.ts +12 -12
- package/dist/src/core/typecheck.js +31 -31
- package/dist/src/core/wire-destroy.d.ts +2 -2
- package/dist/src/core/wire-destroy.js +5 -5
- package/dist/src/core/wire-dom-fragment.d.ts +3 -3
- package/dist/src/core/wire-dom-fragment.js +6 -6
- package/dist/src/core/wire-init.d.ts +2 -2
- package/dist/src/core/wire-init.js +7 -7
- package/dist/src/core/wire-subscript.d.ts +1 -1
- package/dist/src/core/wire-subscript.js +3 -3
- package/dist/src/core/wire-utils.d.ts +9 -9
- package/dist/src/core/wire-utils.js +11 -11
- package/dist/src/core/xpcshell-appdir.d.ts +20 -20
- package/dist/src/core/xpcshell-appdir.js +26 -26
- package/dist/src/core/xpcshell-profile-dir.d.ts +3 -3
- package/dist/src/core/xpcshell-profile-dir.js +7 -7
- package/dist/src/errors/base.d.ts +22 -21
- package/dist/src/errors/base.js +22 -21
- package/dist/src/errors/build.d.ts +6 -6
- package/dist/src/errors/build.js +6 -6
- package/dist/src/errors/codes.d.ts +10 -10
- package/dist/src/errors/codes.js +10 -10
- package/dist/src/errors/furnace.d.ts +2 -2
- package/dist/src/errors/git.d.ts +1 -1
- package/dist/src/errors/git.js +1 -1
- package/dist/src/errors/patch.d.ts +3 -3
- package/dist/src/errors/patch.js +3 -3
- package/dist/src/errors/rebase.d.ts +1 -1
- package/dist/src/errors/rebase.js +1 -1
- package/dist/src/errors/run.d.ts +1 -1
- package/dist/src/errors/run.js +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.js +4 -4
- package/dist/src/types/commands/options.d.ts +86 -85
- package/dist/src/types/commands/patches.d.ts +19 -19
- package/dist/src/types/commands/project.d.ts +3 -3
- package/dist/src/types/config.d.ts +24 -23
- package/dist/src/types/furnace.d.ts +17 -17
- package/dist/src/types/json.d.ts +3 -3
- package/dist/src/types/json.js +2 -2
- package/dist/src/types/typecheck.d.ts +3 -3
- package/dist/src/utils/assert.d.ts +1 -1
- package/dist/src/utils/assert.js +6 -6
- package/dist/src/utils/build-info.d.ts +5 -5
- package/dist/src/utils/build-info.js +6 -6
- package/dist/src/utils/child-env.d.ts +5 -5
- package/dist/src/utils/child-env.js +5 -5
- package/dist/src/utils/concurrency.d.ts +2 -2
- package/dist/src/utils/concurrency.js +2 -2
- package/dist/src/utils/elapsed.js +1 -1
- package/dist/src/utils/errors.d.ts +8 -8
- package/dist/src/utils/errors.js +8 -8
- package/dist/src/utils/fs.d.ts +1 -1
- package/dist/src/utils/fs.js +8 -8
- package/dist/src/utils/line-dispatch.d.ts +3 -3
- package/dist/src/utils/line-dispatch.js +5 -5
- package/dist/src/utils/logger.d.ts +4 -4
- package/dist/src/utils/logger.js +11 -11
- package/dist/src/utils/machine-output.js +2 -2
- package/dist/src/utils/options.d.ts +13 -13
- package/dist/src/utils/options.js +20 -20
- package/dist/src/utils/package-root.js +2 -2
- package/dist/src/utils/paths.d.ts +4 -4
- package/dist/src/utils/paths.js +4 -4
- package/dist/src/utils/process-group.d.ts +8 -8
- package/dist/src/utils/process-group.js +19 -19
- package/dist/src/utils/process.d.ts +34 -33
- package/dist/src/utils/process.js +20 -19
- package/dist/src/utils/ps-duration.d.ts +4 -4
- package/dist/src/utils/ps-duration.js +6 -6
- package/dist/src/utils/regex.d.ts +4 -4
- package/dist/src/utils/regex.js +7 -7
- package/dist/src/utils/sleep.d.ts +7 -7
- package/dist/src/utils/sleep.js +7 -7
- package/dist/src/utils/stdio-drain.d.ts +7 -7
- package/dist/src/utils/stdio-drain.js +8 -8
- package/dist/src/utils/validation.d.ts +11 -12
- package/dist/src/utils/validation.js +20 -21
- package/docs/README.md +2 -2
- package/docs/configuration.md +120 -114
- package/docs/exit-codes.md +63 -58
- package/docs/furnace.md +20 -19
- package/docs/lifecycle-invariants.md +412 -251
- package/docs/machine-output.md +33 -32
- package/docs/patch-workflow.md +48 -45
- package/docs/run-logs.md +62 -58
- package/docs/testing.md +101 -101
- package/docs/verification-trees.md +23 -22
- package/package.json +2 -2
|
@@ -14,15 +14,15 @@ export interface LockHolder {
|
|
|
14
14
|
/**
|
|
15
15
|
* Live queue state around a contended lock.
|
|
16
16
|
*
|
|
17
|
-
* The lock itself behaves correctly under several concurrent sessions
|
|
18
|
-
* gap this closes is
|
|
17
|
+
* The lock itself behaves correctly under several concurrent sessions. The
|
|
18
|
+
* gap this closes is visibility. Waiters advertise themselves in a sibling
|
|
19
19
|
* directory so both the wait output and `fireforge status --lock` can say
|
|
20
20
|
* how many are queued and how many are ahead of you.
|
|
21
21
|
*/
|
|
22
22
|
export interface LockQueueState {
|
|
23
23
|
/** Live waiters currently queued for the lock, including this process. */
|
|
24
24
|
depth: number;
|
|
25
|
-
/** Live waiters that started waiting
|
|
25
|
+
/** Live waiters that started waiting before this process. */
|
|
26
26
|
ahead: number;
|
|
27
27
|
}
|
|
28
28
|
export interface FileLockOptions {
|
|
@@ -31,7 +31,7 @@ export interface FileLockOptions {
|
|
|
31
31
|
/**
|
|
32
32
|
* When set, the poll interval doubles after every contended poll (starting
|
|
33
33
|
* from `pollMs`) up to this cap. When absent, polling stays at the fixed
|
|
34
|
-
* `pollMs` interval
|
|
34
|
+
* `pollMs` interval, with zero behavior change for existing callers.
|
|
35
35
|
*/
|
|
36
36
|
pollMaxMs?: number;
|
|
37
37
|
staleMs?: number;
|
|
@@ -60,17 +60,17 @@ export interface FileLockOptions {
|
|
|
60
60
|
queue?: LockQueueState | undefined;
|
|
61
61
|
}) => void;
|
|
62
62
|
/**
|
|
63
|
-
* When set, a wait whose
|
|
63
|
+
* When set, a wait whose queue position is still improving is granted a
|
|
64
64
|
* fresh `timeoutMs` budget on each advance, up to `maxWaitMs` total.
|
|
65
65
|
*
|
|
66
66
|
* A fixed wall-clock budget against a queue whose depth the tool knows is
|
|
67
67
|
* the wrong shape under several concurrent sessions: a wait that expires
|
|
68
68
|
* one position from the head pays the entire wait and gets nothing. The
|
|
69
|
-
*
|
|
70
|
-
*
|
|
69
|
+
* question that matters is whether the queue is moving, not how long the
|
|
70
|
+
* wait has been. A stalled queue still starves on the original budget,
|
|
71
71
|
* which is the case the timeout exists for.
|
|
72
72
|
*
|
|
73
|
-
* Requires the waiter registry (see {@link LockQueueState})
|
|
73
|
+
* Requires the waiter registry (see {@link LockQueueState}). A lock with
|
|
74
74
|
* no readable queue never extends, because nothing observed an advance.
|
|
75
75
|
*/
|
|
76
76
|
extendWhileAdvancing?: {
|
|
@@ -82,7 +82,7 @@ export interface FileLockOptions {
|
|
|
82
82
|
* Separate from {@link onWaitProgress} on purpose: an extension is the one
|
|
83
83
|
* moment the budget the operator asked for stops being the budget in
|
|
84
84
|
* force, and a wait that silently outlives its stated timeout is
|
|
85
|
-
* indistinguishable from a broken one. `budgetMs` is the new
|
|
85
|
+
* indistinguishable from a broken one. `budgetMs` is the new total budget
|
|
86
86
|
* measured from the start of the wait, so it can be compared directly
|
|
87
87
|
* against the requested figure.
|
|
88
88
|
*/
|
|
@@ -92,7 +92,7 @@ export interface FileLockOptions {
|
|
|
92
92
|
}) => void;
|
|
93
93
|
onTimeoutMessage?: string;
|
|
94
94
|
onStaleLockMessage?: (ageMs: number) => string | undefined;
|
|
95
|
-
/** Extra owner-file lines for human diagnostics
|
|
95
|
+
/** Extra owner-file lines for human diagnostics. Lock mechanics ignore them. */
|
|
96
96
|
ownerMetadata?: readonly string[];
|
|
97
97
|
}
|
|
98
98
|
/** Derives the sibling lock-directory path used to guard a file-based resource. */
|
|
@@ -128,11 +128,11 @@ export declare function readLockStatus(lockPath: string): Promise<LockStatusSnap
|
|
|
128
128
|
* Removes a lock directory only if this process still owns it, for callers
|
|
129
129
|
* that hold no acquisition token.
|
|
130
130
|
*
|
|
131
|
-
* The ownership half of {@link releaseLock}, minus the token check
|
|
131
|
+
* The ownership half of {@link releaseLock}, minus the token check: a
|
|
132
132
|
* signal-time sweeper knows which lock paths its own operations opened, but
|
|
133
133
|
* not the per-acquisition UUID those acquisitions minted. The PID check is
|
|
134
134
|
* still the one that matters: it stops the sweeper deleting a lock a
|
|
135
|
-
*
|
|
135
|
+
* different process acquired in the window between our operation dying and
|
|
136
136
|
* the sweep running.
|
|
137
137
|
*
|
|
138
138
|
* Never throws: callers are shutdown paths where a slow or failing I/O must
|
|
@@ -14,8 +14,8 @@ const DEFAULT_LOCK_POLL_MS = 50;
|
|
|
14
14
|
const DEFAULT_STALE_LOCK_MS = 5 * 60_000;
|
|
15
15
|
/**
|
|
16
16
|
* Minimum interval between stale-lock probes while a waiter polls. Probing
|
|
17
|
-
* exactly once per waiter means a holder that dies
|
|
18
|
-
* the waiter polling a permanently dead lock until `timeoutMs
|
|
17
|
+
* exactly once per waiter means a holder that dies after that probe leaves
|
|
18
|
+
* the waiter polling a permanently dead lock until `timeoutMs`, up to 24
|
|
19
19
|
* hours for the build lock (see `withBuildLock` in mach.ts). Re-probing on a
|
|
20
20
|
* small interval bounds that to seconds while keeping the per-poll cost
|
|
21
21
|
* negligible: one stat plus one small read every ~5 s.
|
|
@@ -105,7 +105,7 @@ async function registerWaiter(lockPath, startedAt) {
|
|
|
105
105
|
await writeFile(file, `${process.pid}\n`, 'utf-8');
|
|
106
106
|
}
|
|
107
107
|
catch (error) {
|
|
108
|
-
// Advisory only
|
|
108
|
+
// Advisory only. Never fail a lock acquisition over the queue display.
|
|
109
109
|
verbose(`Could not register lock waiter for ${lockPath}: ${toError(error).message}`);
|
|
110
110
|
return async () => {
|
|
111
111
|
/* nothing to remove */
|
|
@@ -119,8 +119,8 @@ async function registerWaiter(lockPath, startedAt) {
|
|
|
119
119
|
* Reads the contended lock's owner and queue in one go. One probe per cycle
|
|
120
120
|
* serves both the progress line and the deadline extension, so the two can
|
|
121
121
|
* never disagree about the position they saw. Failure to read the owner
|
|
122
|
-
* degrades to `holder: undefined
|
|
123
|
-
* loop.
|
|
122
|
+
* degrades to `holder: undefined`, since a diagnostic must never break the
|
|
123
|
+
* wait loop.
|
|
124
124
|
*/
|
|
125
125
|
async function probeWaitState(lockPath, startedAt) {
|
|
126
126
|
const owner = await readLockOwner(lockPath);
|
|
@@ -135,13 +135,13 @@ async function probeWaitState(lockPath, startedAt) {
|
|
|
135
135
|
*
|
|
136
136
|
* A naive stat → read-PID → `rm` sequence has a TOCTOU hole: two waiters
|
|
137
137
|
* both observe the dead owner, one removes the stale dir and `mkdir`s its
|
|
138
|
-
* own lock, and the other's already-decided `rm` then deletes the
|
|
139
|
-
* lock
|
|
138
|
+
* own lock, and the other's already-decided `rm` then deletes the fresh
|
|
139
|
+
* lock, putting two processes inside the critical section at once.
|
|
140
140
|
*
|
|
141
141
|
* Rename-aside closes it: `rename()` is atomic, so exactly one reaper wins
|
|
142
142
|
* ownership of the directory (the loser gets ENOENT and re-polls), and
|
|
143
143
|
* nothing ever deletes the live path directly. After winning the rename the
|
|
144
|
-
* owner is re-read from the renamed dir
|
|
144
|
+
* owner is re-read from the renamed dir. If it is now a live process (the
|
|
145
145
|
* lock was released and re-acquired between the staleness read and the
|
|
146
146
|
* rename), it is renamed back instead of reaped.
|
|
147
147
|
*
|
|
@@ -156,7 +156,7 @@ async function reapStaleLock(lockPath, ageMs, onStaleLockMessage) {
|
|
|
156
156
|
}
|
|
157
157
|
catch (error) {
|
|
158
158
|
if (getNodeErrorCode(error) === 'ENOENT') {
|
|
159
|
-
// Another reaper won, or the owner released
|
|
159
|
+
// Another reaper won, or the owner released. Either way, retry mkdir.
|
|
160
160
|
verbose(`Stale lock disappeared before reaping completed: ${lockPath}`);
|
|
161
161
|
return true;
|
|
162
162
|
}
|
|
@@ -174,7 +174,7 @@ async function reapStaleLock(lockPath, ageMs, onStaleLockMessage) {
|
|
|
174
174
|
catch (restoreError) {
|
|
175
175
|
// Rename-back can only fail if a third process mkdir'd the lock path
|
|
176
176
|
// in this instant. The displaced live owner keeps running without its
|
|
177
|
-
// lock
|
|
177
|
+
// lock, so surface it loudly instead of hiding the lost exclusion.
|
|
178
178
|
warn(`Could not restore live lock for PID ${owner.pid} at ${lockPath} ` +
|
|
179
179
|
`(${toError(restoreError).message}). Displaced lock kept at ${reapPath} for inspection.`);
|
|
180
180
|
}
|
|
@@ -199,10 +199,10 @@ async function removeIfStaleLock(lockPath, staleMs, onStaleLockMessage) {
|
|
|
199
199
|
try {
|
|
200
200
|
const lockStat = await stat(lockPath);
|
|
201
201
|
const ageMs = Date.now() - lockStat.mtimeMs;
|
|
202
|
-
// Check PID
|
|
202
|
+
// Check the PID first, independent of age. Age-gating everything means a
|
|
203
203
|
// lock younger than `staleMs` (default 5 minutes) is never removed even
|
|
204
|
-
// when its PID file points at a process that has already exited
|
|
205
|
-
// situation after SIGINT'ing `furnace preview`, where the signal handler
|
|
204
|
+
// when its PID file points at a process that has already exited. That is
|
|
205
|
+
// the situation after SIGINT'ing `furnace preview`, where the signal handler
|
|
206
206
|
// calls `process.exit` and `withFileLock`'s release never runs. With the
|
|
207
207
|
// PID-first check an explicitly-dead owner unblocks immediately.
|
|
208
208
|
const owner = await readLockOwner(lockPath);
|
|
@@ -210,8 +210,8 @@ async function removeIfStaleLock(lockPath, staleMs, onStaleLockMessage) {
|
|
|
210
210
|
if (!isLockOwnerAlive(owner)) {
|
|
211
211
|
return await reapStaleLock(lockPath, ageMs, onStaleLockMessage);
|
|
212
212
|
}
|
|
213
|
-
// The owner is alive (PID exists
|
|
214
|
-
// the process that acquired the lock, not a recycled PID)
|
|
213
|
+
// The owner is alive (PID exists and, where `/proc` can tell, it is
|
|
214
|
+
// the process that acquired the lock, not a recycled PID), so respect
|
|
215
215
|
// it regardless of age. A slow `mach build` legitimately holds the
|
|
216
216
|
// lock past the stale threshold and we don't want to race-remove it.
|
|
217
217
|
verbose(`Lock at ${lockPath} is ${Math.round(ageMs / 1000)}s old but PID ${owner.pid} is still running — not removing`);
|
|
@@ -237,8 +237,8 @@ async function removeIfStaleLock(lockPath, staleMs, onStaleLockMessage) {
|
|
|
237
237
|
}
|
|
238
238
|
/**
|
|
239
239
|
* Releases a lock previously acquired by this process, verifying ownership
|
|
240
|
-
* first. An unconditional `finally { rm }` would remove
|
|
241
|
-
* directory is present
|
|
240
|
+
* first. An unconditional `finally { rm }` would remove whichever lock
|
|
241
|
+
* directory is present. If a stale-lock reaper had (in a pathological race)
|
|
242
242
|
* replaced our lock, that deletes the new owner's lock and compounds the
|
|
243
243
|
* double-acquisition. When the PID file no longer names us, warn and leave
|
|
244
244
|
* the directory alone.
|
|
@@ -260,11 +260,11 @@ async function releaseLock(lockPath, ownerToken) {
|
|
|
260
260
|
* Removes a lock directory only if this process still owns it, for callers
|
|
261
261
|
* that hold no acquisition token.
|
|
262
262
|
*
|
|
263
|
-
* The ownership half of {@link releaseLock}, minus the token check
|
|
263
|
+
* The ownership half of {@link releaseLock}, minus the token check: a
|
|
264
264
|
* signal-time sweeper knows which lock paths its own operations opened, but
|
|
265
265
|
* not the per-acquisition UUID those acquisitions minted. The PID check is
|
|
266
266
|
* still the one that matters: it stops the sweeper deleting a lock a
|
|
267
|
-
*
|
|
267
|
+
* different process acquired in the window between our operation dying and
|
|
268
268
|
* the sweep running.
|
|
269
269
|
*
|
|
270
270
|
* Never throws: callers are shutdown paths where a slow or failing I/O must
|
|
@@ -293,8 +293,11 @@ export async function releaseLockIfOwned(lockPath) {
|
|
|
293
293
|
* Typed as {@link LockContentionError} (a `FireForgeError`) so the CLI
|
|
294
294
|
* boundary prints the refusal as one clean line instead of an "Unexpected
|
|
295
295
|
* error" stack. Callers supplying `onTimeoutMessage` still get the holder
|
|
296
|
-
* identification appended
|
|
296
|
+
* identification appended: the reason-first copy stays theirs, the "who
|
|
297
297
|
* holds it" is ours.
|
|
298
|
+
*
|
|
299
|
+
* `queue` is read at expiry by the caller, not carried over from the last
|
|
300
|
+
* progress probe: see {@link formatQueuePositionReached}.
|
|
298
301
|
*/
|
|
299
302
|
async function buildLockTimeoutError(lockPath, onTimeoutMessage, cause, queue) {
|
|
300
303
|
const owner = await readLockOwner(lockPath);
|
|
@@ -315,15 +318,22 @@ async function buildLockTimeoutError(lockPath, onTimeoutMessage, cause, queue) {
|
|
|
315
318
|
/**
|
|
316
319
|
* States the queue position the expired wait actually reached.
|
|
317
320
|
*
|
|
318
|
-
* Silence here
|
|
321
|
+
* Silence here made the worst outcome invisible: a wait that expires
|
|
319
322
|
* one position from the head has paid the whole budget for nothing, and the
|
|
320
323
|
* operator cannot tell that from a queue that never moved. Naming the
|
|
321
324
|
* position turns "raise the budget" into an informed decision instead of a
|
|
322
|
-
* guess. Omitted when no queue was observed
|
|
325
|
+
* guess. Omitted when no queue was observed, since an empty registry is not
|
|
323
326
|
* evidence of an empty queue.
|
|
327
|
+
*
|
|
328
|
+
* The queue is read at the moment the budget expires rather than taken from
|
|
329
|
+
* the last progress probe. A probe snapshot is up to one `waitProgressMs`
|
|
330
|
+
* stale, and, the reason this changed, it may not exist at all: a short
|
|
331
|
+
* budget can expire before the first probe lands (a slow runner's
|
|
332
|
+
* `registerWaiter` alone can outlast it), and a caller that configured no
|
|
333
|
+
* probe at all never had one. Both used to lose the position silently.
|
|
324
334
|
*/
|
|
325
335
|
function formatQueuePositionReached(queue) {
|
|
326
|
-
if (queue
|
|
336
|
+
if (queue.depth === 0)
|
|
327
337
|
return '';
|
|
328
338
|
if (queue.ahead === 0) {
|
|
329
339
|
return ` You were next in a queue of ${queue.depth} when the wait expired — a larger --wait-lock would very likely have got a turn.`;
|
|
@@ -352,13 +362,13 @@ async function runWaitProbe(lockPath, startedAt, timeoutMs, options) {
|
|
|
352
362
|
* SIGTERM'd command leaves its lock on disk. The next contender reclaims it
|
|
353
363
|
* immediately (the PID-first staleness check sees a dead owner), but in the
|
|
354
364
|
* window between, `fireforge status --lock` reports the lock held by a
|
|
355
|
-
* process that is not running
|
|
356
|
-
*
|
|
365
|
+
* process that is not running, which is the question an operator asks after
|
|
366
|
+
* a kill. The signal sweep closes that window.
|
|
357
367
|
*
|
|
358
368
|
* Paths only: the per-acquisition token lives in `withFileLock`'s frame and
|
|
359
369
|
* a sweeper cannot see it. {@link releaseLockIfOwned} checks the PID, which
|
|
360
|
-
* is the check that matters
|
|
361
|
-
*
|
|
370
|
+
* is the check that matters: it stops the sweep deleting a lock a
|
|
371
|
+
* different process acquired in the meantime.
|
|
362
372
|
*/
|
|
363
373
|
const heldLocks = new Set();
|
|
364
374
|
/**
|
|
@@ -392,14 +402,12 @@ export async function withFileLock(lockPath, operation, options = {}) {
|
|
|
392
402
|
let lastStaleProbeAt;
|
|
393
403
|
let currentPollMs = pollMs;
|
|
394
404
|
let lastProbeAt = startedAt;
|
|
395
|
-
/** Best (lowest) queue position seen so far
|
|
405
|
+
/** Best (lowest) queue position seen so far. Used to detect an advance. */
|
|
396
406
|
let bestAhead;
|
|
397
|
-
/** Last observed queue state, so the timeout refusal can name the position reached. */
|
|
398
|
-
let lastQueue;
|
|
399
407
|
/** Set on first contention so an uncontended acquire costs nothing. */
|
|
400
408
|
let deregisterWaiter;
|
|
401
409
|
// One cadence drives both the progress line and the deadline extension.
|
|
402
|
-
// Either feature alone is enough to want the probe
|
|
410
|
+
// Either feature alone is enough to want the probe. Neither is worth a
|
|
403
411
|
// second timer.
|
|
404
412
|
const probeIntervalMs = options.waitProgressMs ?? WAIT_PROBE_INTERVAL_MS;
|
|
405
413
|
const probeWanted = (options.onWaitProgress !== undefined && options.waitProgressMs !== undefined) ||
|
|
@@ -426,26 +434,31 @@ export async function withFileLock(lockPath, operation, options = {}) {
|
|
|
426
434
|
}
|
|
427
435
|
}
|
|
428
436
|
if (Date.now() >= deadline) {
|
|
429
|
-
|
|
437
|
+
// The queue is read here, not carried over from the last progress
|
|
438
|
+
// probe: the position the wait reached is the one number that says
|
|
439
|
+
// whether a larger budget would have helped, and it must not depend
|
|
440
|
+
// on a probe having happened to land inside the budget. We are still
|
|
441
|
+
// registered at this point. `deregisterWaiter` runs in the `finally`
|
|
442
|
+
// below, which is only entered once this refusal has been built.
|
|
443
|
+
throw await buildLockTimeoutError(lockPath, options.onTimeoutMessage, error, await readLockQueue(lockPath, startedAt));
|
|
430
444
|
}
|
|
431
445
|
if (probeWanted && Date.now() - lastProbeAt >= probeIntervalMs) {
|
|
432
446
|
lastProbeAt = Date.now();
|
|
433
|
-
// Report the budget that is
|
|
447
|
+
// Report the budget that is currently in force, not the one
|
|
434
448
|
// originally requested: once an extension has been granted, the
|
|
435
449
|
// requested figure is stale, and a progress line that keeps
|
|
436
450
|
// quoting it reads as a timeout that does not fire.
|
|
437
451
|
const probe = await runWaitProbe(lockPath, startedAt, deadline - startedAt, options);
|
|
438
|
-
|
|
439
|
-
// An advance — someone ahead of us took their turn and finished —
|
|
452
|
+
// An advance (someone ahead of us took their turn and finished)
|
|
440
453
|
// buys a fresh budget, because the wait is working. Only a queue
|
|
441
454
|
// that stops moving starves, which is the case the timeout is for.
|
|
442
455
|
//
|
|
443
|
-
// The
|
|
456
|
+
// The first observation is a seed, never an advance. `bestAhead`
|
|
444
457
|
// used to start `undefined`, so the opening probe always satisfied
|
|
445
458
|
// this test and handed out a free full budget: a `--wait-lock 300`
|
|
446
459
|
// run outlived its 300 s even against a queue that never moved
|
|
447
|
-
// once, which is
|
|
448
|
-
// seconds" a downstream fork reported.
|
|
460
|
+
// once, which is the "it does not reliably give up after N
|
|
461
|
+
// seconds" behaviour a downstream fork reported.
|
|
449
462
|
if (hardDeadline !== undefined) {
|
|
450
463
|
if (bestAhead === undefined) {
|
|
451
464
|
bestAhead = probe.queue.ahead;
|
|
@@ -468,7 +481,7 @@ export async function withFileLock(lockPath, operation, options = {}) {
|
|
|
468
481
|
}
|
|
469
482
|
}
|
|
470
483
|
finally {
|
|
471
|
-
// Leaving the queue happens the moment we stop waiting
|
|
484
|
+
// Leaving the queue happens the moment we stop waiting, whether we
|
|
472
485
|
// acquired the lock or timed out.
|
|
473
486
|
await deregisterWaiter?.();
|
|
474
487
|
}
|
|
@@ -16,7 +16,7 @@ export interface ArchiveIntegrityOptions {
|
|
|
16
16
|
* @param archive - Resolved archive descriptor
|
|
17
17
|
* @param cacheDir - Cache directory
|
|
18
18
|
* @param onProgress - Optional progress callback
|
|
19
|
-
* @param expectedSha256 - Operator-pinned digest
|
|
19
|
+
* @param expectedSha256 - Operator-pinned digest. When set it replaces the
|
|
20
20
|
* published-SHA256SUMS check
|
|
21
21
|
* @param onCacheProgress - Phase progress messages
|
|
22
22
|
* @param integrity - Default integrity-check options (unpinned downloads)
|
|
@@ -28,10 +28,10 @@ async function sha256File(filePath) {
|
|
|
28
28
|
* Fetches Mozilla's published SHA256SUMS for the release and extracts the
|
|
29
29
|
* digest for this archive. Yields a reason instead of a digest when the
|
|
30
30
|
* checksum file cannot be fetched (non-2xx, network error, timeout) or does
|
|
31
|
-
* not list the archive (a captive portal's HTML page lands here too)
|
|
31
|
+
* not list the archive (a captive portal's HTML page lands here too). The
|
|
32
32
|
* caller decides whether that fails closed. The request goes through the
|
|
33
|
-
* same `fetchWithRetry` as the archive download
|
|
34
|
-
* per attempt, same three-attempt back-off on 429/5xx and network errors
|
|
33
|
+
* same `fetchWithRetry` as the archive download (same 60 s abort ceiling
|
|
34
|
+
* per attempt, same three-attempt back-off on 429/5xx and network errors),
|
|
35
35
|
* so an unresponsive checksum host cannot hang the command after the
|
|
36
36
|
* tarball has landed, and one transient blip on the small checksum request
|
|
37
37
|
* does not fail the verification closed and discard the archive.
|
|
@@ -65,15 +65,15 @@ async function fetchPublishedSha256(archive) {
|
|
|
65
65
|
/**
|
|
66
66
|
* Default integrity check for a download with no operator pin: the archive
|
|
67
67
|
* digest must equal the one Mozilla publishes. TLS alone is thin trust for
|
|
68
|
-
* the artifact that becomes the git baseline every patch is built on
|
|
68
|
+
* the artifact that becomes the git baseline every patch is built on: a
|
|
69
69
|
* compromised CDN response would otherwise be trusted with no signal.
|
|
70
70
|
*
|
|
71
|
-
* Fails
|
|
71
|
+
* Fails closed both on a mismatch and when the published digest cannot be
|
|
72
72
|
* obtained: a 404, a captive-portal HTML page, or a dropped connection
|
|
73
73
|
* would otherwise quietly downgrade the check to "TLS looked fine", which
|
|
74
|
-
* is
|
|
74
|
+
* is the condition an attacker positioned on the network can
|
|
75
75
|
* arrange. `allowUnverifiedDownload` (an explicit config opt-in for offline
|
|
76
|
-
* mirrors) degrades the unavailable case to a loud warning
|
|
76
|
+
* mirrors) degrades the unavailable case to a loud warning. A fetched but
|
|
77
77
|
* mismatching digest is never accepted.
|
|
78
78
|
*/
|
|
79
79
|
async function verifyAgainstPublishedSha256(archive, actualSha256, options) {
|
|
@@ -97,7 +97,7 @@ async function verifyAgainstPublishedSha256(archive, actualSha256, options) {
|
|
|
97
97
|
* @param archive - Resolved archive descriptor
|
|
98
98
|
* @param cacheDir - Cache directory
|
|
99
99
|
* @param onProgress - Optional progress callback
|
|
100
|
-
* @param expectedSha256 - Operator-pinned digest
|
|
100
|
+
* @param expectedSha256 - Operator-pinned digest. When set it replaces the
|
|
101
101
|
* published-SHA256SUMS check
|
|
102
102
|
* @param onCacheProgress - Phase progress messages
|
|
103
103
|
* @param integrity - Default integrity-check options (unpinned downloads)
|
|
@@ -133,7 +133,7 @@ async function cacheEntryExists(archive, cacheDir) {
|
|
|
133
133
|
async function validateCachedArchive(archive, cacheDir, expectedSha256) {
|
|
134
134
|
const tarballPath = join(cacheDir, archive.filename);
|
|
135
135
|
const metadataPath = join(cacheDir, archive.metadataFilename);
|
|
136
|
-
//
|
|
136
|
+
// Outside the try/catch below on purpose: a permission error probing the
|
|
137
137
|
// cache must propagate instead of reading as "invalid cache" and triggering
|
|
138
138
|
// a re-download into a directory we cannot write anyway.
|
|
139
139
|
if (!(await pathExistsStrict(tarballPath)) || !(await pathExistsStrict(metadataPath))) {
|
|
@@ -244,6 +244,6 @@ async function removeArchivePartFiles(archive, cacheDir) {
|
|
|
244
244
|
}
|
|
245
245
|
catch (error) {
|
|
246
246
|
void error;
|
|
247
|
-
// Cache dir may not exist yet
|
|
247
|
+
// Cache dir may not exist yet. That's fine.
|
|
248
248
|
}
|
|
249
249
|
}
|
|
@@ -9,9 +9,9 @@ export type ProgressCallback = (downloaded: number, total: number) => void;
|
|
|
9
9
|
* Fetches a URL with timeout and bounded retry for transient failures
|
|
10
10
|
* (429/5xx, network errors, the per-request timeout).
|
|
11
11
|
*
|
|
12
|
-
* Any other response
|
|
13
|
-
* returned as-is on the first attempt
|
|
14
|
-
* status means for
|
|
12
|
+
* Any other response (2xx or a non-retryable failure such as 404) is
|
|
13
|
+
* returned as-is on the first attempt. The caller decides what a given
|
|
14
|
+
* status means for its request. Shared by the tarball download and the
|
|
15
15
|
* SHA256SUMS lookup so both requests the download makes survive the same
|
|
16
16
|
* transient blips: with checksum verification failing closed, an
|
|
17
17
|
* un-retried checksum fetch would throw away a just-landed 600 MB archive
|
|
@@ -10,8 +10,8 @@ import { DownloadError, VersionNotFoundError } from '../errors/download.js';
|
|
|
10
10
|
import { sleep } from '../utils/sleep.js';
|
|
11
11
|
/**
|
|
12
12
|
* Per-attempt request timeout in milliseconds (60 seconds). Every request
|
|
13
|
-
* the download makes
|
|
14
|
-
* `firefox-cache.ts`
|
|
13
|
+
* the download makes (the tarball and the SHA256SUMS lookup in
|
|
14
|
+
* `firefox-cache.ts`) goes through {@link fetchWithRetry} and so shares
|
|
15
15
|
* this abort ceiling.
|
|
16
16
|
*/
|
|
17
17
|
const REQUEST_TIMEOUT_MS = 60_000;
|
|
@@ -21,15 +21,15 @@ const MAX_ATTEMPTS = 3;
|
|
|
21
21
|
const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
|
|
22
22
|
/** Base delay between retries in milliseconds. */
|
|
23
23
|
const RETRY_BASE_DELAY_MS = 1_000;
|
|
24
|
-
/** Stall detection timeout
|
|
24
|
+
/** Stall detection timeout: abort if no data received for this duration (30 seconds). */
|
|
25
25
|
const DOWNLOAD_STALL_TIMEOUT_MS = 30_000;
|
|
26
26
|
/**
|
|
27
27
|
* Fetches a URL with timeout and bounded retry for transient failures
|
|
28
28
|
* (429/5xx, network errors, the per-request timeout).
|
|
29
29
|
*
|
|
30
|
-
* Any other response
|
|
31
|
-
* returned as-is on the first attempt
|
|
32
|
-
* status means for
|
|
30
|
+
* Any other response (2xx or a non-retryable failure such as 404) is
|
|
31
|
+
* returned as-is on the first attempt. The caller decides what a given
|
|
32
|
+
* status means for its request. Shared by the tarball download and the
|
|
33
33
|
* SHA256SUMS lookup so both requests the download makes survive the same
|
|
34
34
|
* transient blips: with checksum verification failing closed, an
|
|
35
35
|
* un-retried checksum fetch would throw away a just-landed 600 MB archive
|
|
@@ -13,9 +13,9 @@ export declare function findUnsafeArchiveEntryName(names: readonly string[]): st
|
|
|
13
13
|
*
|
|
14
14
|
* A relative link target without `..` segments can only resolve inside the
|
|
15
15
|
* extraction root, so only absolute or `..`-containing targets are rejected.
|
|
16
|
-
* Symlinks are `l`-typed lines with a ` -> target` suffix (the
|
|
16
|
+
* Symlinks are `l`-typed lines with a ` -> target` suffix (the last arrow is
|
|
17
17
|
* taken, so a link whose own name contains ` -> ` still parses to its real
|
|
18
|
-
* target)
|
|
18
|
+
* target). Hardlinks print ` link to target` on GNU tar and bsdtar alike.
|
|
19
19
|
*
|
|
20
20
|
* @param verboseLines - `tar -tvf` listing lines
|
|
21
21
|
* @returns A description of the first unsafe link found, or undefined
|
|
@@ -22,20 +22,20 @@ function isUnsafeArchivePath(path) {
|
|
|
22
22
|
/**
|
|
23
23
|
* Returns true when an archive member path has a `.git` segment at any
|
|
24
24
|
* depth (`.git`, `.git/config`, `foo/.git/hooks/pre-commit`). Such a member
|
|
25
|
-
* lands
|
|
25
|
+
* lands inside the extraction root, so the traversal check passes it, but
|
|
26
26
|
* the extracted tree is immediately turned into a git repository and
|
|
27
27
|
* committed, and git honours a pre-seeded `.git/config` (`core.fsmonitor`,
|
|
28
28
|
* `core.hooksPath`) or `.git/hooks/*` on that first commit. Only the `.git`
|
|
29
|
-
* directory/file is rejected
|
|
29
|
+
* directory/file is rejected. `.gitmodules`, `.gitignore` and the like are
|
|
30
30
|
* legitimate upstream files.
|
|
31
31
|
*/
|
|
32
32
|
function hasGitSegment(path) {
|
|
33
33
|
return path.split(/[\\/]/).some((segment) => segment.toLowerCase() === '.git');
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* Returns true when an archive member
|
|
36
|
+
* Returns true when an archive member name must be rejected: an escaping
|
|
37
37
|
* path, or a `.git` segment (see {@link hasGitSegment}). Link targets use
|
|
38
|
-
* the narrower {@link isUnsafeArchivePath}
|
|
38
|
+
* the narrower {@link isUnsafeArchivePath}: a target can only be reached
|
|
39
39
|
* through a member name that was checked here.
|
|
40
40
|
*/
|
|
41
41
|
function isUnsafeArchiveMemberName(name) {
|
|
@@ -62,9 +62,9 @@ export function findUnsafeArchiveEntryName(names) {
|
|
|
62
62
|
*
|
|
63
63
|
* A relative link target without `..` segments can only resolve inside the
|
|
64
64
|
* extraction root, so only absolute or `..`-containing targets are rejected.
|
|
65
|
-
* Symlinks are `l`-typed lines with a ` -> target` suffix (the
|
|
65
|
+
* Symlinks are `l`-typed lines with a ` -> target` suffix (the last arrow is
|
|
66
66
|
* taken, so a link whose own name contains ` -> ` still parses to its real
|
|
67
|
-
* target)
|
|
67
|
+
* target). Hardlinks print ` link to target` on GNU tar and bsdtar alike.
|
|
68
68
|
*
|
|
69
69
|
* @param verboseLines - `tar -tvf` listing lines
|
|
70
70
|
* @returns A description of the first unsafe link found, or undefined
|
|
@@ -93,13 +93,13 @@ export function findUnsafeArchiveLink(verboseLines) {
|
|
|
93
93
|
* Lists the archive and rejects members that could escape the extraction
|
|
94
94
|
* root before anything is written to disk: absolute names, `..` traversal,
|
|
95
95
|
* and symlink/hardlink targets that are absolute or `..`-escaping. Modern
|
|
96
|
-
* GNU tar / bsdtar refuse most of these at extraction time
|
|
96
|
+
* GNU tar / bsdtar refuse most of these at extraction time. The preflight
|
|
97
97
|
* makes the guarantee explicit and independent of the host tar's defaults.
|
|
98
98
|
*
|
|
99
99
|
* Two listings are needed because they answer different questions and
|
|
100
100
|
* neither is safe to derive from the other: member names come from `-tf`,
|
|
101
|
-
* where the whole line
|
|
102
|
-
* `-tvf`. Names are
|
|
101
|
+
* where the whole line is the name, while link targets only appear in
|
|
102
|
+
* `-tvf`. Names are not parsed out of the `-tvf` columns, on purpose: the
|
|
103
103
|
* adjacent uname/gname fields are attacker-controlled in a crafted archive,
|
|
104
104
|
* so a date-shaped owner name could shift the column boundary and hide an
|
|
105
105
|
* absolute member name from the check. Each listing pass costs one full
|
|
@@ -138,11 +138,11 @@ function createLineScanner(onLine) {
|
|
|
138
138
|
*
|
|
139
139
|
* Streaming matters for safety, not just memory: a buffered `exec` collector
|
|
140
140
|
* silently truncates at 50 MB, and a full Firefox `-tvf` listing already
|
|
141
|
-
* sits in the 40
|
|
141
|
+
* sits in the 40-50 MB range, so a buffered implementation scans only the
|
|
142
142
|
* head of the listing while reporting the archive safe. A crafted archive
|
|
143
|
-
* could exploit
|
|
143
|
+
* could exploit that by padding benign entries first and hiding a
|
|
144
144
|
* traversal name or absolute link target past the cap. Per-line streaming
|
|
145
|
-
* scans
|
|
145
|
+
* scans every entry with bounded memory.
|
|
146
146
|
*/
|
|
147
147
|
async function runListingScan(archivePath, tarFlag, checkLine) {
|
|
148
148
|
// LC_ALL=C keeps the -tvf column format stable for the link parse.
|
|
@@ -160,7 +160,7 @@ async function runListingScan(archivePath, tarFlag, checkLine) {
|
|
|
160
160
|
scanner.push(chunk);
|
|
161
161
|
},
|
|
162
162
|
onStderr: (chunk) => {
|
|
163
|
-
// Keep a small diagnostic tail
|
|
163
|
+
// Keep a small diagnostic tail. The listing itself is the big stream.
|
|
164
164
|
stderrTail = (stderrTail + chunk).slice(-4096);
|
|
165
165
|
},
|
|
166
166
|
});
|
|
@@ -24,10 +24,10 @@ export type FirefoxSourceProgressCallback = (message: string) => void;
|
|
|
24
24
|
* Sweeps orphaned working directories left behind by interrupted downloads
|
|
25
25
|
* (`<engine>.tmp-*` from extraction, `<engine>.replacement-*` from --force
|
|
26
26
|
* replacement). Both hold multi-GB partial trees, are worthless after the
|
|
27
|
-
* owning process died, and nothing else ever reclaimed them.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* owning process died, and nothing else ever reclaimed them. Does not
|
|
28
|
+
* touch `<engine>.backup-*`: a backup may hold the operator's previous
|
|
29
|
+
* engine after a failed forced replacement and is their recovery copy, so
|
|
30
|
+
* callers should surface its existence instead.
|
|
31
31
|
*
|
|
32
32
|
* @returns names of the directories that were removed
|
|
33
33
|
*/
|
|
@@ -46,7 +46,7 @@ export interface DownloadFirefoxSourceOptions {
|
|
|
46
46
|
onProgress?: ProgressCallback | undefined;
|
|
47
47
|
/** Called when the run moves from downloading to extracting. */
|
|
48
48
|
onPhase?: FirefoxSourcePhaseCallback | undefined;
|
|
49
|
-
/** Expected archive checksum
|
|
49
|
+
/** Expected archive checksum, verified after download when supplied. */
|
|
50
50
|
expectedSha256?: string | undefined;
|
|
51
51
|
/** Called with byte progress within the current phase. */
|
|
52
52
|
onPhaseProgress?: FirefoxSourceProgressCallback | undefined;
|
package/dist/src/core/firefox.js
CHANGED
|
@@ -18,8 +18,8 @@ export { formatBytes, getFirefoxVersion } from './firefox-extract.js';
|
|
|
18
18
|
/**
|
|
19
19
|
* Classifies an extraction failure as archive corruption (invalidate the
|
|
20
20
|
* cache and re-download) versus an environmental problem (keep the cached
|
|
21
|
-
* archive
|
|
22
|
-
* conservative list
|
|
21
|
+
* archive so the retry can reuse it). Environmental signatures are the
|
|
22
|
+
* conservative list. Anything unrecognized is treated as corruption so a
|
|
23
23
|
* genuinely bad archive can never survive in the cache.
|
|
24
24
|
*/
|
|
25
25
|
function isLikelyArchiveCorruptionError(error) {
|
|
@@ -32,7 +32,7 @@ function isLikelyArchiveCorruptionError(error) {
|
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
34
|
// Missing tar binary / preflight refusing an unsafe archive both carry
|
|
35
|
-
// ExtractionError text that names the condition
|
|
35
|
+
// ExtractionError text that names the condition. A missing tar is
|
|
36
36
|
// environmental, an unsafe-entry rejection indicts the archive.
|
|
37
37
|
if (/"tar" command was not found/i.test(message)) {
|
|
38
38
|
return false;
|
|
@@ -43,10 +43,10 @@ function isLikelyArchiveCorruptionError(error) {
|
|
|
43
43
|
* Sweeps orphaned working directories left behind by interrupted downloads
|
|
44
44
|
* (`<engine>.tmp-*` from extraction, `<engine>.replacement-*` from --force
|
|
45
45
|
* replacement). Both hold multi-GB partial trees, are worthless after the
|
|
46
|
-
* owning process died, and nothing else ever reclaimed them.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
46
|
+
* owning process died, and nothing else ever reclaimed them. Does not
|
|
47
|
+
* touch `<engine>.backup-*`: a backup may hold the operator's previous
|
|
48
|
+
* engine after a failed forced replacement and is their recovery copy, so
|
|
49
|
+
* callers should surface its existence instead.
|
|
50
50
|
*
|
|
51
51
|
* @returns names of the directories that were removed
|
|
52
52
|
*/
|
|
@@ -95,7 +95,7 @@ export async function downloadFirefoxSource(options) {
|
|
|
95
95
|
catch (error) {
|
|
96
96
|
await removeDir(tempDir);
|
|
97
97
|
// Only throw away the ~600 MB cached archive when the failure indicts
|
|
98
|
-
// the
|
|
98
|
+
// the archive, not the environment. ENOSPC, a permission problem in
|
|
99
99
|
// destDir, or tar disappearing mid-run used to invalidate a perfectly
|
|
100
100
|
// good download and force a full re-fetch on retry.
|
|
101
101
|
if (isLikelyArchiveCorruptionError(error)) {
|
|
@@ -52,7 +52,7 @@ export async function buildCustomDryRunActions(ctx, config, targetDir, entries)
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
// A sharedFtl widget owns its strings via the shared bundle
|
|
55
|
+
// A sharedFtl widget owns its strings via the shared bundle. Surface the
|
|
56
56
|
// removal of any dangling per-widget locale jar.mn entry so the dry-run
|
|
57
57
|
// plan matches what apply will do (and explains the unblocked build).
|
|
58
58
|
const pruneAction = await describeSharedFtlPrune(ctx, config);
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* per-file LOC budget.
|
|
5
5
|
*
|
|
6
6
|
* Every helper here degrades gracefully: if the locale jar.mn is missing or
|
|
7
|
-
* the FTL tree is non-standard, apply logs an
|
|
7
|
+
* the FTL tree is non-standard, apply logs an advisory `stepError`
|
|
8
8
|
* (`advisory: true`) rather than aborting the whole command. Advisory step
|
|
9
|
-
* errors are reported as warnings and never trigger rollback
|
|
9
|
+
* errors are reported as warnings and never trigger rollback: a missing
|
|
10
10
|
* jar.mn on a fork without a locale package must not block a working
|
|
11
11
|
* `.mjs`/`.css` from shipping.
|
|
12
12
|
*/
|
|
@@ -29,8 +29,8 @@ export declare function describeSharedFtlPrune(ctx: Pick<ComponentApplyContext,
|
|
|
29
29
|
* in the locale jar.mn.
|
|
30
30
|
*
|
|
31
31
|
* Failure modes (missing jar.mn, regex write error) are captured as
|
|
32
|
-
* stepErrors rather than thrown
|
|
33
|
-
* blocked by a broken locale path.
|
|
32
|
+
* stepErrors rather than thrown, since a well-formed `.mjs`/`.css` must
|
|
33
|
+
* never be blocked by a broken locale path.
|
|
34
34
|
*/
|
|
35
35
|
export declare function applyCustomFtlFile(ctx: Pick<ComponentApplyContext, 'engineDir' | 'name' | 'componentDir' | 'ftlDir'>, affectedPaths: string[], stepErrors: StepError[], rollbackJournal?: RollbackJournal): Promise<void>;
|
|
36
36
|
/**
|
|
@@ -41,7 +41,7 @@ export declare function applyCustomFtlFile(ctx: Pick<ComponentApplyContext, 'eng
|
|
|
41
41
|
export declare function describeLocaleFtlJarMnRegistration(name: string, ftlDir: string, ftlFile: string): DryRunAction | undefined;
|
|
42
42
|
/**
|
|
43
43
|
* Drops the locale jar.mn entry for `fileName` when it's a `.ftl` whose
|
|
44
|
-
* source workspace file has been deleted. Idempotent
|
|
44
|
+
* source workspace file has been deleted. Idempotent: absent entries are a
|
|
45
45
|
* no-op. Early-returns for `sharedFtl` components: the shared bundle is
|
|
46
46
|
* owned elsewhere, and dropping its jar.mn line on our component's delete
|
|
47
47
|
* would orphan every other participant.
|