@hominis/fireforge 0.35.0 → 0.37.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 +42 -0
- package/README.md +34 -9
- package/dist/bin/fireforge.js +21 -6
- package/dist/src/cli.js +22 -5
- package/dist/src/commands/bootstrap-checks.js +0 -1
- package/dist/src/commands/bootstrap.d.ts +2 -1
- package/dist/src/commands/bootstrap.js +16 -7
- package/dist/src/commands/build.js +5 -4
- package/dist/src/commands/config.js +30 -10
- package/dist/src/commands/discard.js +18 -3
- package/dist/src/commands/doctor/post-rebase-audit.js +0 -1
- package/dist/src/commands/doctor-check-core.js +0 -1
- package/dist/src/commands/doctor-external-toolchains.d.ts +3 -0
- package/dist/src/commands/doctor-external-toolchains.js +54 -0
- package/dist/src/commands/doctor-furnace-jar.js +0 -1
- package/dist/src/commands/doctor-furnace-manifest-sync.js +0 -1
- package/dist/src/commands/doctor-furnace.js +2 -2
- package/dist/src/commands/doctor-orphaned-harness.d.ts +52 -0
- package/dist/src/commands/doctor-orphaned-harness.js +132 -0
- package/dist/src/commands/doctor-working-tree.js +0 -1
- package/dist/src/commands/doctor.js +6 -1
- package/dist/src/commands/download.js +28 -13
- package/dist/src/commands/export-all.js +5 -3
- package/dist/src/commands/export-flow.js +12 -7
- package/dist/src/commands/export-placement-gate.js +0 -1
- package/dist/src/commands/export-placement-policy.js +0 -1
- package/dist/src/commands/export-shared.d.ts +5 -1
- package/dist/src/commands/export-shared.js +18 -6
- package/dist/src/commands/export.js +22 -10
- package/dist/src/commands/furnace/apply.js +22 -6
- package/dist/src/commands/furnace/chrome-doc-remove.js +0 -1
- package/dist/src/commands/furnace/chrome-doc-templates.js +0 -1
- package/dist/src/commands/furnace/chrome-doc-tests.js +0 -1
- package/dist/src/commands/furnace/chrome-doc.js +0 -1
- package/dist/src/commands/furnace/create-browser-test.js +0 -1
- package/dist/src/commands/furnace/create-dry-run.js +0 -1
- package/dist/src/commands/furnace/create-features.js +0 -1
- package/dist/src/commands/furnace/create-mochikit.js +0 -1
- package/dist/src/commands/furnace/create-readback.js +0 -1
- package/dist/src/commands/furnace/create-templates.js +0 -1
- package/dist/src/commands/furnace/create-validation.js +0 -1
- package/dist/src/commands/furnace/create-xpcshell.js +0 -1
- package/dist/src/commands/furnace/create.js +0 -1
- package/dist/src/commands/furnace/deploy.js +7 -83
- package/dist/src/commands/furnace/diff.js +0 -1
- package/dist/src/commands/furnace/index.js +59 -25
- package/dist/src/commands/furnace/init.js +0 -1
- package/dist/src/commands/furnace/list.js +0 -1
- package/dist/src/commands/furnace/override.js +0 -1
- package/dist/src/commands/furnace/preview.js +3 -3
- package/dist/src/commands/furnace/refresh.js +0 -1
- package/dist/src/commands/furnace/remove-state.d.ts +15 -0
- package/dist/src/commands/furnace/remove-state.js +47 -1
- package/dist/src/commands/furnace/remove.js +3 -9
- package/dist/src/commands/furnace/rename-browser-test.js +1 -5
- package/dist/src/commands/furnace/rename-helpers.d.ts +6 -0
- package/dist/src/commands/furnace/rename-helpers.js +30 -1
- package/dist/src/commands/furnace/rename-xpcshell.js +7 -6
- package/dist/src/commands/furnace/rename.js +15 -36
- package/dist/src/commands/furnace/scan.js +0 -1
- package/dist/src/commands/furnace/status.js +0 -1
- package/dist/src/commands/furnace/sync.js +76 -4
- package/dist/src/commands/furnace/validate.js +0 -1
- package/dist/src/commands/furnace/validation-output.js +0 -1
- package/dist/src/commands/import.js +35 -9
- package/dist/src/commands/lint-per-patch.js +19 -7
- package/dist/src/commands/lint.js +13 -3
- package/dist/src/commands/manifest.js +1 -2
- package/dist/src/commands/package.js +2 -1
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/delete.js +1 -1
- package/dist/src/commands/patch/index.js +1 -1
- package/dist/src/commands/patch/lint-ignore.js +21 -6
- package/dist/src/commands/patch/move-files.js +1 -1
- package/dist/src/commands/patch/patch-context.js +0 -1
- package/dist/src/commands/patch/rename.js +4 -4
- package/dist/src/commands/patch/reorder.js +3 -4
- package/dist/src/commands/patch/split-plan.js +2 -1
- package/dist/src/commands/patch/split.js +4 -4
- package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
- package/dist/src/commands/patch/staged-dependency.js +114 -52
- package/dist/src/commands/patch/tier.js +18 -5
- package/dist/src/commands/re-export-bulk-scan.js +0 -1
- package/dist/src/commands/re-export-files.js +5 -1
- package/dist/src/commands/re-export-options.js +0 -1
- package/dist/src/commands/re-export-scan.js +0 -1
- package/dist/src/commands/re-export.js +14 -4
- package/dist/src/commands/rebase/abort.js +2 -9
- package/dist/src/commands/rebase/confirm.js +0 -1
- package/dist/src/commands/rebase/conflict-summary.js +0 -1
- package/dist/src/commands/rebase/continue.js +0 -1
- package/dist/src/commands/rebase/index.d.ts +1 -1
- package/dist/src/commands/rebase/index.js +10 -15
- package/dist/src/commands/rebase/patch-loop.js +38 -7
- package/dist/src/commands/rebase/summary.js +2 -3
- package/dist/src/commands/rebase.js +0 -1
- package/dist/src/commands/register.js +1 -1
- package/dist/src/commands/reset.js +16 -9
- package/dist/src/commands/resolve.js +2 -2
- package/dist/src/commands/run.js +4 -4
- package/dist/src/commands/setup-support.js +0 -1
- package/dist/src/commands/setup.js +1 -2
- package/dist/src/commands/source.js +1 -2
- package/dist/src/commands/status-output.js +1 -12
- package/dist/src/commands/status.js +147 -133
- package/dist/src/commands/test-appdir.js +0 -1
- package/dist/src/commands/test-diagnose.js +38 -3
- package/dist/src/commands/test-modes.d.ts +15 -0
- package/dist/src/commands/test-modes.js +61 -0
- package/dist/src/commands/test-register.d.ts +4 -0
- package/dist/src/commands/test-register.js +64 -0
- package/dist/src/commands/test-run.d.ts +23 -7
- package/dist/src/commands/test-run.js +15 -14
- package/dist/src/commands/test.d.ts +0 -4
- package/dist/src/commands/test.js +147 -96
- package/dist/src/commands/token-coverage.js +0 -1
- package/dist/src/commands/token.js +1 -2
- package/dist/src/commands/typecheck.js +1 -1
- package/dist/src/commands/verify.js +3 -9
- package/dist/src/commands/watch.js +5 -1
- package/dist/src/commands/wire.js +1 -1
- package/dist/src/core/ast-utils.js +0 -1
- package/dist/src/core/brand-validation.js +0 -1
- package/dist/src/core/branding.js +0 -1
- package/dist/src/core/browser-wire.js +0 -1
- package/dist/src/core/build-audit-platform.js +0 -1
- package/dist/src/core/build-audit-registration.js +0 -1
- package/dist/src/core/build-audit-resolve.js +0 -1
- package/dist/src/core/build-audit-transforms.js +0 -1
- package/dist/src/core/build-audit.js +0 -1
- package/dist/src/core/build-baseline-types.d.ts +24 -0
- package/dist/src/core/build-baseline-types.js +0 -1
- package/dist/src/core/build-baseline.d.ts +5 -2
- package/dist/src/core/build-baseline.js +5 -2
- package/dist/src/core/build-prepare.js +2 -2
- package/dist/src/core/config-mutate.js +0 -1
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +6 -1
- package/dist/src/core/config-state.js +0 -1
- package/dist/src/core/config-validate-patch-policy.js +0 -1
- package/dist/src/core/config-validate-test-toolchains.d.ts +8 -0
- package/dist/src/core/config-validate-test-toolchains.js +105 -0
- package/dist/src/core/config-validate.js +3 -1
- package/dist/src/core/config.js +0 -1
- package/dist/src/core/destructive.js +0 -1
- package/dist/src/core/diff-hunks.js +13 -7
- package/dist/src/core/engine-changes.js +0 -1
- package/dist/src/core/engine-session-lock.d.ts +12 -0
- package/dist/src/core/engine-session-lock.js +45 -0
- package/dist/src/core/file-lock.d.ts +8 -0
- package/dist/src/core/file-lock.js +157 -48
- package/dist/src/core/firefox-archive.d.ts +4 -0
- package/dist/src/core/firefox-archive.js +6 -1
- package/dist/src/core/firefox-cache.js +60 -4
- package/dist/src/core/firefox-download.js +0 -1
- package/dist/src/core/firefox-extract.js +75 -16
- package/dist/src/core/firefox-ignorefile.js +0 -1
- package/dist/src/core/firefox.d.ts +12 -0
- package/dist/src/core/firefox.js +64 -2
- package/dist/src/core/furnace-apply-dry-run.js +0 -1
- package/dist/src/core/furnace-apply-ftl.d.ts +7 -3
- package/dist/src/core/furnace-apply-ftl.js +10 -5
- package/dist/src/core/furnace-apply-helpers.js +0 -1
- package/dist/src/core/furnace-apply-output.js +2 -2
- package/dist/src/core/furnace-apply.d.ts +7 -1
- package/dist/src/core/furnace-apply.js +47 -13
- package/dist/src/core/furnace-checksum-utils.js +0 -1
- package/dist/src/core/furnace-config-array-utils.js +0 -1
- package/dist/src/core/furnace-config-custom.js +10 -1
- package/dist/src/core/furnace-config-order.js +0 -1
- package/dist/src/core/furnace-config-tokens.js +0 -1
- package/dist/src/core/furnace-config-validate.js +0 -1
- package/dist/src/core/furnace-config.d.ts +11 -0
- package/dist/src/core/furnace-config.js +37 -1
- package/dist/src/core/furnace-constants.js +0 -1
- package/dist/src/core/furnace-css-fragments.js +0 -1
- package/dist/src/core/furnace-graph-utils.js +0 -1
- package/dist/src/core/furnace-jsconfig.d.ts +2 -3
- package/dist/src/core/furnace-jsconfig.js +25 -18
- package/dist/src/core/furnace-marker.js +0 -1
- package/dist/src/core/furnace-operation.js +0 -1
- package/dist/src/core/furnace-refresh.js +0 -1
- package/dist/src/core/furnace-registration-ast.d.ts +14 -0
- package/dist/src/core/furnace-registration-ast.js +1 -2
- package/dist/src/core/furnace-registration-remove.js +0 -1
- package/dist/src/core/furnace-registration-validate.js +1 -5
- package/dist/src/core/furnace-registration.js +0 -1
- package/dist/src/core/furnace-rollback.js +0 -1
- package/dist/src/core/furnace-scanner.js +0 -1
- package/dist/src/core/furnace-stale-export.d.ts +59 -0
- package/dist/src/core/furnace-stale-export.js +123 -0
- package/dist/src/core/furnace-staleness.js +0 -1
- package/dist/src/core/furnace-state-persist.d.ts +72 -0
- package/dist/src/core/furnace-state-persist.js +118 -0
- package/dist/src/core/furnace-step-errors.d.ts +23 -0
- package/dist/src/core/furnace-step-errors.js +25 -0
- package/dist/src/core/furnace-stories.js +0 -1
- package/dist/src/core/furnace-validate-accessibility.js +0 -1
- package/dist/src/core/furnace-validate-checks.js +0 -1
- package/dist/src/core/furnace-validate-compatibility.js +9 -3
- package/dist/src/core/furnace-validate-helpers.js +0 -1
- package/dist/src/core/furnace-validate-registration.js +7 -7
- package/dist/src/core/furnace-validate-structure.d.ts +8 -0
- package/dist/src/core/furnace-validate-structure.js +14 -4
- package/dist/src/core/furnace-validate.js +0 -1
- package/dist/src/core/furnace-version-drift.d.ts +6 -3
- package/dist/src/core/furnace-version-drift.js +6 -4
- package/dist/src/core/git-base.js +0 -1
- package/dist/src/core/git-diff.js +0 -1
- package/dist/src/core/git-file-ops.js +0 -1
- package/dist/src/core/git-status.d.ts +13 -0
- package/dist/src/core/git-status.js +32 -2
- package/dist/src/core/git.d.ts +10 -1
- package/dist/src/core/git.js +63 -6
- package/dist/src/core/license-headers.js +0 -1
- package/dist/src/core/lint-cache.js +0 -1
- package/dist/src/core/lint-projection.js +0 -1
- package/dist/src/core/mach-build-artifacts.js +0 -1
- package/dist/src/core/mach-error-hints.js +0 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +60 -0
- package/dist/src/core/mach-known-noise-filter.js +193 -0
- package/dist/src/core/mach-mozconfig.js +0 -1
- package/dist/src/core/mach-python.js +0 -1
- package/dist/src/core/mach-resource-shim.js +0 -1
- package/dist/src/core/mach.d.ts +8 -0
- package/dist/src/core/mach.js +36 -4
- package/dist/src/core/manifest-helpers.js +0 -1
- package/dist/src/core/manifest-register.js +0 -1
- package/dist/src/core/manifest-rules.js +0 -1
- package/dist/src/core/manifest-tokenizers.js +0 -1
- package/dist/src/core/marionette-port.d.ts +8 -0
- package/dist/src/core/marionette-port.js +34 -3
- package/dist/src/core/marionette-preflight.js +0 -1
- package/dist/src/core/markdown-table.js +0 -1
- package/dist/src/core/ownership-table.js +2 -9
- package/dist/src/core/parser-fallback.js +0 -1
- package/dist/src/core/patch-apply-fuzz.d.ts +27 -9
- package/dist/src/core/patch-apply-fuzz.js +71 -34
- package/dist/src/core/patch-apply.d.ts +30 -0
- package/dist/src/core/patch-apply.js +101 -18
- package/dist/src/core/patch-artifact-normalize.d.ts +18 -5
- package/dist/src/core/patch-artifact-normalize.js +19 -15
- package/dist/src/core/patch-export-coverage.js +0 -1
- package/dist/src/core/patch-export-metadata.js +0 -1
- package/dist/src/core/patch-export-update.js +2 -3
- package/dist/src/core/patch-export.d.ts +2 -6
- package/dist/src/core/patch-export.js +24 -48
- package/dist/src/core/patch-files.js +0 -1
- package/dist/src/core/patch-identifier-suggest.js +0 -1
- package/dist/src/core/patch-lint-checkjs.js +0 -1
- package/dist/src/core/patch-lint-chrome-jsdoc.js +0 -1
- package/dist/src/core/patch-lint-cross.js +80 -3
- package/dist/src/core/patch-lint-css.js +0 -1
- package/dist/src/core/patch-lint-diff-tag.js +0 -1
- package/dist/src/core/patch-lint-diff.d.ts +2 -1
- package/dist/src/core/patch-lint-diff.js +17 -32
- package/dist/src/core/patch-lint-imports.js +0 -1
- package/dist/src/core/patch-lint-jsdoc.js +0 -1
- package/dist/src/core/patch-lint-observer.js +0 -1
- package/dist/src/core/patch-lint-ownership.js +0 -1
- package/dist/src/core/patch-lint-reexports.js +0 -1
- package/dist/src/core/patch-lint.d.ts +37 -2
- package/dist/src/core/patch-lint.js +47 -34
- package/dist/src/core/patch-lock.js +0 -1
- package/dist/src/core/patch-manifest-consistency.js +9 -1
- package/dist/src/core/patch-manifest-io.d.ts +19 -2
- package/dist/src/core/patch-manifest-io.js +57 -21
- package/dist/src/core/patch-manifest-query.js +18 -10
- package/dist/src/core/patch-manifest-resolve.js +0 -1
- package/dist/src/core/patch-manifest-validate.js +24 -1
- package/dist/src/core/patch-manifest.d.ts +1 -1
- package/dist/src/core/patch-manifest.js +1 -2
- package/dist/src/core/patch-parse.d.ts +59 -0
- package/dist/src/core/patch-parse.js +209 -92
- package/dist/src/core/patch-policy.js +0 -1
- package/dist/src/core/patch-registration-refs.js +16 -26
- package/dist/src/core/patch-source-metadata.js +0 -1
- package/dist/src/core/patch-transform.d.ts +4 -0
- package/dist/src/core/patch-transform.js +30 -38
- package/dist/src/core/rebase-session.js +0 -1
- package/dist/src/core/register-browser-content.js +0 -1
- package/dist/src/core/register-module.js +0 -1
- package/dist/src/core/register-result.js +0 -1
- package/dist/src/core/register-scaffold.js +0 -1
- package/dist/src/core/register-shared-css.js +0 -1
- package/dist/src/core/register-test-manifest.js +0 -1
- package/dist/src/core/register-xpcshell-test.js +0 -1
- package/dist/src/core/shared-ftl.js +0 -1
- package/dist/src/core/signal-critical.js +0 -1
- package/dist/src/core/smoke-patterns.js +0 -1
- package/dist/src/core/state-file.js +0 -1
- package/dist/src/core/status-classify.d.ts +16 -0
- package/dist/src/core/status-classify.js +24 -16
- package/dist/src/core/test-harness-crash.d.ts +80 -7
- package/dist/src/core/test-harness-crash.js +251 -15
- package/dist/src/core/test-harness-output.js +0 -1
- package/dist/src/core/test-path-scope.d.ts +21 -13
- package/dist/src/core/test-path-scope.js +43 -28
- package/dist/src/core/test-stale-check.d.ts +29 -1
- package/dist/src/core/test-stale-check.js +59 -1
- package/dist/src/core/test-stale-symlink.js +0 -1
- package/dist/src/core/test-xpcshell-retry.js +0 -1
- package/dist/src/core/token-coverage.js +0 -1
- package/dist/src/core/token-dark-mode.js +0 -1
- package/dist/src/core/token-docs.js +0 -1
- package/dist/src/core/token-manager.js +0 -1
- package/dist/src/core/token-scaffold.js +0 -1
- package/dist/src/core/token-variant.js +0 -1
- package/dist/src/core/toolchain-preflight.d.ts +31 -10
- package/dist/src/core/toolchain-preflight.js +97 -36
- package/dist/src/core/typecheck-shim.js +13 -1
- package/dist/src/core/typecheck.js +0 -1
- package/dist/src/core/wire-destroy.js +0 -1
- package/dist/src/core/wire-dom-fragment.js +0 -1
- package/dist/src/core/wire-init.js +0 -1
- package/dist/src/core/wire-subscript.js +0 -1
- package/dist/src/core/wire-targets.js +0 -1
- package/dist/src/core/wire-utils.js +0 -1
- package/dist/src/core/xpcshell-appdir.js +1 -4
- package/dist/src/errors/base.d.ts +29 -0
- package/dist/src/errors/base.js +35 -1
- package/dist/src/errors/build.js +0 -1
- package/dist/src/errors/codes.d.ts +6 -0
- package/dist/src/errors/codes.js +6 -1
- package/dist/src/errors/config.js +0 -1
- package/dist/src/errors/download.js +0 -1
- package/dist/src/errors/furnace.d.ts +11 -1
- package/dist/src/errors/furnace.js +6 -5
- package/dist/src/errors/git.js +0 -1
- package/dist/src/errors/patch.d.ts +17 -0
- package/dist/src/errors/patch.js +29 -1
- package/dist/src/errors/rebase.js +0 -1
- package/dist/src/errors/run.js +1 -1
- package/dist/src/index.js +0 -1
- package/dist/src/types/cli.js +0 -1
- package/dist/src/types/commands/index.d.ts +1 -1
- package/dist/src/types/commands/index.js +0 -1
- package/dist/src/types/commands/options.d.ts +40 -9
- package/dist/src/types/commands/options.js +0 -1
- package/dist/src/types/commands/patches.d.ts +37 -0
- package/dist/src/types/commands/patches.js +0 -1
- package/dist/src/types/commands/project.d.ts +11 -2
- package/dist/src/types/commands/project.js +0 -1
- package/dist/src/types/config.d.ts +32 -0
- package/dist/src/types/config.js +0 -1
- package/dist/src/types/furnace.d.ts +23 -0
- package/dist/src/types/furnace.js +0 -1
- package/dist/src/types/index.js +0 -1
- package/dist/src/types/typecheck.js +0 -1
- package/dist/src/utils/elapsed.js +0 -1
- package/dist/src/utils/errors.js +0 -1
- package/dist/src/utils/fs.js +37 -5
- package/dist/src/utils/logger.d.ts +7 -0
- package/dist/src/utils/logger.js +85 -3
- package/dist/src/utils/options.d.ts +15 -0
- package/dist/src/utils/options.js +25 -1
- package/dist/src/utils/package-root.js +11 -4
- package/dist/src/utils/parse.js +0 -1
- package/dist/src/utils/paths.js +0 -1
- package/dist/src/utils/platform.js +0 -1
- package/dist/src/utils/process-group.d.ts +33 -0
- package/dist/src/utils/process-group.js +161 -0
- package/dist/src/utils/process.d.ts +48 -4
- package/dist/src/utils/process.js +258 -59
- package/dist/src/utils/regex.d.ts +13 -3
- package/dist/src/utils/regex.js +53 -4
- package/dist/src/utils/validation.d.ts +0 -12
- package/dist/src/utils/validation.js +13 -26
- package/package.json +9 -8
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Stale-furnace-source gate for `fireforge patch export` / `re-export`.
|
|
4
|
+
*
|
|
5
|
+
* Exports capture the DEPLOYED engine copies of furnace-managed files, not
|
|
6
|
+
* the `components/` sources. Editing a component source and re-exporting
|
|
7
|
+
* its owning patch WITHOUT an intervening `furnace deploy`/`apply` silently
|
|
8
|
+
* exports the stale deployed copy — per-patch lint then flags the old line
|
|
9
|
+
* count and the patch body lags the source. This gate detects that
|
|
10
|
+
* sequence by comparing component source directories against the checksums
|
|
11
|
+
* recorded at the last apply (`FurnaceState.appliedChecksums`) — the same
|
|
12
|
+
* signal `warnIfFurnaceStale` uses for run/watch — and refuses the export
|
|
13
|
+
* unless the operator passes `--allow-stale-furnace`.
|
|
14
|
+
*
|
|
15
|
+
* Checksum-based on purpose: git checkouts and `furnace refresh` churn
|
|
16
|
+
* mtimes without content changes, so an mtime comparison would misfire.
|
|
17
|
+
*
|
|
18
|
+
* Probe failures (broken furnace config, missing state) degrade to a
|
|
19
|
+
* verbose log and an empty result — a broken furnace setup must never
|
|
20
|
+
* block non-furnace patch work.
|
|
21
|
+
*/
|
|
22
|
+
import { GeneralError } from '../errors/base.js';
|
|
23
|
+
import { pathExists } from '../utils/fs.js';
|
|
24
|
+
import { verbose, warn } from '../utils/logger.js';
|
|
25
|
+
import { extractComponentChecksums, hasComponentChanged } from './furnace-apply-helpers.js';
|
|
26
|
+
import { furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, loadFurnaceState, } from './furnace-config.js';
|
|
27
|
+
import { resolveFtlDir } from './furnace-constants.js';
|
|
28
|
+
/**
|
|
29
|
+
* Returns the furnace components whose deployed engine copies the given
|
|
30
|
+
* files fall under AND whose `components/` sources have changed since the
|
|
31
|
+
* last apply. Empty when furnace is not configured, never applied, or the
|
|
32
|
+
* probe fails.
|
|
33
|
+
*
|
|
34
|
+
* Files are attributed per component: the custom `targetPath` / override
|
|
35
|
+
* `basePath` prefixes, plus — for a localized non-sharedFtl custom
|
|
36
|
+
* component — its exact deployed `<ftlDir>/<name>.ftl` file (the shared
|
|
37
|
+
* FTL dir as a whole stays unclaimed; sibling files there belong to other
|
|
38
|
+
* components). `sharedFtl` bundles are NOT furnace-deployed (apply only
|
|
39
|
+
* prunes a dangling per-widget jar.mn line), so they have no deployed copy
|
|
40
|
+
* to go stale and get no candidate by design. The storybook stories prefix
|
|
41
|
+
* is likewise skipped.
|
|
42
|
+
*
|
|
43
|
+
* @param projectRoot - Project root directory
|
|
44
|
+
* @param files - Engine-relative paths the export will capture
|
|
45
|
+
*/
|
|
46
|
+
export async function findStaleFurnaceComponentsForFiles(projectRoot, files) {
|
|
47
|
+
try {
|
|
48
|
+
if (files.length === 0)
|
|
49
|
+
return [];
|
|
50
|
+
if (!(await furnaceConfigExists(projectRoot)))
|
|
51
|
+
return [];
|
|
52
|
+
const config = await loadFurnaceConfig(projectRoot);
|
|
53
|
+
const state = await loadFurnaceState(projectRoot);
|
|
54
|
+
if (!state.appliedChecksums)
|
|
55
|
+
return [];
|
|
56
|
+
const furnacePaths = getFurnacePaths(projectRoot);
|
|
57
|
+
const ftlDir = resolveFtlDir(config.ftlBasePath);
|
|
58
|
+
const ftlPrefix = ftlDir.endsWith('/') ? ftlDir : `${ftlDir}/`;
|
|
59
|
+
const candidates = [
|
|
60
|
+
...Object.entries(config.overrides).map(([name, cfg]) => ({
|
|
61
|
+
name,
|
|
62
|
+
type: 'override',
|
|
63
|
+
prefix: cfg.basePath.endsWith('/') ? cfg.basePath : `${cfg.basePath}/`,
|
|
64
|
+
exactFiles: [],
|
|
65
|
+
})),
|
|
66
|
+
...Object.entries(config.custom).map(([name, cfg]) => ({
|
|
67
|
+
name,
|
|
68
|
+
type: 'custom',
|
|
69
|
+
prefix: cfg.targetPath.endsWith('/') ? cfg.targetPath : `${cfg.targetPath}/`,
|
|
70
|
+
// A localized non-sharedFtl component also owns exactly one
|
|
71
|
+
// deployed file in the shared FTL dir (applyCustomFtlFile copies
|
|
72
|
+
// `<name>.ftl` there). sharedFtl bundles are not furnace-deployed.
|
|
73
|
+
exactFiles: cfg.localized && cfg.sharedFtl === undefined ? [`${ftlPrefix}${name}.ftl`] : [],
|
|
74
|
+
})),
|
|
75
|
+
];
|
|
76
|
+
const stale = [];
|
|
77
|
+
for (const candidate of candidates) {
|
|
78
|
+
if (!files.some((file) => file.startsWith(candidate.prefix) || candidate.exactFiles.includes(file)))
|
|
79
|
+
continue;
|
|
80
|
+
const sourceRoot = candidate.type === 'override' ? furnacePaths.overridesDir : furnacePaths.customDir;
|
|
81
|
+
const sourceDir = `${sourceRoot}/${candidate.name}`;
|
|
82
|
+
if (!(await pathExists(sourceDir)))
|
|
83
|
+
continue;
|
|
84
|
+
const previous = extractComponentChecksums(state.appliedChecksums, candidate.type, candidate.name);
|
|
85
|
+
if (await hasComponentChanged(sourceDir, previous)) {
|
|
86
|
+
stale.push({ name: candidate.name, type: candidate.type, prefix: candidate.prefix });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return stale;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
verbose(`Stale-furnace export gate skipped due to an error: ${error instanceof Error ? error.message : String(error)}`);
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Gate helper for export/re-export: refuses (or, with `allow`, warns) when
|
|
98
|
+
* any exported file belongs to a furnace component whose source changed
|
|
99
|
+
* since the last apply — the export would capture the stale deployed copy.
|
|
100
|
+
*
|
|
101
|
+
* @param projectRoot - Project root directory
|
|
102
|
+
* @param files - Engine-relative paths the export will capture
|
|
103
|
+
* @param allow - True when the operator passed `--allow-stale-furnace`
|
|
104
|
+
* @param command - Which command runs the gate (message wording only)
|
|
105
|
+
*/
|
|
106
|
+
export async function enforceFreshFurnaceSources(projectRoot, files, allow, command) {
|
|
107
|
+
const stale = await findStaleFurnaceComponentsForFiles(projectRoot, files);
|
|
108
|
+
if (stale.length === 0)
|
|
109
|
+
return;
|
|
110
|
+
const list = stale
|
|
111
|
+
.map((component) => `${component.name} (components/${component.type === 'custom' ? 'custom' : 'overrides'}/${component.name}/ → ${component.prefix})`)
|
|
112
|
+
.join(', ');
|
|
113
|
+
const message = `Component source${stale.length === 1 ? '' : 's'} changed since the last furnace apply: ${list}. ` +
|
|
114
|
+
`The deployed engine cop${stale.length === 1 ? 'y is' : 'ies are'} stale, so this ${command} would ` +
|
|
115
|
+
'capture the OLD component content into the patch. Run "fireforge furnace deploy" (or ' +
|
|
116
|
+
'"fireforge furnace apply") first so the export captures the current source, or pass ' +
|
|
117
|
+
'--allow-stale-furnace to export the deployed copy anyway.';
|
|
118
|
+
if (allow) {
|
|
119
|
+
warn(message);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
throw new GeneralError(message);
|
|
123
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-component furnace state persistence.
|
|
3
|
+
*
|
|
4
|
+
* There are exactly two sanctioned ways checksums land in
|
|
5
|
+
* furnace-state.json:
|
|
6
|
+
*
|
|
7
|
+
* 1. The BATCH path inside `applyAllComponents` (persistState: true),
|
|
8
|
+
* which replaces `appliedChecksums` wholesale — correct only when the
|
|
9
|
+
* run covered every component.
|
|
10
|
+
* 2. The per-component MERGE in this module, which rewrites only the
|
|
11
|
+
* `<type>/<name>/…` keys of the component that was applied.
|
|
12
|
+
*
|
|
13
|
+
* Every targeted (named) apply/deploy MUST use path 2 with
|
|
14
|
+
* `persistState: false`. Routing a named run through the batch path wipes
|
|
15
|
+
* every other component's checksum state: the batch loops filter to the
|
|
16
|
+
* named component, so the wholesale replace persists a state file
|
|
17
|
+
* containing only that component. Downstream, `diffDeletedFiles` and
|
|
18
|
+
* `findOrphanedEngineFiles` both key on `appliedChecksums`, so the wiped
|
|
19
|
+
* components' stale engine files become undetectable by apply AND by
|
|
20
|
+
* `furnace validate` (2026-07-05 review, finding F1 — `furnace apply
|
|
21
|
+
* <name>` had exactly this bug while `furnace deploy <name>` documented
|
|
22
|
+
* and avoided it).
|
|
23
|
+
*/
|
|
24
|
+
import { type applyAllComponents } from './furnace-apply.js';
|
|
25
|
+
import { type getFurnacePaths } from './furnace-config.js';
|
|
26
|
+
type ApplyAllResult = Awaited<ReturnType<typeof applyAllComponents>>;
|
|
27
|
+
/**
|
|
28
|
+
* Counts applied entries that carry at least one BLOCKING step error
|
|
29
|
+
* (advisory step errors are warnings and never fail a run).
|
|
30
|
+
*/
|
|
31
|
+
export declare function getStepFailureCount(result: ApplyAllResult): number;
|
|
32
|
+
/**
|
|
33
|
+
* Decides whether a single-component apply/deploy completed cleanly enough
|
|
34
|
+
* to persist its checksums into furnace-state.json.
|
|
35
|
+
*
|
|
36
|
+
* Named runs are atomic: if any apply step fails, the rollback journal
|
|
37
|
+
* restores the engine to its pre-run state and this helper returns `false`
|
|
38
|
+
* so state is not touched. The conditions must stay in lock-step with the
|
|
39
|
+
* rollback trigger in `applyAllComponents` — both read step errors, so a
|
|
40
|
+
* future refactor cannot drift them apart and accidentally persist partial
|
|
41
|
+
* state.
|
|
42
|
+
*/
|
|
43
|
+
export declare function shouldPersistSingleComponentState(result: ApplyAllResult, isDryRun: boolean): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Validates and narrows the applied entry a named run should persist.
|
|
46
|
+
*
|
|
47
|
+
* Guards against future refactors that might reorder or misroute the
|
|
48
|
+
* applied[] array: a named run persists state under a single component
|
|
49
|
+
* name, so the first applied entry MUST be that component. Persisting a
|
|
50
|
+
* different component's checksums would cause the next status/apply run to
|
|
51
|
+
* mis-report health for both components involved.
|
|
52
|
+
*
|
|
53
|
+
* @param commandLabel - Human label for error messages ("Deploy", "Apply")
|
|
54
|
+
*/
|
|
55
|
+
export declare function getPersistableAppliedEntry(commandLabel: string, name: string, appliedEntry: ApplyAllResult['applied'][number] | undefined): {
|
|
56
|
+
name: string;
|
|
57
|
+
type: 'override' | 'custom';
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Persists checksum state for a successfully applied named component by
|
|
61
|
+
* MERGING its `<type>/<name>/…` keys into the existing state — never
|
|
62
|
+
* replacing the whole map.
|
|
63
|
+
*
|
|
64
|
+
* @param projectRoot - Root directory of the project
|
|
65
|
+
* @param appliedEntry - Applied component descriptor
|
|
66
|
+
* @param furnacePaths - Resolved Furnace workspace paths
|
|
67
|
+
*/
|
|
68
|
+
export declare function persistSingleComponentState(projectRoot: string, appliedEntry: {
|
|
69
|
+
name: string;
|
|
70
|
+
type: 'override' | 'custom';
|
|
71
|
+
}, furnacePaths: ReturnType<typeof getFurnacePaths>): Promise<void>;
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Single-component furnace state persistence.
|
|
4
|
+
*
|
|
5
|
+
* There are exactly two sanctioned ways checksums land in
|
|
6
|
+
* furnace-state.json:
|
|
7
|
+
*
|
|
8
|
+
* 1. The BATCH path inside `applyAllComponents` (persistState: true),
|
|
9
|
+
* which replaces `appliedChecksums` wholesale — correct only when the
|
|
10
|
+
* run covered every component.
|
|
11
|
+
* 2. The per-component MERGE in this module, which rewrites only the
|
|
12
|
+
* `<type>/<name>/…` keys of the component that was applied.
|
|
13
|
+
*
|
|
14
|
+
* Every targeted (named) apply/deploy MUST use path 2 with
|
|
15
|
+
* `persistState: false`. Routing a named run through the batch path wipes
|
|
16
|
+
* every other component's checksum state: the batch loops filter to the
|
|
17
|
+
* named component, so the wholesale replace persists a state file
|
|
18
|
+
* containing only that component. Downstream, `diffDeletedFiles` and
|
|
19
|
+
* `findOrphanedEngineFiles` both key on `appliedChecksums`, so the wiped
|
|
20
|
+
* components' stale engine files become undetectable by apply AND by
|
|
21
|
+
* `furnace validate` (2026-07-05 review, finding F1 — `furnace apply
|
|
22
|
+
* <name>` had exactly this bug while `furnace deploy <name>` documented
|
|
23
|
+
* and avoided it).
|
|
24
|
+
*/
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { FurnaceError } from '../errors/furnace.js';
|
|
27
|
+
import { computeComponentChecksums, prefixChecksums, } from './furnace-apply.js';
|
|
28
|
+
import { updateFurnaceState } from './furnace-config.js';
|
|
29
|
+
import { countEntriesWithBlockingStepErrors } from './furnace-step-errors.js';
|
|
30
|
+
/**
|
|
31
|
+
* Counts applied entries that carry at least one BLOCKING step error
|
|
32
|
+
* (advisory step errors are warnings and never fail a run).
|
|
33
|
+
*/
|
|
34
|
+
export function getStepFailureCount(result) {
|
|
35
|
+
return countEntriesWithBlockingStepErrors(result.applied);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Decides whether a single-component apply/deploy completed cleanly enough
|
|
39
|
+
* to persist its checksums into furnace-state.json.
|
|
40
|
+
*
|
|
41
|
+
* Named runs are atomic: if any apply step fails, the rollback journal
|
|
42
|
+
* restores the engine to its pre-run state and this helper returns `false`
|
|
43
|
+
* so state is not touched. The conditions must stay in lock-step with the
|
|
44
|
+
* rollback trigger in `applyAllComponents` — both read step errors, so a
|
|
45
|
+
* future refactor cannot drift them apart and accidentally persist partial
|
|
46
|
+
* state.
|
|
47
|
+
*/
|
|
48
|
+
export function shouldPersistSingleComponentState(result, isDryRun) {
|
|
49
|
+
if (isDryRun)
|
|
50
|
+
return false;
|
|
51
|
+
if (result.errors.length > 0)
|
|
52
|
+
return false;
|
|
53
|
+
if (getStepFailureCount(result) > 0)
|
|
54
|
+
return false;
|
|
55
|
+
return result.applied.length > 0;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validates and narrows the applied entry a named run should persist.
|
|
59
|
+
*
|
|
60
|
+
* Guards against future refactors that might reorder or misroute the
|
|
61
|
+
* applied[] array: a named run persists state under a single component
|
|
62
|
+
* name, so the first applied entry MUST be that component. Persisting a
|
|
63
|
+
* different component's checksums would cause the next status/apply run to
|
|
64
|
+
* mis-report health for both components involved.
|
|
65
|
+
*
|
|
66
|
+
* @param commandLabel - Human label for error messages ("Deploy", "Apply")
|
|
67
|
+
*/
|
|
68
|
+
export function getPersistableAppliedEntry(commandLabel, name, appliedEntry) {
|
|
69
|
+
if (!appliedEntry) {
|
|
70
|
+
throw new FurnaceError(`${commandLabel} for "${name}" finished without producing an applied component entry; ` +
|
|
71
|
+
`furnace state was not modified. Run "fireforge doctor --repair-furnace" to ` +
|
|
72
|
+
`reconcile state, then retry. If this persists, file a bug with the ` +
|
|
73
|
+
`output of "fireforge doctor".`);
|
|
74
|
+
}
|
|
75
|
+
if (appliedEntry.type !== 'override' && appliedEntry.type !== 'custom') {
|
|
76
|
+
throw new FurnaceError(`${commandLabel} for "${name}" returned an unsupported component type "${appliedEntry.type}"; ` +
|
|
77
|
+
`furnace state was not modified. Run "fireforge doctor --repair-furnace" to reconcile, ` +
|
|
78
|
+
`then verify the component with "fireforge furnace validate" before retrying.`);
|
|
79
|
+
}
|
|
80
|
+
if (appliedEntry.name !== name) {
|
|
81
|
+
throw new FurnaceError(`${commandLabel} for "${name}" returned an applied entry for a different component ` +
|
|
82
|
+
`("${appliedEntry.name}"); refusing to persist mismatched state. ` +
|
|
83
|
+
`Run "fireforge doctor --repair-furnace" to reconcile, then retry.`);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
name: appliedEntry.name,
|
|
87
|
+
type: appliedEntry.type,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Persists checksum state for a successfully applied named component by
|
|
92
|
+
* MERGING its `<type>/<name>/…` keys into the existing state — never
|
|
93
|
+
* replacing the whole map.
|
|
94
|
+
*
|
|
95
|
+
* @param projectRoot - Root directory of the project
|
|
96
|
+
* @param appliedEntry - Applied component descriptor
|
|
97
|
+
* @param furnacePaths - Resolved Furnace workspace paths
|
|
98
|
+
*/
|
|
99
|
+
export async function persistSingleComponentState(projectRoot, appliedEntry, furnacePaths) {
|
|
100
|
+
const componentDir = appliedEntry.type === 'override'
|
|
101
|
+
? join(furnacePaths.overridesDir, appliedEntry.name)
|
|
102
|
+
: join(furnacePaths.customDir, appliedEntry.name);
|
|
103
|
+
const checksums = await computeComponentChecksums(componentDir);
|
|
104
|
+
const prefixed = prefixChecksums(checksums, appliedEntry.type, appliedEntry.name);
|
|
105
|
+
const componentPrefix = `${appliedEntry.type}/${appliedEntry.name}/`;
|
|
106
|
+
await updateFurnaceState(projectRoot, (current) => ({
|
|
107
|
+
...current,
|
|
108
|
+
appliedChecksums: {
|
|
109
|
+
...Object.fromEntries(Object.entries(current.appliedChecksums ?? {}).filter(([key]) => !key.startsWith(componentPrefix))),
|
|
110
|
+
...prefixed,
|
|
111
|
+
},
|
|
112
|
+
engineChecksums: {
|
|
113
|
+
...Object.fromEntries(Object.entries(current.engineChecksums ?? {}).filter(([key]) => !key.startsWith(componentPrefix))),
|
|
114
|
+
...prefixed,
|
|
115
|
+
},
|
|
116
|
+
lastApply: new Date().toISOString(),
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared predicates over {@link StepError} lists.
|
|
3
|
+
*
|
|
4
|
+
* Step errors come in two severities: BLOCKING (the default — the apply is
|
|
5
|
+
* considered failed, triggers rollback and a non-zero exit) and ADVISORY
|
|
6
|
+
* (`advisory: true` — reported as a warning, never blocks; used by the
|
|
7
|
+
* `.ftl` helpers whose contract is graceful degradation). Every gate that
|
|
8
|
+
* decides rollback/failure must count only blocking errors; every reporter
|
|
9
|
+
* should still surface advisory ones. This module is a leaf (types-only
|
|
10
|
+
* imports) so both `furnace-apply.ts` and its callers can share the
|
|
11
|
+
* predicates without import cycles.
|
|
12
|
+
*/
|
|
13
|
+
import type { StepError } from '../types/furnace.js';
|
|
14
|
+
/** Narrow shape shared by applied-component entries carrying step errors. */
|
|
15
|
+
export interface HasStepErrors {
|
|
16
|
+
stepErrors?: StepError[];
|
|
17
|
+
}
|
|
18
|
+
/** Returns only the blocking (non-advisory) step errors. */
|
|
19
|
+
export declare function blockingStepErrors(stepErrors: StepError[] | undefined): StepError[];
|
|
20
|
+
/** True when the entry carries at least one blocking step error. */
|
|
21
|
+
export declare function hasBlockingStepErrors(entry: HasStepErrors): boolean;
|
|
22
|
+
/** Counts applied entries that carry at least one blocking step error. */
|
|
23
|
+
export declare function countEntriesWithBlockingStepErrors(entries: readonly HasStepErrors[]): number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Shared predicates over {@link StepError} lists.
|
|
4
|
+
*
|
|
5
|
+
* Step errors come in two severities: BLOCKING (the default — the apply is
|
|
6
|
+
* considered failed, triggers rollback and a non-zero exit) and ADVISORY
|
|
7
|
+
* (`advisory: true` — reported as a warning, never blocks; used by the
|
|
8
|
+
* `.ftl` helpers whose contract is graceful degradation). Every gate that
|
|
9
|
+
* decides rollback/failure must count only blocking errors; every reporter
|
|
10
|
+
* should still surface advisory ones. This module is a leaf (types-only
|
|
11
|
+
* imports) so both `furnace-apply.ts` and its callers can share the
|
|
12
|
+
* predicates without import cycles.
|
|
13
|
+
*/
|
|
14
|
+
/** Returns only the blocking (non-advisory) step errors. */
|
|
15
|
+
export function blockingStepErrors(stepErrors) {
|
|
16
|
+
return (stepErrors ?? []).filter((e) => e.advisory !== true);
|
|
17
|
+
}
|
|
18
|
+
/** True when the entry carries at least one blocking step error. */
|
|
19
|
+
export function hasBlockingStepErrors(entry) {
|
|
20
|
+
return blockingStepErrors(entry.stepErrors).length > 0;
|
|
21
|
+
}
|
|
22
|
+
/** Counts applied entries that carry at least one blocking step error. */
|
|
23
|
+
export function countEntriesWithBlockingStepErrors(entries) {
|
|
24
|
+
return entries.filter((entry) => hasBlockingStepErrors(entry)).length;
|
|
25
|
+
}
|
|
@@ -4,4 +4,3 @@ export { validateAccessibility } from './furnace-validate-accessibility.js';
|
|
|
4
4
|
export { validateCompatibility } from './furnace-validate-compatibility.js';
|
|
5
5
|
export { checkRegistrationConsistency, validateJarMnEntries, validateRegistrationPatterns, validateTokenLink, } from './furnace-validate-registration.js';
|
|
6
6
|
export { validateStructure } from './furnace-validate-structure.js';
|
|
7
|
-
//# sourceMappingURL=furnace-validate-checks.js.map
|
|
@@ -3,7 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { pathExists, readText } from '../utils/fs.js';
|
|
4
4
|
import { hasRawCssColors } from '../utils/regex.js';
|
|
5
5
|
import { classExtendsMozLitElement, collectCssVariableDeclarations, collectCssVariableReferences, createIssue, getTokenPrefixContext, hasCustomElementDefineCall, hasRelativeModuleImport, stripCssBlockComments, } from './furnace-validate-helpers.js';
|
|
6
|
-
async function validateMjsCompatibility(mjsPath, tagName) {
|
|
6
|
+
async function validateMjsCompatibility(mjsPath, tagName, isLibrary) {
|
|
7
7
|
if (!(await pathExists(mjsPath)))
|
|
8
8
|
return [];
|
|
9
9
|
const mjsContent = await readText(mjsPath);
|
|
@@ -11,6 +11,12 @@ async function validateMjsCompatibility(mjsPath, tagName) {
|
|
|
11
11
|
if (hasRelativeModuleImport(mjsContent)) {
|
|
12
12
|
issues.push(createIssue(tagName, 'error', 'relative-import', 'Imports must use chrome:// URIs, not relative paths.'));
|
|
13
13
|
}
|
|
14
|
+
// A library component (kind: "library") deliberately defines no element:
|
|
15
|
+
// it exports a base class + helpers for other components to extend, so
|
|
16
|
+
// the define/extends requirements do not apply.
|
|
17
|
+
if (isLibrary) {
|
|
18
|
+
return issues;
|
|
19
|
+
}
|
|
14
20
|
if (!hasCustomElementDefineCall(mjsContent)) {
|
|
15
21
|
issues.push(createIssue(tagName, 'error', 'no-custom-element-define', 'Missing customElements.define() call. Component will not be registered.'));
|
|
16
22
|
}
|
|
@@ -124,7 +130,8 @@ export async function validateCompatibility(componentDir, tagName, type, config,
|
|
|
124
130
|
const issues = [];
|
|
125
131
|
const mjsPath = join(componentDir, `${tagName}.mjs`);
|
|
126
132
|
const cssPath = join(componentDir, `${tagName}.css`);
|
|
127
|
-
const
|
|
133
|
+
const isLibrary = type === 'custom' && config?.custom[tagName]?.kind === 'library';
|
|
134
|
+
const mjsIssues = await validateMjsCompatibility(mjsPath, tagName, isLibrary);
|
|
128
135
|
issues.push(...mjsIssues);
|
|
129
136
|
const cssIssues = await validateCssCompatibility(cssPath, tagName, type, config, root);
|
|
130
137
|
issues.push(...cssIssues);
|
|
@@ -149,4 +156,3 @@ export async function validateCompatibility(componentDir, tagName, type, config,
|
|
|
149
156
|
}
|
|
150
157
|
return issues;
|
|
151
158
|
}
|
|
152
|
-
//# sourceMappingURL=furnace-validate-compatibility.js.map
|
|
@@ -5,12 +5,13 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { toError } from '../utils/errors.js';
|
|
6
6
|
import { pathExists, readText } from '../utils/fs.js';
|
|
7
7
|
import { warn } from '../utils/logger.js';
|
|
8
|
-
import { stripJsComments } from '../utils/regex.js';
|
|
8
|
+
import { escapeRegex, stripJsComments } from '../utils/regex.js';
|
|
9
9
|
import { getProjectPaths, loadConfig } from './config.js';
|
|
10
10
|
import { getFurnacePaths } from './furnace-config.js';
|
|
11
11
|
import { CUSTOM_ELEMENTS_JS, FTL_DIR, JAR_MN } from './furnace-constants.js';
|
|
12
12
|
import { expandCssFragments, listFragmentIncludes } from './furnace-css-fragments.js';
|
|
13
13
|
import { findStaleJarMnEntries } from './furnace-registration.js';
|
|
14
|
+
import { isTagAlreadyRegistered } from './furnace-registration-ast.js';
|
|
14
15
|
import { isTagInCorrectCustomElementsPlacement } from './furnace-registration-validate.js';
|
|
15
16
|
import { getTokensCssPath } from './token-manager.js';
|
|
16
17
|
/**
|
|
@@ -47,9 +48,6 @@ export async function validateRegistrationPatterns(root, config) {
|
|
|
47
48
|
}
|
|
48
49
|
return issues;
|
|
49
50
|
}
|
|
50
|
-
function escapeRegex(value) {
|
|
51
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
52
|
-
}
|
|
53
51
|
/**
|
|
54
52
|
* Checks registration consistency for a single custom component.
|
|
55
53
|
*
|
|
@@ -160,8 +158,11 @@ export async function checkRegistrationConsistency(root, name, config, ftlDir) {
|
|
|
160
158
|
const cePath = join(engineDir, CUSTOM_ELEMENTS_JS);
|
|
161
159
|
if (await pathExists(cePath)) {
|
|
162
160
|
const ceContent = await readText(cePath);
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
// Structure-aware check shared with the ADD path — a bare substring
|
|
162
|
+
// test counted any mention of the tag (a leftover comment, an
|
|
163
|
+
// unrelated string) as "registered", masking genuinely missing
|
|
164
|
+
// registrations from both validate and the re-apply drift oracle.
|
|
165
|
+
status.customElementsPresent = isTagAlreadyRegistered(ceContent, name);
|
|
165
166
|
if (status.customElementsPresent) {
|
|
166
167
|
status.customElementsCorrectBlock = isTagInCorrectCustomElementsPlacement(ceContent, name, true);
|
|
167
168
|
}
|
|
@@ -396,4 +397,3 @@ export async function runPostApplyConsistencyChecks(root, config, result, ftlDir
|
|
|
396
397
|
}
|
|
397
398
|
}
|
|
398
399
|
}
|
|
399
|
-
//# sourceMappingURL=furnace-validate-registration.js.map
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { ComponentType, CustomComponentConfig, ValidationIssue } from '../types/furnace.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns true when file content carries unresolved three-way-merge
|
|
4
|
+
* conflict markers (as left behind by `furnace refresh`). Shared between
|
|
5
|
+
* the structure validator and `furnace sync`'s pre-apply gate — both must
|
|
6
|
+
* agree on what "conflicted" means so a file that validate flags can never
|
|
7
|
+
* slip through sync into the engine.
|
|
8
|
+
*/
|
|
9
|
+
export declare function containsMergeConflictMarkers(content: string): boolean;
|
|
2
10
|
/**
|
|
3
11
|
* Validates the file structure of a component directory.
|
|
4
12
|
* Checks for required files and naming conventions.
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
import { readdir } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { pathExists, readText } from '../utils/fs.js';
|
|
5
|
+
/**
|
|
6
|
+
* Returns true when file content carries unresolved three-way-merge
|
|
7
|
+
* conflict markers (as left behind by `furnace refresh`). Shared between
|
|
8
|
+
* the structure validator and `furnace sync`'s pre-apply gate — both must
|
|
9
|
+
* agree on what "conflicted" means so a file that validate flags can never
|
|
10
|
+
* slip through sync into the engine.
|
|
11
|
+
*/
|
|
12
|
+
export function containsMergeConflictMarkers(content) {
|
|
13
|
+
return /^<{7}\s/m.test(content) || /^>{7}\s/m.test(content) || /^={7}$/m.test(content);
|
|
14
|
+
}
|
|
5
15
|
/**
|
|
6
16
|
* Validates the file structure of a component directory.
|
|
7
17
|
* Checks for required files and naming conventions.
|
|
@@ -27,8 +37,9 @@ export async function validateStructure(componentDir, tagName, type, customConfi
|
|
|
27
37
|
message: `Required file ${tagName}.mjs not found.`,
|
|
28
38
|
});
|
|
29
39
|
}
|
|
30
|
-
// .css should exist
|
|
31
|
-
|
|
40
|
+
// .css should exist — except for library components (base class + helpers,
|
|
41
|
+
// no element of their own), which render nothing and need no stylesheet.
|
|
42
|
+
if (!(await pathExists(cssPath)) && customConfig?.kind !== 'library') {
|
|
32
43
|
issues.push({
|
|
33
44
|
component: tagName,
|
|
34
45
|
severity: 'warning',
|
|
@@ -65,7 +76,7 @@ export async function validateStructure(componentDir, tagName, type, customConfi
|
|
|
65
76
|
!entry.name.endsWith('.ftl'))
|
|
66
77
|
continue;
|
|
67
78
|
const content = await readText(join(componentDir, entry.name));
|
|
68
|
-
if (
|
|
79
|
+
if (containsMergeConflictMarkers(content)) {
|
|
69
80
|
issues.push({
|
|
70
81
|
component: tagName,
|
|
71
82
|
severity: 'error',
|
|
@@ -109,4 +120,3 @@ export async function validateStructure(componentDir, tagName, type, customConfi
|
|
|
109
120
|
}
|
|
110
121
|
return issues;
|
|
111
122
|
}
|
|
112
|
-
//# sourceMappingURL=furnace-validate-structure.js.map
|
|
@@ -14,9 +14,12 @@
|
|
|
14
14
|
* copied from `forgeConfig.firefox.version`. Any string mismatch is worth
|
|
15
15
|
* surfacing — even "140.0" vs "140.9.0esr" is a real drift signal.
|
|
16
16
|
*
|
|
17
|
-
* The result
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* The result GATES apply/deploy: both warn and then FAIL without --force
|
|
18
|
+
* (see furnaceApplyCommand / furnaceDeployCommand), and `furnace sync`
|
|
19
|
+
* re-checks it after refresh before applying. Status reports drift
|
|
20
|
+
* alongside the component overview. Keep this doc in sync with the gates —
|
|
21
|
+
* an earlier version claimed the result was advisory, which invited
|
|
22
|
+
* re-breaking the gate.
|
|
20
23
|
*/
|
|
21
24
|
import type { FurnaceConfig } from '../types/furnace.js';
|
|
22
25
|
/** Severity of the version drift between an override's base and the current Firefox version. */
|
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
* copied from `forgeConfig.firefox.version`. Any string mismatch is worth
|
|
16
16
|
* surfacing — even "140.0" vs "140.9.0esr" is a real drift signal.
|
|
17
17
|
*
|
|
18
|
-
* The result
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* The result GATES apply/deploy: both warn and then FAIL without --force
|
|
19
|
+
* (see furnaceApplyCommand / furnaceDeployCommand), and `furnace sync`
|
|
20
|
+
* re-checks it after refresh before applying. Status reports drift
|
|
21
|
+
* alongside the component overview. Keep this doc in sync with the gates —
|
|
22
|
+
* an earlier version claimed the result was advisory, which invited
|
|
23
|
+
* re-breaking the gate.
|
|
21
24
|
*/
|
|
22
25
|
/**
|
|
23
26
|
* Parses a version string into its major, minor, and patch numeric
|
|
@@ -98,4 +101,3 @@ export function formatOverrideBaseVersionDriftError(entries) {
|
|
|
98
101
|
'update baseVersion in furnace.json to acknowledge the new baseline, ' +
|
|
99
102
|
'or pass --force to proceed despite the drift.');
|
|
100
103
|
}
|
|
101
|
-
//# sourceMappingURL=furnace-version-drift.js.map
|
|
@@ -12,9 +12,22 @@ export declare function parsePorcelainStatus(output: string): GitStatusEntry[];
|
|
|
12
12
|
* @returns Parsed git status entries
|
|
13
13
|
*/
|
|
14
14
|
export declare function getWorkingTreeStatus(repoDir: string): Promise<GitStatusEntry[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the per-directory expansion cap, honoring the same
|
|
17
|
+
* FIREFORGE_MAX_UNTRACKED_FILES override the status command documents.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveMaxUntrackedFilesPerDir(): number;
|
|
15
20
|
/**
|
|
16
21
|
* Expands collapsed untracked directory entries into individual file entries.
|
|
17
22
|
* Git status may report "?? dir/" instead of listing each file underneath.
|
|
23
|
+
*
|
|
24
|
+
* Expansion is capped per directory (FIREFORGE_MAX_UNTRACKED_FILES, default
|
|
25
|
+
* 5000) with a warning on overflow. The cap used to exist only in the
|
|
26
|
+
* status command's private expander; this shared one — used by reset,
|
|
27
|
+
* discard, verify, lint, export-all — was unbounded, so e.g. `reset
|
|
28
|
+
* --dry-run` after an interrupted download (unborn HEAD, the entire ~300k
|
|
29
|
+
* file tree untracked) enumerated and printed every file.
|
|
30
|
+
*
|
|
18
31
|
* @param repoDir - Repository directory
|
|
19
32
|
* @param entries - Parsed status entries
|
|
20
33
|
* @returns Status entries with untracked directories expanded to individual files
|