@hominis/fireforge 0.44.4 → 0.46.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 +120 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor-source-pin.d.ts +29 -0
- package/dist/src/commands/doctor-source-pin.js +81 -0
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +9 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -60
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -37
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -11
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +32 -2
- package/dist/src/commands/re-export-adjacent.js +51 -10
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.d.ts +8 -0
- package/dist/src/commands/re-export-drift.js +65 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -32
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-options.js +19 -5
- package/dist/src/commands/re-export-register.js +5 -1
- package/dist/src/commands/re-export-scan.js +8 -8
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +44 -27
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +33 -8
- package/dist/src/commands/test-diagnose.js +14 -5
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +6 -3
- package/dist/src/commands/test-run.js +29 -21
- package/dist/src/commands/test-stale-gate.js +8 -5
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +51 -7
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +154 -12
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.d.ts +64 -0
- package/dist/src/core/build-jar-escalation.js +128 -0
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +5 -2
- package/dist/src/core/build-prepare.js +34 -6
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.d.ts +12 -0
- package/dist/src/core/config-validate-file-size.js +65 -0
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +20 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.d.ts +73 -0
- package/dist/src/core/harness-orphans.js +187 -0
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
- package/dist/src/core/mach-known-noise-filter.js +31 -10
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +46 -53
- package/dist/src/core/mach.js +31 -83
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +55 -32
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +43 -25
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.d.ts +52 -0
- package/dist/src/core/patch-lint-file-size.js +53 -0
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.d.ts +36 -0
- package/dist/src/core/patch-lint-prettier.js +126 -0
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
- package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +27 -27
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +21 -8
- package/dist/src/core/patch-transform.js +38 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +18 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +61 -48
- package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
- package/dist/src/core/test-harness-verdict-notes.js +48 -0
- package/dist/src/core/test-stale-check.d.ts +23 -8
- package/dist/src/core/test-stale-check.js +77 -4
- package/dist/src/core/test-stall-triage.js +37 -19
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.d.ts +28 -0
- package/dist/src/errors/build.js +51 -10
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +96 -123
- package/dist/src/types/commands/patches.d.ts +17 -6
- package/dist/src/types/config.d.ts +75 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.d.ts +4 -2
- package/dist/src/utils/fs.js +11 -14
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +136 -1
- package/docs/exit-codes.md +34 -20
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +70 -11
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
2
|
import { readdir } from 'node:fs/promises';
|
|
4
3
|
import { join, relative } from 'node:path';
|
|
5
4
|
import { FurnaceError } from '../errors/furnace.js';
|
|
6
5
|
import { toError } from '../utils/errors.js';
|
|
7
6
|
import { copyFile, ensureDir, pathExists, readText, removeFile } from '../utils/fs.js';
|
|
7
|
+
import { sha256Hex } from '../utils/hash.js';
|
|
8
8
|
import { verbose } from '../utils/logger.js';
|
|
9
9
|
import { normalizePathSlashes } from '../utils/paths.js';
|
|
10
10
|
import { buildCustomDryRunActions } from './furnace-apply-dry-run.js';
|
|
@@ -12,26 +12,12 @@ import { applyCustomFtlFile, applySharedFtlPrune, removeCustomFtlJarMnEntry, } f
|
|
|
12
12
|
import { normalizeForChecksum } from './furnace-checksum-utils.js';
|
|
13
13
|
import { CUSTOM_ELEMENTS_JS, JAR_MN } from './furnace-constants.js';
|
|
14
14
|
import { deployFileWithFragments, SHARED_FRAGMENTS_DIR } from './furnace-css-fragments.js';
|
|
15
|
+
import { isRegularFile } from './furnace-dir-entry.js';
|
|
15
16
|
import { addCustomElementRegistration, addJarMnEntries } from './furnace-registration.js';
|
|
16
17
|
import { recordCreatedDir, snapshotFile } from './furnace-rollback.js';
|
|
17
18
|
import { checkRegistrationConsistency } from './furnace-validate-registration.js';
|
|
18
19
|
import { isGitRepository } from './git.js';
|
|
19
20
|
import { fileExistsInHead, restoreTrackedPath } from './git-file-ops.js';
|
|
20
|
-
/**
|
|
21
|
-
* True for a plain-file directory entry — symlinks and directories are
|
|
22
|
-
* never copy candidates. Exported for the patch-owned overwrite probe,
|
|
23
|
-
* which walks the same override copy-candidate set as apply.
|
|
24
|
-
*/
|
|
25
|
-
export function isRegularFile(entry) {
|
|
26
|
-
if (!entry.isFile())
|
|
27
|
-
return false;
|
|
28
|
-
if (typeof entry.isSymbolicLink === 'function' && entry.isSymbolicLink())
|
|
29
|
-
return false;
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
function isChecksummedComponentFile(name) {
|
|
33
|
-
return name.endsWith('.mjs') || name.endsWith('.css') || name.endsWith('.ftl');
|
|
34
|
-
}
|
|
35
21
|
/**
|
|
36
22
|
* Filter deciding which files in an override workspace directory are candidates
|
|
37
23
|
* for copying into the engine. Exported so `furnace remove` can invert apply
|
|
@@ -102,10 +88,13 @@ export async function computeComponentChecksums(componentDir) {
|
|
|
102
88
|
continue;
|
|
103
89
|
if (entry.name === 'override.json')
|
|
104
90
|
continue;
|
|
105
|
-
|
|
91
|
+
// The three extensions a component can deploy; anything else in the
|
|
92
|
+
// workspace directory is not part of the component's checksum identity.
|
|
93
|
+
const { name: entryName } = entry;
|
|
94
|
+
if (!entryName.endsWith('.mjs') && !entryName.endsWith('.css') && !entryName.endsWith('.ftl'))
|
|
106
95
|
continue;
|
|
107
96
|
const content = await readText(join(componentDir, entry.name));
|
|
108
|
-
const hash =
|
|
97
|
+
const hash = sha256Hex(normalizeForChecksum(content));
|
|
109
98
|
checksums[entry.name] = hash;
|
|
110
99
|
}
|
|
111
100
|
return checksums;
|
|
@@ -233,7 +222,7 @@ export async function hasOverrideEngineDrift(ctx, config, cachedEngineChecksums)
|
|
|
233
222
|
const engineContent = normalizeForChecksum(await readText(enginePath));
|
|
234
223
|
// Fast path: compare engine content hash against cached value from last apply
|
|
235
224
|
if (cachedEngineChecksums) {
|
|
236
|
-
const engineHash =
|
|
225
|
+
const engineHash = sha256Hex(engineContent);
|
|
237
226
|
const cachedHash = cachedEngineChecksums[entry.name];
|
|
238
227
|
if (cachedHash && engineHash !== cachedHash) {
|
|
239
228
|
return true;
|
|
@@ -21,9 +21,11 @@ import { join, relative } from 'node:path';
|
|
|
21
21
|
import { toError } from '../utils/errors.js';
|
|
22
22
|
import { pathExists, readText } from '../utils/fs.js';
|
|
23
23
|
import { verbose } from '../utils/logger.js';
|
|
24
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
24
25
|
import { getProjectPaths } from './config.js';
|
|
25
|
-
import { getOverrideEngineTargetPath, isOverrideCopyCandidate
|
|
26
|
+
import { getOverrideEngineTargetPath, isOverrideCopyCandidate } from './furnace-apply-helpers.js';
|
|
26
27
|
import { FTL_DIR } from './furnace-constants.js';
|
|
28
|
+
import { isRegularFile } from './furnace-dir-entry.js';
|
|
27
29
|
import { checkRegistrationConsistency } from './furnace-validate-registration.js';
|
|
28
30
|
import { loadPatchesManifest } from './patch-manifest.js';
|
|
29
31
|
import { buildPatchClaims } from './status-classify.js';
|
|
@@ -86,7 +88,7 @@ async function findOverrideOverwrites(args) {
|
|
|
86
88
|
// A missing target is a fresh deploy, not a lost engine edit.
|
|
87
89
|
if (!(await pathExists(enginePath)))
|
|
88
90
|
continue;
|
|
89
|
-
const engineRel = relative(engineDir, enginePath)
|
|
91
|
+
const engineRel = normalizePathSlashes(relative(engineDir, enginePath));
|
|
90
92
|
const owners = patchClaims.get(engineRel);
|
|
91
93
|
if (owners === undefined || owners.length === 0)
|
|
92
94
|
continue;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ApplyResult, DryRunAction } from '../types/furnace.js';
|
|
2
2
|
import { type FurnaceOperationContext } from './furnace-operation.js';
|
|
3
3
|
import { type RollbackJournal } from './furnace-rollback.js';
|
|
4
|
-
export { computeComponentChecksums,
|
|
4
|
+
export { computeComponentChecksums, prefixChecksums } from './furnace-apply-helpers.js';
|
|
5
5
|
/**
|
|
6
6
|
* Result of {@link applyAllComponents}.
|
|
7
7
|
*
|
|
@@ -19,7 +19,18 @@ import { addJarMnEntries, removeCustomElementRegistration, removeJarMnEntries, }
|
|
|
19
19
|
import { createRollbackJournal, restoreRollbackJournalOrThrow, snapshotFile, } from './furnace-rollback.js';
|
|
20
20
|
import { blockingStepErrors, hasBlockingStepErrors } from './furnace-step-errors.js';
|
|
21
21
|
import { runPostApplyConsistencyChecks } from './furnace-validate-registration.js';
|
|
22
|
-
export { computeComponentChecksums,
|
|
22
|
+
export { computeComponentChecksums, prefixChecksums } from './furnace-apply-helpers.js';
|
|
23
|
+
/**
|
|
24
|
+
* Records one component's checksums into the run-wide accumulator, under the
|
|
25
|
+
* flattened `type/name/file` keys the state file stores. A Map keeps the
|
|
26
|
+
* accumulation explicit — the previous `Object.assign` merges read as if they
|
|
27
|
+
* replaced the accumulator rather than adding to it.
|
|
28
|
+
*/
|
|
29
|
+
function recordComponentChecksums(accumulator, checksums, type, name) {
|
|
30
|
+
for (const [key, value] of Object.entries(prefixChecksums(checksums, type, name))) {
|
|
31
|
+
accumulator.set(key, value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
23
34
|
function addMissingComponentError(result, name, directoryPath) {
|
|
24
35
|
result.errors.push({
|
|
25
36
|
name,
|
|
@@ -54,7 +65,7 @@ async function applyOverrideBatch(ctx) {
|
|
|
54
65
|
const drifted = await hasOverrideEngineDrift({ engineDir, componentDir, ftlDir }, overrideConfig, cachedEngine);
|
|
55
66
|
if (!drifted) {
|
|
56
67
|
result.skipped.push({ name, reason: 'No changes since last apply' });
|
|
57
|
-
|
|
68
|
+
recordComponentChecksums(newChecksums, previous, 'override', name);
|
|
58
69
|
continue;
|
|
59
70
|
}
|
|
60
71
|
}
|
|
@@ -95,7 +106,7 @@ async function applyOverrideBatch(ctx) {
|
|
|
95
106
|
filesAffectedTotal.push(...filesAffected);
|
|
96
107
|
result.applied.push({ name, type: 'override', filesAffected: filesAffectedTotal });
|
|
97
108
|
if (!dryRun) {
|
|
98
|
-
|
|
109
|
+
recordComponentChecksums(newChecksums, currentChecksums, 'override', name);
|
|
99
110
|
}
|
|
100
111
|
}
|
|
101
112
|
catch (error) {
|
|
@@ -114,7 +125,8 @@ async function applyOverrideBatch(ctx) {
|
|
|
114
125
|
* the customElements.js registration. Snapshots are taken under the same
|
|
115
126
|
* journal so the undo path is symmetric with apply.
|
|
116
127
|
*/
|
|
117
|
-
async function reconcileCustomRegistrationAfterUndeploy(
|
|
128
|
+
async function reconcileCustomRegistrationAfterUndeploy(args) {
|
|
129
|
+
const { engineDir, name, config, deletedFiles, currentChecksums, rollbackJournal, filesAffected, } = args;
|
|
118
130
|
if (!config.register || deletedFiles.length === 0)
|
|
119
131
|
return;
|
|
120
132
|
const deletedRegistrationFiles = deletedFiles.filter((f) => f.endsWith('.mjs') || f.endsWith('.css'));
|
|
@@ -212,7 +224,7 @@ async function applyCustomBatch(ctx, root, markerComment) {
|
|
|
212
224
|
const drifted = await hasCustomEngineDrift({ root, name, componentDir, ftlDir }, customConfig);
|
|
213
225
|
if (!drifted) {
|
|
214
226
|
result.skipped.push({ name, reason: 'No changes since last apply' });
|
|
215
|
-
|
|
227
|
+
recordComponentChecksums(newChecksums, previous, 'custom', name);
|
|
216
228
|
continue;
|
|
217
229
|
}
|
|
218
230
|
}
|
|
@@ -250,7 +262,15 @@ async function applyCustomBatch(ctx, root, markerComment) {
|
|
|
250
262
|
allActions.push(...actions);
|
|
251
263
|
}
|
|
252
264
|
if (!dryRun && deletedFiles.length > 0 && blockingStepErrors(stepErrors).length === 0) {
|
|
253
|
-
await reconcileCustomRegistrationAfterUndeploy(
|
|
265
|
+
await reconcileCustomRegistrationAfterUndeploy({
|
|
266
|
+
engineDir,
|
|
267
|
+
name,
|
|
268
|
+
config: customConfig,
|
|
269
|
+
deletedFiles,
|
|
270
|
+
currentChecksums,
|
|
271
|
+
rollbackJournal,
|
|
272
|
+
filesAffected: filesAffectedTotal,
|
|
273
|
+
});
|
|
254
274
|
}
|
|
255
275
|
filesAffectedTotal.push(...filesAffected);
|
|
256
276
|
result.applied.push({
|
|
@@ -264,7 +284,7 @@ async function applyCustomBatch(ctx, root, markerComment) {
|
|
|
264
284
|
// the next run. Advisory errors (FTL degradation) still persist —
|
|
265
285
|
// re-applying cannot fix a fork that has no locale jar.mn.
|
|
266
286
|
if (!dryRun && blockingStepErrors(stepErrors).length === 0) {
|
|
267
|
-
|
|
287
|
+
recordComponentChecksums(newChecksums, currentChecksums, 'custom', name);
|
|
268
288
|
}
|
|
269
289
|
}
|
|
270
290
|
catch (error) {
|
|
@@ -330,7 +350,7 @@ export async function applyAllComponents(root, dryRun = false, options) {
|
|
|
330
350
|
errors: [],
|
|
331
351
|
};
|
|
332
352
|
const allActions = [];
|
|
333
|
-
const newChecksums =
|
|
353
|
+
const newChecksums = new Map();
|
|
334
354
|
const componentName = options?.componentName;
|
|
335
355
|
// When a single component is requested, validate it exists before running
|
|
336
356
|
// the batch functions (which would silently skip an unknown name).
|
|
@@ -406,8 +426,8 @@ export async function applyAllComponents(root, dryRun = false, options) {
|
|
|
406
426
|
// instead of byte-comparing against workspace sources.
|
|
407
427
|
await updateFurnaceState(root, {
|
|
408
428
|
lastApply: new Date().toISOString(),
|
|
409
|
-
appliedChecksums: newChecksums,
|
|
410
|
-
engineChecksums:
|
|
429
|
+
appliedChecksums: Object.fromEntries(newChecksums),
|
|
430
|
+
engineChecksums: Object.fromEntries(newChecksums),
|
|
411
431
|
});
|
|
412
432
|
}
|
|
413
433
|
else if (rollbackJournal) {
|
|
@@ -6,7 +6,7 @@ export declare const SHARED_FRAGMENTS_DIR = "shared";
|
|
|
6
6
|
/**
|
|
7
7
|
* Paths for furnace-related files and directories.
|
|
8
8
|
*/
|
|
9
|
-
interface FurnacePaths {
|
|
9
|
+
export interface FurnacePaths {
|
|
10
10
|
/** Path to furnace.json */
|
|
11
11
|
furnaceConfig: string;
|
|
12
12
|
/** Path to components directory */
|
|
@@ -53,25 +53,18 @@ export async function furnaceConfigExists(root) {
|
|
|
53
53
|
const paths = getFurnacePaths(root);
|
|
54
54
|
return pathExists(paths.furnaceConfig);
|
|
55
55
|
}
|
|
56
|
+
/** True when a recovered record carries at least one entry. */
|
|
57
|
+
function hasEntries(record) {
|
|
58
|
+
return Object.keys(record).length > 0;
|
|
59
|
+
}
|
|
56
60
|
/** The current (and only) config schema version. */
|
|
57
61
|
const CURRENT_CONFIG_VERSION = 1;
|
|
58
62
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* When a future version 2 is introduced, add a `case 1:` that transforms
|
|
63
|
-
* v1 data into v2 shape and falls through to validation. The pattern is:
|
|
64
|
-
*
|
|
65
|
-
* ```
|
|
66
|
-
* case 1:
|
|
67
|
-
* data = migrateV1ToV2(data);
|
|
68
|
-
* // fallthrough
|
|
69
|
-
* case 2:
|
|
70
|
-
* break;
|
|
71
|
-
* ```
|
|
63
|
+
* Rejects a config whose `version` field is not the single schema version that
|
|
64
|
+
* exists. A non-integer or sub-1 value is a malformed document; a value above
|
|
65
|
+
* the current version was written by a newer FireForge and cannot be read here.
|
|
72
66
|
*/
|
|
73
|
-
function
|
|
74
|
-
const version = data['version'];
|
|
67
|
+
function validateConfigVersion(version) {
|
|
75
68
|
if (typeof version !== 'number' || !Number.isInteger(version) || version < 1) {
|
|
76
69
|
throw new FurnaceError(`Furnace config: "version" must be a positive integer (got ${JSON.stringify(version)}). ` +
|
|
77
70
|
`Current schema version is ${CURRENT_CONFIG_VERSION}.`);
|
|
@@ -80,9 +73,6 @@ function migrateFurnaceConfig(data) {
|
|
|
80
73
|
throw new FurnaceError(`Furnace config: version ${version} is newer than what this version of FireForge supports (${CURRENT_CONFIG_VERSION}). ` +
|
|
81
74
|
'Upgrade FireForge to read this config.');
|
|
82
75
|
}
|
|
83
|
-
// Today only version 1 exists, so no migration is needed. When future
|
|
84
|
-
// versions are added, migration steps will be chained here.
|
|
85
|
-
return data;
|
|
86
76
|
}
|
|
87
77
|
/**
|
|
88
78
|
* Validates a raw config object and returns a typed FurnaceConfig.
|
|
@@ -97,31 +87,28 @@ export function validateFurnaceConfig(data) {
|
|
|
97
87
|
// Furnace config documents arrive from JSON.parse (loadFurnaceConfig /
|
|
98
88
|
// writeFurnaceConfig round-trips), which can only produce JSON values,
|
|
99
89
|
// so the object check above is the only invariant left to establish.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
throw new FurnaceError(`Furnace config: "version" must be ${CURRENT_CONFIG_VERSION} after migration`);
|
|
104
|
-
}
|
|
105
|
-
if (!isString(migrated['componentPrefix'])) {
|
|
90
|
+
const raw = data;
|
|
91
|
+
validateConfigVersion(raw['version']);
|
|
92
|
+
if (!isString(raw['componentPrefix'])) {
|
|
106
93
|
throw new FurnaceError('Furnace config: "componentPrefix" must be a string');
|
|
107
94
|
}
|
|
108
95
|
// Validate optional tokenPrefix
|
|
109
|
-
if (
|
|
96
|
+
if (raw['tokenPrefix'] !== undefined && !isString(raw['tokenPrefix'])) {
|
|
110
97
|
throw new FurnaceError('Furnace config: "tokenPrefix" must be a string if provided');
|
|
111
98
|
}
|
|
112
99
|
// Validate optional tokenAllowlist
|
|
113
|
-
if (
|
|
114
|
-
parseStringArray(
|
|
100
|
+
if (raw['tokenAllowlist'] !== undefined) {
|
|
101
|
+
parseStringArray(raw['tokenAllowlist'], 'tokenAllowlist');
|
|
115
102
|
}
|
|
116
103
|
// Validate optional runtimeVariables — CSS runtime state channels
|
|
117
104
|
// (e.g. `--cam-x`) that are exempt from `token-prefix-violation`.
|
|
118
|
-
validateRuntimeVariables(
|
|
105
|
+
validateRuntimeVariables(raw['runtimeVariables']);
|
|
119
106
|
// Validate optional tokenHostDocuments — list of chrome XHTMLs that the
|
|
120
107
|
// `missing-token-link` validator scans for the tokens CSS link.
|
|
121
|
-
validateTokenHostDocuments(
|
|
122
|
-
const stock = parseStockList(
|
|
123
|
-
const overrides = parseNamedComponentMap(
|
|
124
|
-
const custom = parseNamedComponentMap(
|
|
108
|
+
validateTokenHostDocuments(raw['tokenHostDocuments']);
|
|
109
|
+
const stock = parseStockList(raw['stock']);
|
|
110
|
+
const overrides = parseNamedComponentMap(raw['overrides'], 'override', 'overrides', parseOverrideConfig);
|
|
111
|
+
const custom = parseNamedComponentMap(raw['custom'], 'custom', 'custom', parseCustomConfig);
|
|
125
112
|
// Detect circular composes references among custom components.
|
|
126
113
|
detectComposesCycles(custom);
|
|
127
114
|
// Validate that every composes reference points to a known component.
|
|
@@ -146,12 +133,12 @@ export function validateFurnaceConfig(data) {
|
|
|
146
133
|
}
|
|
147
134
|
const config = {
|
|
148
135
|
version: CURRENT_CONFIG_VERSION,
|
|
149
|
-
componentPrefix:
|
|
136
|
+
componentPrefix: raw['componentPrefix'],
|
|
150
137
|
stock,
|
|
151
138
|
overrides,
|
|
152
139
|
custom,
|
|
153
140
|
};
|
|
154
|
-
applyOptionalFurnaceFields(
|
|
141
|
+
applyOptionalFurnaceFields(raw, config);
|
|
155
142
|
return config;
|
|
156
143
|
}
|
|
157
144
|
/**
|
|
@@ -228,7 +215,7 @@ function sanitizeFurnaceState(data) {
|
|
|
228
215
|
}
|
|
229
216
|
appliedChecksums[filePath] = checksum;
|
|
230
217
|
}
|
|
231
|
-
if (
|
|
218
|
+
if (hasEntries(appliedChecksums) || !hasInvalidChecksum) {
|
|
232
219
|
state.appliedChecksums = appliedChecksums;
|
|
233
220
|
recoveredFields.push('appliedChecksums');
|
|
234
221
|
}
|
|
@@ -245,7 +232,7 @@ function sanitizeFurnaceState(data) {
|
|
|
245
232
|
engineChecksums[filePath] = checksum;
|
|
246
233
|
}
|
|
247
234
|
}
|
|
248
|
-
if (
|
|
235
|
+
if (hasEntries(engineChecksums)) {
|
|
249
236
|
state.engineChecksums = engineChecksums;
|
|
250
237
|
recoveredFields.push('engineChecksums');
|
|
251
238
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
/** Path to customElements.js within the engine source tree */
|
|
3
3
|
export const CUSTOM_ELEMENTS_JS = 'toolkit/content/customElements.js';
|
|
4
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
4
5
|
/** Path to jar.mn within the engine source tree (toolkit global) */
|
|
5
6
|
export const JAR_MN = 'toolkit/content/jar.mn';
|
|
6
7
|
/**
|
|
@@ -78,7 +79,7 @@ export function resolveFtlDir(configuredPath) {
|
|
|
78
79
|
* degrade gracefully rather than inventing a path.
|
|
79
80
|
*/
|
|
80
81
|
export function resolveFtlChromeSubPath(ftlBasePath) {
|
|
81
|
-
const path = (ftlBasePath ?? FTL_DIR)
|
|
82
|
+
const path = normalizePathSlashes(ftlBasePath ?? FTL_DIR);
|
|
82
83
|
const match = /^(.*?)\/locales\/[^/]+\/(.+?)\/?$/.exec(path);
|
|
83
84
|
if (!match?.[2])
|
|
84
85
|
return undefined;
|
|
@@ -93,7 +94,7 @@ export function resolveFtlChromeSubPath(ftlBasePath) {
|
|
|
93
94
|
* callers must treat that as "cannot locate" and degrade gracefully.
|
|
94
95
|
*/
|
|
95
96
|
export function resolveFtlLocaleJarMnPath(ftlBasePath) {
|
|
96
|
-
const path = (ftlBasePath ?? FTL_DIR)
|
|
97
|
+
const path = normalizePathSlashes(ftlBasePath ?? FTL_DIR);
|
|
97
98
|
const match = /^(.*?)\/locales\/[^/]+\//.exec(path);
|
|
98
99
|
if (!match?.[1])
|
|
99
100
|
return undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The minimal `readdir(..., { withFileTypes: true })` entry shape the furnace
|
|
3
|
+
* apply path needs, plus the single predicate that decides whether an entry is
|
|
4
|
+
* a copy candidate. Kept in its own leaf module so both `furnace-apply-helpers`
|
|
5
|
+
* and its dry-run mirror can share one definition without importing each other.
|
|
6
|
+
*/
|
|
7
|
+
/** Structural subset of `Dirent` used by the furnace apply/dry-run walkers. */
|
|
8
|
+
export interface DirectoryEntry {
|
|
9
|
+
isFile(): boolean;
|
|
10
|
+
isSymbolicLink?(): boolean;
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* True for a plain-file directory entry — symlinks and directories are
|
|
15
|
+
* never copy candidates.
|
|
16
|
+
*
|
|
17
|
+
* @param entry - Directory entry to classify
|
|
18
|
+
* @returns True when the entry is a regular file
|
|
19
|
+
*/
|
|
20
|
+
export declare function isRegularFile(entry: DirectoryEntry): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The minimal `readdir(..., { withFileTypes: true })` entry shape the furnace
|
|
4
|
+
* apply path needs, plus the single predicate that decides whether an entry is
|
|
5
|
+
* a copy candidate. Kept in its own leaf module so both `furnace-apply-helpers`
|
|
6
|
+
* and its dry-run mirror can share one definition without importing each other.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* True for a plain-file directory entry — symlinks and directories are
|
|
10
|
+
* never copy candidates.
|
|
11
|
+
*
|
|
12
|
+
* @param entry - Directory entry to classify
|
|
13
|
+
* @returns True when the entry is a regular file
|
|
14
|
+
*/
|
|
15
|
+
export function isRegularFile(entry) {
|
|
16
|
+
if (!entry.isFile())
|
|
17
|
+
return false;
|
|
18
|
+
if (typeof entry.isSymbolicLink === 'function' && entry.isSymbolicLink())
|
|
19
|
+
return false;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
@@ -188,5 +188,7 @@ export declare function recordFurnaceRollbackFailure(root: string, operation: Fu
|
|
|
188
188
|
* reuse the module across tests, so the test suite must call this between
|
|
189
189
|
* cases that exercise the signal pathway. Not exported from the package
|
|
190
190
|
* entry point.
|
|
191
|
+
*
|
|
192
|
+
* @internal Exported only so tests can reach it; not part of the public surface.
|
|
191
193
|
*/
|
|
192
194
|
export declare function __resetFurnaceOperationStateForTests(): void;
|
|
@@ -381,7 +381,7 @@ export async function runFurnaceMutation(root, kind, body, options = {}) {
|
|
|
381
381
|
const lockStatus = await readLockStatus(lockPath);
|
|
382
382
|
assert(lockStatus.held, 'furnace lock held before the mutation body runs');
|
|
383
383
|
assert(lockStatus.holder === undefined || lockStatus.holder.pid === process.pid, () => `furnace lock is owned by this process ` +
|
|
384
|
-
`(held by PID ${
|
|
384
|
+
`(held by PID ${lockStatus.holder?.pid}, we are ${process.pid})`);
|
|
385
385
|
activeOperations.set(token, operation);
|
|
386
386
|
try {
|
|
387
387
|
return await body({
|
|
@@ -432,6 +432,8 @@ export async function recordFurnaceRollbackFailure(root, operation, reason) {
|
|
|
432
432
|
* reuse the module across tests, so the test suite must call this between
|
|
433
433
|
* cases that exercise the signal pathway. Not exported from the package
|
|
434
434
|
* entry point.
|
|
435
|
+
*
|
|
436
|
+
* @internal Exported only so tests can reach it; not part of the public surface.
|
|
435
437
|
*/
|
|
436
438
|
export function __resetFurnaceOperationStateForTests() {
|
|
437
439
|
activeOperations.clear();
|
|
@@ -3,14 +3,26 @@ export interface RefreshFileResult {
|
|
|
3
3
|
status: 'merged' | 'conflict' | 'unchanged' | 'new-file';
|
|
4
4
|
conflictMarkers?: number;
|
|
5
5
|
}
|
|
6
|
+
export interface RefreshOverrideFileOptions {
|
|
7
|
+
/** Path to the engine git repository. */
|
|
8
|
+
engineDir: string;
|
|
9
|
+
/** Path to the current override file in the workspace. */
|
|
10
|
+
overridePath: string;
|
|
11
|
+
/** Engine-relative path for git show. */
|
|
12
|
+
engineRelPath: string;
|
|
13
|
+
/** The git ref at which the override was originally created. */
|
|
14
|
+
baseCommit: string;
|
|
15
|
+
/** Display name for the file. */
|
|
16
|
+
fileName: string;
|
|
17
|
+
/** When true, compute the merge but write nothing. */
|
|
18
|
+
dryRun?: boolean | undefined;
|
|
19
|
+
/** Conflict resolution preference handed to `git merge-file`. */
|
|
20
|
+
strategy?: 'ours' | 'theirs' | undefined;
|
|
21
|
+
}
|
|
6
22
|
/**
|
|
7
23
|
* Refreshes a single override file against the current engine HEAD.
|
|
8
24
|
*
|
|
9
|
-
* @param
|
|
10
|
-
* @param overridePath - Path to the current override file in the workspace
|
|
11
|
-
* @param engineRelPath - Engine-relative path for git show
|
|
12
|
-
* @param baseCommit - The git ref at which the override was originally created
|
|
13
|
-
* @param fileName - Display name for the file
|
|
25
|
+
* @param options - See {@link RefreshOverrideFileOptions}
|
|
14
26
|
* @returns Merge result with the updated content written to the override file
|
|
15
27
|
*/
|
|
16
|
-
export declare function refreshOverrideFile(
|
|
28
|
+
export declare function refreshOverrideFile(options: RefreshOverrideFileOptions): Promise<RefreshFileResult>;
|
|
@@ -81,14 +81,11 @@ async function threeWayMergeFile(base, ours, theirs, label, strategy) {
|
|
|
81
81
|
/**
|
|
82
82
|
* Refreshes a single override file against the current engine HEAD.
|
|
83
83
|
*
|
|
84
|
-
* @param
|
|
85
|
-
* @param overridePath - Path to the current override file in the workspace
|
|
86
|
-
* @param engineRelPath - Engine-relative path for git show
|
|
87
|
-
* @param baseCommit - The git ref at which the override was originally created
|
|
88
|
-
* @param fileName - Display name for the file
|
|
84
|
+
* @param options - See {@link RefreshOverrideFileOptions}
|
|
89
85
|
* @returns Merge result with the updated content written to the override file
|
|
90
86
|
*/
|
|
91
|
-
export async function refreshOverrideFile(
|
|
87
|
+
export async function refreshOverrideFile(options) {
|
|
88
|
+
const { engineDir, overridePath, engineRelPath, baseCommit, fileName, dryRun, strategy } = options;
|
|
92
89
|
// Read the three versions
|
|
93
90
|
const oursContent = await readText(overridePath);
|
|
94
91
|
const baseContent = await getFileContentAtRef(engineDir, engineRelPath, baseCommit);
|
|
@@ -46,9 +46,6 @@ function formatMarkerSuffix(markerComment) {
|
|
|
46
46
|
return '';
|
|
47
47
|
return ` // ${markerComment}:`;
|
|
48
48
|
}
|
|
49
|
-
// Re-export from split modules so existing import sites continue working
|
|
50
|
-
export { removeCustomElementRegistration } from './furnace-registration-remove.js';
|
|
51
|
-
// Re-export constants so existing import sites continue working
|
|
52
49
|
// ---------------------------------------------------------------------------
|
|
53
50
|
// Helpers
|
|
54
51
|
// ---------------------------------------------------------------------------
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { addCustomElementRegistration,
|
|
1
|
+
export { addCustomElementRegistration, validateCustomElementRegistration, } from './furnace-registration-ast.js';
|
|
2
|
+
export { removeCustomElementRegistration } from './furnace-registration-remove.js';
|
|
2
3
|
/**
|
|
3
4
|
* Adds jar.mn entries that map chrome:// URIs to on-disk paths for a
|
|
4
5
|
* component's files.
|
|
@@ -2,22 +2,31 @@
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { FurnaceError } from '../errors/furnace.js';
|
|
4
4
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
5
|
+
/**
|
|
6
|
+
* Returns the leading whitespace of the first line matching `pattern`, whose
|
|
7
|
+
* first capture group must be that indentation. `undefined` when no line
|
|
8
|
+
* matches, so callers can chain progressively looser patterns before falling
|
|
9
|
+
* back to a convention default.
|
|
10
|
+
*/
|
|
11
|
+
function detectIndent(lines, pattern) {
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
const match = pattern.exec(line);
|
|
14
|
+
if (match?.[1])
|
|
15
|
+
return match[1];
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
5
19
|
/**
|
|
6
20
|
* Detects the indentation used by existing `content/global/elements/` lines
|
|
7
21
|
* in jar.mn. Falls back to 3 spaces (the historical Firefox convention) when
|
|
8
22
|
* no reference line is found.
|
|
9
23
|
*/
|
|
10
24
|
function detectJarMnIndent(lines) {
|
|
11
|
-
|
|
12
|
-
const match = /^(\s+)content\/global\/elements\//.exec(line);
|
|
13
|
-
if (match?.[1]) {
|
|
14
|
-
return match[1];
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return ' ';
|
|
25
|
+
return detectIndent(lines, /^(\s+)content\/global\/elements\//) ?? ' ';
|
|
18
26
|
}
|
|
19
27
|
// Re-export everything from the AST module so existing imports keep working
|
|
20
|
-
export { addCustomElementRegistration,
|
|
28
|
+
export { addCustomElementRegistration, validateCustomElementRegistration, } from './furnace-registration-ast.js';
|
|
29
|
+
export { removeCustomElementRegistration } from './furnace-registration-remove.js';
|
|
21
30
|
import { escapeRegex } from '../utils/regex.js';
|
|
22
31
|
import { JAR_MN } from './furnace-constants.js';
|
|
23
32
|
/**
|
|
@@ -72,10 +81,7 @@ export async function addJarMnEntries(engineDir, tagName, files) {
|
|
|
72
81
|
// Find insertion point among existing content/global/elements/ lines
|
|
73
82
|
const elementLinePattern = /^\s+content\/global\/elements\/([^.]+)\./;
|
|
74
83
|
let insertIndex = -1;
|
|
75
|
-
for (
|
|
76
|
-
const line = lines[i];
|
|
77
|
-
if (line === undefined)
|
|
78
|
-
continue;
|
|
84
|
+
for (const [i, line] of lines.entries()) {
|
|
79
85
|
const match = elementLinePattern.exec(line);
|
|
80
86
|
if (match) {
|
|
81
87
|
const existingTag = match[1] ?? '';
|
|
@@ -148,10 +154,7 @@ export async function addLocaleFtlJarMnEntry(engineDir, jarMnRelPath, tagName, c
|
|
|
148
154
|
const newEntry = `${indent}locale/@AB_CD@/${chromeSubPath}/${ftlFile} (%${chromeSubPath}/${ftlFile})`;
|
|
149
155
|
const sectionPattern = new RegExp(`^(\\s+)locale\\/(?:@AB_CD@|[a-zA-Z-]+)\\/${escapedChrome}\\/([^.\\s]+)\\.ftl`);
|
|
150
156
|
let insertIndex = -1;
|
|
151
|
-
for (
|
|
152
|
-
const line = lines[i];
|
|
153
|
-
if (line === undefined)
|
|
154
|
-
continue;
|
|
157
|
+
for (const [i, line] of lines.entries()) {
|
|
155
158
|
const match = sectionPattern.exec(line);
|
|
156
159
|
if (match) {
|
|
157
160
|
const existingTag = match[2] ?? '';
|
|
@@ -174,19 +177,9 @@ export async function addLocaleFtlJarMnEntry(engineDir, jarMnRelPath, tagName, c
|
|
|
174
177
|
/** Detects locale jar.mn indentation by sampling an existing matching entry. */
|
|
175
178
|
function detectLocaleJarMnIndent(lines, chromeSubPath) {
|
|
176
179
|
const escapedChrome = escapeRegex(chromeSubPath);
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (match?.[1])
|
|
181
|
-
return match[1];
|
|
182
|
-
}
|
|
183
|
-
// Fall back to detecting any existing `locale/...` indent before giving up.
|
|
184
|
-
for (const line of lines) {
|
|
185
|
-
const match = /^(\s+)locale\//.exec(line);
|
|
186
|
-
if (match?.[1])
|
|
187
|
-
return match[1];
|
|
188
|
-
}
|
|
189
|
-
return ' ';
|
|
180
|
+
const exact = new RegExp(`^(\\s+)locale\\/(?:@AB_CD@|[a-zA-Z-]+)\\/${escapedChrome}\\/`);
|
|
181
|
+
// Fall back to any existing `locale/...` indent before giving up.
|
|
182
|
+
return detectIndent(lines, exact) ?? detectIndent(lines, /^(\s+)locale\//) ?? ' ';
|
|
190
183
|
}
|
|
191
184
|
/**
|
|
192
185
|
* Removes a locale jar.mn entry previously written by `addLocaleFtlJarMnEntry`.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
2
|
import { readdir } from 'node:fs/promises';
|
|
4
3
|
import { join } from 'node:path';
|
|
5
4
|
import { toError } from '../utils/errors.js';
|
|
6
5
|
import { pathExists, readText } from '../utils/fs.js';
|
|
6
|
+
import { sha256Hex } from '../utils/hash.js';
|
|
7
7
|
import { verbose, warn } from '../utils/logger.js';
|
|
8
8
|
import { asEstree, getNodeSource, parseScript, walkAST } from './ast-utils.js';
|
|
9
9
|
import { CUSTOM_ELEMENTS_JS, FTL_DIR, WIDGETS_DIR } from './furnace-constants.js';
|
|
@@ -48,7 +48,7 @@ export async function scanCustomElementsRegistrations(engineDir) {
|
|
|
48
48
|
return registrations;
|
|
49
49
|
}
|
|
50
50
|
const content = await readText(filePath);
|
|
51
|
-
const contentHash =
|
|
51
|
+
const contentHash = sha256Hex(content);
|
|
52
52
|
// Return cached result if file hasn't changed since last parse.
|
|
53
53
|
if (registrationCache && registrationCache.hash === contentHash) {
|
|
54
54
|
return new Map(registrationCache.registrations);
|
|
@@ -121,10 +121,7 @@ export async function scanCustomElementsRegistrations(engineDir) {
|
|
|
121
121
|
'Results may be incomplete — run "fireforge furnace validate" to verify registration consistency.');
|
|
122
122
|
verbose(`Scanner regex fallback activated due to parse error: ${reason}`);
|
|
123
123
|
const lines = content.split('\n');
|
|
124
|
-
for (
|
|
125
|
-
const line = lines[i];
|
|
126
|
-
if (line === undefined)
|
|
127
|
-
continue;
|
|
124
|
+
for (const [i, line] of lines.entries()) {
|
|
128
125
|
const callbackMatch = /setElementCreationCallback\(\s*"([^"]+)"/.exec(line);
|
|
129
126
|
if (!callbackMatch?.[1])
|
|
130
127
|
continue;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `furnace validate`.
|
|
21
21
|
*/
|
|
22
22
|
import { type ApplyAllComponentsResult } from './furnace-apply.js';
|
|
23
|
-
import { type
|
|
23
|
+
import { type FurnacePaths } from './furnace-config.js';
|
|
24
24
|
/**
|
|
25
25
|
* Counts applied entries that carry at least one BLOCKING step error
|
|
26
26
|
* (advisory step errors are warnings and never fail a run).
|
|
@@ -65,4 +65,4 @@ export declare function getPersistableAppliedEntry(commandLabel: string, name: s
|
|
|
65
65
|
export declare function persistSingleComponentState(projectRoot: string, appliedEntry: {
|
|
66
66
|
name: string;
|
|
67
67
|
type: 'override' | 'custom';
|
|
68
|
-
}, furnacePaths:
|
|
68
|
+
}, furnacePaths: FurnacePaths): Promise<void>;
|