@hominis/fireforge 0.10.1 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -1
- package/README.md +125 -238
- package/dist/bin/fireforge.js +26 -0
- package/dist/src/cli.d.ts +1 -1
- package/dist/src/cli.js +131 -52
- package/dist/src/commands/bootstrap.js +6 -2
- package/dist/src/commands/build.js +4 -2
- package/dist/src/commands/discard.js +16 -4
- package/dist/src/commands/doctor-furnace.d.ts +8 -0
- package/dist/src/commands/doctor-furnace.js +422 -0
- package/dist/src/commands/doctor.d.ts +115 -0
- package/dist/src/commands/doctor.js +327 -258
- package/dist/src/commands/download.js +16 -1
- package/dist/src/commands/export-all.js +15 -0
- package/dist/src/commands/export-flow.d.ts +91 -0
- package/dist/src/commands/export-flow.js +344 -0
- package/dist/src/commands/export.js +151 -5
- package/dist/src/commands/furnace/apply.d.ts +3 -2
- package/dist/src/commands/furnace/apply.js +169 -36
- package/dist/src/commands/furnace/create.js +162 -52
- package/dist/src/commands/furnace/deploy.js +156 -144
- package/dist/src/commands/furnace/diff.d.ts +8 -4
- package/dist/src/commands/furnace/diff.js +142 -73
- package/dist/src/commands/furnace/index.d.ts +6 -2
- package/dist/src/commands/furnace/index.js +76 -25
- package/dist/src/commands/furnace/init.d.ts +11 -0
- package/dist/src/commands/furnace/init.js +76 -0
- package/dist/src/commands/furnace/list.d.ts +4 -1
- package/dist/src/commands/furnace/list.js +35 -3
- package/dist/src/commands/furnace/override.d.ts +8 -0
- package/dist/src/commands/furnace/override.js +216 -26
- package/dist/src/commands/furnace/preview.js +184 -30
- package/dist/src/commands/furnace/refresh.d.ts +10 -0
- package/dist/src/commands/furnace/refresh.js +268 -0
- package/dist/src/commands/furnace/remove.js +285 -89
- package/dist/src/commands/furnace/rename.d.ts +5 -0
- package/dist/src/commands/furnace/rename.js +308 -0
- package/dist/src/commands/furnace/scan.d.ts +4 -1
- package/dist/src/commands/furnace/scan.js +72 -11
- package/dist/src/commands/furnace/status.js +85 -20
- package/dist/src/commands/furnace/sync.d.ts +12 -0
- package/dist/src/commands/furnace/sync.js +77 -0
- package/dist/src/commands/furnace/validate.d.ts +4 -1
- package/dist/src/commands/furnace/validate.js +99 -3
- package/dist/src/commands/furnace/validation-output.d.ts +24 -1
- package/dist/src/commands/furnace/validation-output.js +93 -1
- package/dist/src/commands/import.js +37 -4
- package/dist/src/commands/lint.js +11 -2
- package/dist/src/commands/manifest.d.ts +39 -0
- package/dist/src/commands/manifest.js +59 -0
- package/dist/src/commands/patch/delete.d.ts +28 -0
- package/dist/src/commands/patch/delete.js +209 -0
- package/dist/src/commands/patch/index.d.ts +17 -0
- package/dist/src/commands/patch/index.js +25 -0
- package/dist/src/commands/patch/reorder.d.ts +30 -0
- package/dist/src/commands/patch/reorder.js +377 -0
- package/dist/src/commands/re-export-files.d.ts +17 -0
- package/dist/src/commands/re-export-files.js +177 -0
- package/dist/src/commands/re-export.js +44 -0
- package/dist/src/commands/rebase/abort.d.ts +1 -1
- package/dist/src/commands/rebase/abort.js +12 -3
- package/dist/src/commands/rebase/confirm.d.ts +3 -3
- package/dist/src/commands/rebase/confirm.js +4 -4
- package/dist/src/commands/rebase/index.js +13 -4
- package/dist/src/commands/reset.js +20 -4
- package/dist/src/commands/run.js +46 -1
- package/dist/src/commands/setup-support.js +6 -5
- package/dist/src/commands/status.js +97 -6
- package/dist/src/commands/test.js +5 -37
- package/dist/src/commands/verify.d.ts +31 -0
- package/dist/src/commands/verify.js +126 -0
- package/dist/src/core/build-prepare.js +40 -16
- package/dist/src/core/destructive.d.ts +96 -0
- package/dist/src/core/destructive.js +137 -0
- package/dist/src/core/diff-hunks.d.ts +73 -0
- package/dist/src/core/diff-hunks.js +268 -0
- package/dist/src/core/firefox.d.ts +1 -1
- package/dist/src/core/firefox.js +1 -1
- package/dist/src/core/furnace-apply-helpers.d.ts +89 -6
- package/dist/src/core/furnace-apply-helpers.js +302 -57
- package/dist/src/core/furnace-apply-output.d.ts +16 -0
- package/dist/src/core/furnace-apply-output.js +57 -0
- package/dist/src/core/furnace-apply.d.ts +21 -3
- package/dist/src/core/furnace-apply.js +260 -29
- package/dist/src/core/furnace-checksum-utils.d.ts +4 -0
- package/dist/src/core/furnace-checksum-utils.js +24 -0
- package/dist/src/core/furnace-config.d.ts +28 -1
- package/dist/src/core/furnace-config.js +180 -17
- package/dist/src/core/furnace-constants.d.ts +22 -0
- package/dist/src/core/furnace-constants.js +36 -0
- package/dist/src/core/furnace-graph-utils.d.ts +11 -0
- package/dist/src/core/furnace-graph-utils.js +94 -0
- package/dist/src/core/furnace-operation.d.ts +108 -0
- package/dist/src/core/furnace-operation.js +220 -0
- package/dist/src/core/furnace-refresh.d.ts +20 -0
- package/dist/src/core/furnace-refresh.js +118 -0
- package/dist/src/core/furnace-registration-ast.d.ts +5 -0
- package/dist/src/core/furnace-registration-ast.js +134 -4
- package/dist/src/core/furnace-registration-remove.d.ts +25 -3
- package/dist/src/core/furnace-registration-remove.js +196 -62
- package/dist/src/core/furnace-registration-validate.d.ts +13 -1
- package/dist/src/core/furnace-registration-validate.js +15 -3
- package/dist/src/core/furnace-registration.d.ts +27 -4
- package/dist/src/core/furnace-registration.js +93 -11
- package/dist/src/core/furnace-rollback.d.ts +11 -0
- package/dist/src/core/furnace-rollback.js +78 -7
- package/dist/src/core/furnace-scanner.d.ts +8 -2
- package/dist/src/core/furnace-scanner.js +152 -55
- package/dist/src/core/furnace-stories.js +7 -5
- package/dist/src/core/furnace-validate-accessibility.js +7 -1
- package/dist/src/core/furnace-validate-compatibility.d.ts +1 -1
- package/dist/src/core/furnace-validate-compatibility.js +85 -1
- package/dist/src/core/furnace-validate-helpers.d.ts +4 -0
- package/dist/src/core/furnace-validate-helpers.js +31 -0
- package/dist/src/core/furnace-validate-registration.d.ts +17 -2
- package/dist/src/core/furnace-validate-registration.js +73 -3
- package/dist/src/core/furnace-validate-structure.d.ts +10 -2
- package/dist/src/core/furnace-validate-structure.js +45 -3
- package/dist/src/core/furnace-validate.d.ts +10 -1
- package/dist/src/core/furnace-validate.js +80 -6
- package/dist/src/core/furnace-version-drift.d.ts +55 -0
- package/dist/src/core/furnace-version-drift.js +101 -0
- package/dist/src/core/git-file-ops.d.ts +8 -0
- package/dist/src/core/git-file-ops.js +19 -6
- package/dist/src/core/lint-projection.d.ts +25 -0
- package/dist/src/core/lint-projection.js +44 -0
- package/dist/src/core/mach.d.ts +4 -2
- package/dist/src/core/mach.js +17 -2
- package/dist/src/core/markdown-table.d.ts +104 -0
- package/dist/src/core/markdown-table.js +266 -0
- package/dist/src/core/ownership-table.d.ts +53 -0
- package/dist/src/core/ownership-table.js +144 -0
- package/dist/src/core/patch-apply.d.ts +17 -3
- package/dist/src/core/patch-apply.js +86 -8
- package/dist/src/core/patch-export.d.ts +119 -5
- package/dist/src/core/patch-export.js +183 -25
- package/dist/src/core/patch-lint-cross.d.ts +195 -0
- package/dist/src/core/patch-lint-cross.js +428 -0
- package/dist/src/core/patch-lint-diff.d.ts +33 -0
- package/dist/src/core/patch-lint-diff.js +84 -0
- package/dist/src/core/patch-lint.d.ts +2 -4
- package/dist/src/core/patch-lint.js +12 -50
- package/dist/src/core/patch-lock.js +2 -1
- package/dist/src/core/patch-manifest-io.d.ts +102 -1
- package/dist/src/core/patch-manifest-io.js +270 -2
- package/dist/src/core/patch-manifest-query.d.ts +1 -1
- package/dist/src/core/patch-manifest-query.js +1 -1
- package/dist/src/core/patch-manifest.d.ts +1 -1
- package/dist/src/core/patch-manifest.js +1 -1
- package/dist/src/core/patch-transform.d.ts +12 -0
- package/dist/src/core/patch-transform.js +21 -7
- package/dist/src/core/token-manager.js +67 -69
- package/dist/src/core/wire-destroy.js +6 -3
- package/dist/src/core/wire-init.js +10 -4
- package/dist/src/core/wire-subscript.js +9 -3
- package/dist/src/core/wire-utils.d.ts +52 -5
- package/dist/src/core/wire-utils.js +69 -6
- package/dist/src/errors/base.d.ts +20 -0
- package/dist/src/errors/base.js +24 -0
- package/dist/src/errors/furnace.js +7 -1
- package/dist/src/errors/rebase.js +6 -1
- package/dist/src/types/commands/index.d.ts +1 -1
- package/dist/src/types/commands/options.d.ts +125 -4
- package/dist/src/types/commands/patches.d.ts +11 -1
- package/dist/src/types/config.d.ts +1 -1
- package/dist/src/types/furnace.d.ts +55 -1
- package/dist/src/utils/fs.d.ts +12 -0
- package/dist/src/utils/fs.js +30 -1
- package/dist/src/utils/package-root.d.ts +5 -0
- package/dist/src/utils/package-root.js +12 -0
- package/dist/src/utils/process.js +9 -4
- package/dist/src/utils/validation.d.ts +20 -2
- package/dist/src/utils/validation.js +26 -3
- package/package.json +1 -1
|
@@ -1,112 +1,92 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { readdir } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { getProjectPaths } from '../../core/config.js';
|
|
4
|
+
import { getProjectPaths, loadState } from '../../core/config.js';
|
|
5
|
+
import { diffLines, renderHunks } from '../../core/diff-hunks.js';
|
|
6
|
+
import { getOverrideEngineTargetPath } from '../../core/furnace-apply-helpers.js';
|
|
5
7
|
import { getFurnacePaths, loadFurnaceConfig } from '../../core/furnace-config.js';
|
|
8
|
+
import { isComponentSourceFile, resolveFtlDir } from '../../core/furnace-constants.js';
|
|
9
|
+
import { getFileContentAtRef } from '../../core/git-file-ops.js';
|
|
6
10
|
import { FurnaceError } from '../../errors/furnace.js';
|
|
7
11
|
import { pathExists, readText } from '../../utils/fs.js';
|
|
8
12
|
import { formatErrorText, formatSuccessText, info, intro, outro } from '../../utils/logger.js';
|
|
9
13
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* Renders a multi-hunk unified diff between the two strings and returns a
|
|
15
|
+
* flat list of display-ready lines. Each line has already had its marker
|
|
16
|
+
* prefix and color applied by this function; the caller just emits them.
|
|
17
|
+
*
|
|
18
|
+
* Pure delegation to the `diff-hunks` module — kept here as a thin wrapper
|
|
19
|
+
* so the command file does not need to care about the hunk data shape.
|
|
15
20
|
*/
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (oldLines[firstDiff] !== newLines[firstDiff])
|
|
29
|
-
break;
|
|
30
|
-
firstDiff++;
|
|
31
|
-
}
|
|
32
|
-
let lastOldDiff = oldLines.length - 1;
|
|
33
|
-
let lastNewDiff = newLines.length - 1;
|
|
34
|
-
while (lastOldDiff > firstDiff && lastNewDiff > firstDiff) {
|
|
35
|
-
if (oldLines[lastOldDiff] !== newLines[lastNewDiff])
|
|
36
|
-
break;
|
|
37
|
-
lastOldDiff--;
|
|
38
|
-
lastNewDiff--;
|
|
39
|
-
}
|
|
40
|
-
// Context lines before the change
|
|
41
|
-
const contextLines = 3;
|
|
42
|
-
const contextStart = Math.max(0, firstDiff - contextLines);
|
|
43
|
-
const contextEndOld = Math.min(oldLines.length - 1, lastOldDiff + contextLines);
|
|
44
|
-
const contextEndNew = Math.min(newLines.length - 1, lastNewDiff + contextLines);
|
|
45
|
-
// Leading context
|
|
46
|
-
for (let i = contextStart; i < firstDiff; i++) {
|
|
47
|
-
output.push(` ${oldLines[i]}`);
|
|
48
|
-
}
|
|
49
|
-
// Removed lines
|
|
50
|
-
for (let i = firstDiff; i <= lastOldDiff; i++) {
|
|
51
|
-
output.push(formatErrorText(`- ${oldLines[i]}`));
|
|
52
|
-
}
|
|
53
|
-
// Added lines
|
|
54
|
-
for (let i = firstDiff; i <= lastNewDiff; i++) {
|
|
55
|
-
output.push(formatSuccessText(`+ ${newLines[i]}`));
|
|
56
|
-
}
|
|
57
|
-
// Trailing context
|
|
58
|
-
const trailingStart = Math.max(lastOldDiff + 1, lastNewDiff + 1);
|
|
59
|
-
const trailingEnd = Math.max(contextEndOld, contextEndNew);
|
|
60
|
-
// Use the new lines for trailing context (they should match old lines here)
|
|
61
|
-
for (let i = trailingStart; i <= trailingEnd && i < newLines.length; i++) {
|
|
62
|
-
output.push(` ${newLines[i]}`);
|
|
63
|
-
}
|
|
64
|
-
return output;
|
|
21
|
+
function formatUnifiedDiff(original, modified) {
|
|
22
|
+
const hunks = diffLines(original, modified, 3);
|
|
23
|
+
return renderHunks(hunks).map((line) => {
|
|
24
|
+
switch (line.kind) {
|
|
25
|
+
case 'removed':
|
|
26
|
+
return formatErrorText(line.text);
|
|
27
|
+
case 'added':
|
|
28
|
+
return formatSuccessText(line.text);
|
|
29
|
+
default:
|
|
30
|
+
return line.text;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
65
33
|
}
|
|
66
34
|
/**
|
|
67
|
-
*
|
|
68
|
-
* Only works for override components.
|
|
69
|
-
* @param projectRoot - Root directory of the project
|
|
70
|
-
* @param name - Component name to diff
|
|
35
|
+
* Diffs an override component against its Firefox baseline at baseCommit.
|
|
71
36
|
*/
|
|
72
|
-
|
|
73
|
-
intro('Furnace Diff');
|
|
74
|
-
const config = await loadFurnaceConfig(projectRoot);
|
|
75
|
-
const paths = getProjectPaths(projectRoot);
|
|
76
|
-
const furnacePaths = getFurnacePaths(projectRoot);
|
|
77
|
-
// Verify the component is an override
|
|
37
|
+
async function diffOverride(name, projectRoot, config) {
|
|
78
38
|
const overrideConfig = config.overrides[name];
|
|
79
39
|
if (!overrideConfig) {
|
|
80
|
-
throw new FurnaceError(`"${name}"
|
|
40
|
+
throw new FurnaceError(`Override "${name}" not found in furnace.json.`, name);
|
|
81
41
|
}
|
|
42
|
+
const paths = getProjectPaths(projectRoot);
|
|
43
|
+
const furnacePaths = getFurnacePaths(projectRoot);
|
|
44
|
+
const ftlDir = resolveFtlDir(config.ftlBasePath);
|
|
82
45
|
const overrideDir = join(furnacePaths.overridesDir, name);
|
|
83
46
|
if (!(await pathExists(overrideDir))) {
|
|
84
47
|
throw new FurnaceError(`Override directory not found: components/overrides/${name}`, name);
|
|
85
48
|
}
|
|
49
|
+
// Prefer the per-override baseCommit (survives download --force); fall back
|
|
50
|
+
// to the project-wide value for overrides created before this field existed.
|
|
51
|
+
const state = await loadState(projectRoot);
|
|
52
|
+
const baseCommit = overrideConfig.baseCommit ?? state.baseCommit;
|
|
53
|
+
if (!baseCommit) {
|
|
54
|
+
throw new FurnaceError('Cannot diff: baseCommit not found. Re-run "fireforge download" to establish a baseline.', name);
|
|
55
|
+
}
|
|
86
56
|
const entries = await readdir(overrideDir, { withFileTypes: true });
|
|
87
57
|
let hasDifferences = false;
|
|
88
58
|
for (const entry of entries) {
|
|
89
59
|
if (!entry.isFile())
|
|
90
60
|
continue;
|
|
91
|
-
if (!
|
|
61
|
+
if (!isComponentSourceFile(entry.name))
|
|
92
62
|
continue;
|
|
93
|
-
|
|
63
|
+
// git show takes a repo-relative path. paths.engine IS the repo root.
|
|
64
|
+
const enginePath = getOverrideEngineTargetPath(paths.engine, overrideConfig, entry.name, ftlDir).slice(paths.engine.length + 1);
|
|
94
65
|
const modifiedPath = join(overrideDir, entry.name);
|
|
95
|
-
|
|
66
|
+
const baselineDisplayPath = enginePath;
|
|
67
|
+
let originalContent;
|
|
68
|
+
try {
|
|
69
|
+
originalContent = await getFileContentAtRef(paths.engine, enginePath, baseCommit);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw new FurnaceError(`Cannot read baseline for "${entry.name}" at commit ${baseCommit.slice(0, 8)}: ` +
|
|
73
|
+
`${error instanceof Error ? error.message : String(error)}. ` +
|
|
74
|
+
`The commit may no longer exist in the engine history (e.g. after a re-download). ` +
|
|
75
|
+
`Run "fireforge furnace refresh --reset-base ${name}" to establish a new baseline.`, name);
|
|
76
|
+
}
|
|
77
|
+
if (originalContent === null) {
|
|
96
78
|
info(`${entry.name}: original not found in engine (new file)`);
|
|
97
79
|
hasDifferences = true;
|
|
98
80
|
continue;
|
|
99
81
|
}
|
|
100
|
-
const originalContent = await readText(originalPath);
|
|
101
82
|
const modifiedContent = await readText(modifiedPath);
|
|
102
83
|
if (originalContent === modifiedContent) {
|
|
103
84
|
continue;
|
|
104
85
|
}
|
|
105
86
|
hasDifferences = true;
|
|
106
|
-
info(`--- ${
|
|
87
|
+
info(`--- ${baselineDisplayPath}`);
|
|
107
88
|
info(`+++ components/overrides/${name}/${entry.name}`);
|
|
108
|
-
const
|
|
109
|
-
for (const line of diffLines) {
|
|
89
|
+
for (const line of formatUnifiedDiff(originalContent, modifiedContent)) {
|
|
110
90
|
info(line);
|
|
111
91
|
}
|
|
112
92
|
info('');
|
|
@@ -114,6 +94,95 @@ export async function furnaceDiffCommand(projectRoot, name) {
|
|
|
114
94
|
if (!hasDifferences) {
|
|
115
95
|
info('No modifications found');
|
|
116
96
|
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Diffs a custom component's workspace files against the engine-deployed copy.
|
|
100
|
+
* Shows what would change (or has changed) on the next `furnace apply`.
|
|
101
|
+
*/
|
|
102
|
+
async function diffCustom(name, projectRoot, config) {
|
|
103
|
+
const customConfig = config.custom[name];
|
|
104
|
+
if (!customConfig) {
|
|
105
|
+
throw new FurnaceError(`Custom component "${name}" not found in furnace.json.`, name);
|
|
106
|
+
}
|
|
107
|
+
const paths = getProjectPaths(projectRoot);
|
|
108
|
+
const furnacePaths = getFurnacePaths(projectRoot);
|
|
109
|
+
const customDir = join(furnacePaths.customDir, name);
|
|
110
|
+
if (!(await pathExists(customDir))) {
|
|
111
|
+
throw new FurnaceError(`Custom component directory not found: components/custom/${name}`, name);
|
|
112
|
+
}
|
|
113
|
+
const engineDir = join(paths.engine, customConfig.targetPath);
|
|
114
|
+
const entries = await readdir(customDir, { withFileTypes: true });
|
|
115
|
+
let hasDifferences = false;
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (!entry.isFile())
|
|
118
|
+
continue;
|
|
119
|
+
if (!isComponentSourceFile(entry.name))
|
|
120
|
+
continue;
|
|
121
|
+
const workspacePath = join(customDir, entry.name);
|
|
122
|
+
const deployedPath = join(engineDir, entry.name);
|
|
123
|
+
const workspaceContent = await readText(workspacePath);
|
|
124
|
+
if (!(await pathExists(deployedPath))) {
|
|
125
|
+
info(`${entry.name}: not yet deployed to engine (new file)`);
|
|
126
|
+
hasDifferences = true;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const deployedContent = await readText(deployedPath);
|
|
130
|
+
if (workspaceContent === deployedContent) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
hasDifferences = true;
|
|
134
|
+
info(`--- engine/${customConfig.targetPath}/${entry.name}`);
|
|
135
|
+
info(`+++ components/custom/${name}/${entry.name}`);
|
|
136
|
+
for (const line of formatUnifiedDiff(deployedContent, workspaceContent)) {
|
|
137
|
+
info(line);
|
|
138
|
+
}
|
|
139
|
+
info('');
|
|
140
|
+
}
|
|
141
|
+
if (!hasDifferences) {
|
|
142
|
+
info('No differences between workspace and engine');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Runs the furnace diff command.
|
|
147
|
+
*
|
|
148
|
+
* For overrides: shows changes vs the Firefox original at baseCommit.
|
|
149
|
+
* For custom components: shows workspace vs engine-deployed copy.
|
|
150
|
+
* When no name is provided, diffs all override and custom components.
|
|
151
|
+
*
|
|
152
|
+
* @param projectRoot - Root directory of the project
|
|
153
|
+
* @param name - Optional component name to diff (diffs all when omitted)
|
|
154
|
+
*/
|
|
155
|
+
export async function furnaceDiffCommand(projectRoot, name) {
|
|
156
|
+
intro('Furnace Diff');
|
|
157
|
+
const config = await loadFurnaceConfig(projectRoot);
|
|
158
|
+
if (name) {
|
|
159
|
+
if (name in config.overrides) {
|
|
160
|
+
await diffOverride(name, projectRoot, config);
|
|
161
|
+
}
|
|
162
|
+
else if (name in config.custom) {
|
|
163
|
+
await diffCustom(name, projectRoot, config);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
throw new FurnaceError(`"${name}" is not found in furnace.json. Run "fireforge furnace list" to see registered components.`, name);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
const overrideNames = Object.keys(config.overrides);
|
|
171
|
+
const customNames = Object.keys(config.custom);
|
|
172
|
+
if (overrideNames.length === 0 && customNames.length === 0) {
|
|
173
|
+
info('No components to diff.');
|
|
174
|
+
outro('Diff complete');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
for (const overrideName of overrideNames) {
|
|
178
|
+
info(`\n── ${overrideName} (override) ──`);
|
|
179
|
+
await diffOverride(overrideName, projectRoot, config);
|
|
180
|
+
}
|
|
181
|
+
for (const customName of customNames) {
|
|
182
|
+
info(`\n── ${customName} (custom) ──`);
|
|
183
|
+
await diffCustom(customName, projectRoot, config);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
117
186
|
outro('Diff complete');
|
|
118
187
|
}
|
|
119
188
|
//# sourceMappingURL=diff.js.map
|
|
@@ -4,13 +4,17 @@ import { furnaceApplyCommand } from './apply.js';
|
|
|
4
4
|
import { furnaceCreateCommand } from './create.js';
|
|
5
5
|
import { furnaceDeployCommand } from './deploy.js';
|
|
6
6
|
import { furnaceDiffCommand } from './diff.js';
|
|
7
|
+
import { furnaceInitCommand } from './init.js';
|
|
7
8
|
import { furnaceListCommand } from './list.js';
|
|
8
|
-
import { furnaceOverrideCommand } from './override.js';
|
|
9
|
+
import { furnaceBatchOverrideCommand, furnaceOverrideCommand } from './override.js';
|
|
9
10
|
import { furnacePreviewCommand } from './preview.js';
|
|
11
|
+
import { furnaceRefreshCommand } from './refresh.js';
|
|
10
12
|
import { furnaceRemoveCommand } from './remove.js';
|
|
13
|
+
import { furnaceRenameCommand } from './rename.js';
|
|
11
14
|
import { furnaceScanCommand } from './scan.js';
|
|
12
15
|
import { furnaceStatusCommand } from './status.js';
|
|
16
|
+
import { furnaceSyncCommand } from './sync.js';
|
|
13
17
|
import { furnaceValidateCommand } from './validate.js';
|
|
14
|
-
export { furnaceApplyCommand, furnaceCreateCommand, furnaceDeployCommand, furnaceDiffCommand, furnaceListCommand, furnaceOverrideCommand, furnacePreviewCommand, furnaceRemoveCommand, furnaceScanCommand, furnaceStatusCommand, furnaceValidateCommand, };
|
|
18
|
+
export { furnaceApplyCommand, furnaceBatchOverrideCommand, furnaceCreateCommand, furnaceDeployCommand, furnaceDiffCommand, furnaceInitCommand, furnaceListCommand, furnaceOverrideCommand, furnacePreviewCommand, furnaceRefreshCommand, furnaceRemoveCommand, furnaceRenameCommand, furnaceScanCommand, furnaceStatusCommand, furnaceSyncCommand, furnaceValidateCommand, };
|
|
15
19
|
/** Registers the furnace command on the CLI program. */
|
|
16
20
|
export declare function registerFurnace(program: Command, context: CommandContext): void;
|
|
@@ -5,16 +5,21 @@ import { furnaceApplyCommand } from './apply.js';
|
|
|
5
5
|
import { furnaceCreateCommand } from './create.js';
|
|
6
6
|
import { furnaceDeployCommand } from './deploy.js';
|
|
7
7
|
import { furnaceDiffCommand } from './diff.js';
|
|
8
|
+
import { furnaceInitCommand } from './init.js';
|
|
8
9
|
import { furnaceListCommand } from './list.js';
|
|
9
|
-
import { furnaceOverrideCommand } from './override.js';
|
|
10
|
+
import { furnaceBatchOverrideCommand, furnaceOverrideCommand } from './override.js';
|
|
10
11
|
import { furnacePreviewCommand } from './preview.js';
|
|
12
|
+
import { furnaceRefreshCommand } from './refresh.js';
|
|
11
13
|
import { furnaceRemoveCommand } from './remove.js';
|
|
14
|
+
import { furnaceRenameCommand } from './rename.js';
|
|
12
15
|
import { furnaceScanCommand } from './scan.js';
|
|
13
16
|
import { furnaceStatusCommand } from './status.js';
|
|
17
|
+
import { furnaceSyncCommand } from './sync.js';
|
|
14
18
|
import { furnaceValidateCommand } from './validate.js';
|
|
15
|
-
export { furnaceApplyCommand, furnaceCreateCommand, furnaceDeployCommand, furnaceDiffCommand, furnaceListCommand, furnaceOverrideCommand, furnacePreviewCommand, furnaceRemoveCommand, furnaceScanCommand, furnaceStatusCommand, furnaceValidateCommand, };
|
|
19
|
+
export { furnaceApplyCommand, furnaceBatchOverrideCommand, furnaceCreateCommand, furnaceDeployCommand, furnaceDiffCommand, furnaceInitCommand, furnaceListCommand, furnaceOverrideCommand, furnacePreviewCommand, furnaceRefreshCommand, furnaceRemoveCommand, furnaceRenameCommand, furnaceScanCommand, furnaceStatusCommand, furnaceSyncCommand, furnaceValidateCommand, };
|
|
16
20
|
/**
|
|
17
|
-
* Registers
|
|
21
|
+
* Registers Furnace commands for querying component state: status, scan,
|
|
22
|
+
* and action commands like apply, deploy, and create.
|
|
18
23
|
* @param furnace - Parent Furnace command
|
|
19
24
|
* @param context - Shared CLI registration context
|
|
20
25
|
*/
|
|
@@ -27,24 +32,38 @@ function registerFurnaceInfoCommands(furnace, context) {
|
|
|
27
32
|
await furnaceStatusCommand(getProjectRoot(), name);
|
|
28
33
|
}));
|
|
29
34
|
furnace
|
|
30
|
-
.command('apply')
|
|
31
|
-
.description('Apply
|
|
32
|
-
.option('--dry-run', 'Show what would be changed without writing')
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
+
.command('apply [name]')
|
|
36
|
+
.description('Apply components to the engine (optionally a single component)')
|
|
37
|
+
.option('--dry-run', 'Show what would be changed without writing (reads may overlap concurrent mutations)')
|
|
38
|
+
.option('--force', 'Proceed despite baseVersion drift (stale overrides)')
|
|
39
|
+
.option('-w, --watch', 'Watch component directories and re-apply on changes')
|
|
40
|
+
.action(withErrorHandling(async (name, options) => {
|
|
41
|
+
await furnaceApplyCommand(getProjectRoot(), name, pickDefined(options ?? {}));
|
|
35
42
|
}));
|
|
36
43
|
furnace
|
|
37
44
|
.command('deploy [name]')
|
|
38
45
|
.description('Apply components and validate in one step')
|
|
39
|
-
.option('--dry-run', 'Show what would be changed without writing')
|
|
46
|
+
.option('--dry-run', 'Show what would be changed without writing (reads may overlap concurrent mutations)')
|
|
47
|
+
.option('--force', 'Proceed despite baseVersion drift (stale overrides)')
|
|
48
|
+
.option('--skip-validate', 'Skip the validation step (apply only)')
|
|
40
49
|
.action(withErrorHandling(async (name, options) => {
|
|
41
50
|
await furnaceDeployCommand(getProjectRoot(), name, pickDefined(options ?? {}));
|
|
42
51
|
}));
|
|
43
52
|
furnace
|
|
44
53
|
.command('scan')
|
|
45
54
|
.description('Scan engine for available components')
|
|
46
|
-
.
|
|
47
|
-
|
|
55
|
+
.option('--deep', 'Search additional Firefox directories beyond the default widgets path')
|
|
56
|
+
.action(withErrorHandling(async (options) => {
|
|
57
|
+
await furnaceScanCommand(getProjectRoot(), pickDefined(options));
|
|
58
|
+
}));
|
|
59
|
+
furnace
|
|
60
|
+
.command('init')
|
|
61
|
+
.description('Initialize furnace.json with project settings')
|
|
62
|
+
.option('-p, --prefix <prefix>', 'Component prefix (e.g. "moz-", "ff-")')
|
|
63
|
+
.option('--ftl-base-path <path>', 'Fluent l10n base path')
|
|
64
|
+
.option('--force', 'Overwrite existing furnace.json')
|
|
65
|
+
.action(withErrorHandling(async (options) => {
|
|
66
|
+
await furnaceInitCommand(getProjectRoot(), options);
|
|
48
67
|
}));
|
|
49
68
|
furnace
|
|
50
69
|
.command('create [name]')
|
|
@@ -53,36 +72,43 @@ function registerFurnaceInfoCommands(furnace, context) {
|
|
|
53
72
|
.option('--localized', 'Include Fluent l10n support')
|
|
54
73
|
.option('--no-register', 'Skip customElements.js registration')
|
|
55
74
|
.option('--with-tests', 'Scaffold Mochitest directory and register in moz.build')
|
|
56
|
-
.option('--compose <tags>', '
|
|
75
|
+
.option('--compose <tags>', 'Record stock tags composed internally (metadata only, comma-separated)', (val) => val.split(',').map((s) => s.trim()))
|
|
57
76
|
.action(withErrorHandling(async (name, options) => {
|
|
58
77
|
await furnaceCreateCommand(getProjectRoot(), name, options);
|
|
59
78
|
}));
|
|
60
79
|
}
|
|
61
80
|
/**
|
|
62
|
-
* Registers
|
|
81
|
+
* Registers Furnace commands for authoring, inspection, and maintenance:
|
|
82
|
+
* override, list, remove, preview, validate, diff, refresh, and rename.
|
|
63
83
|
* @param furnace - Parent Furnace command
|
|
64
84
|
* @param context - Shared CLI registration context
|
|
65
85
|
*/
|
|
66
86
|
function registerFurnaceModifyCommands(furnace, context) {
|
|
67
87
|
const { getProjectRoot, withErrorHandling } = context;
|
|
68
88
|
furnace
|
|
69
|
-
.command('override [
|
|
70
|
-
.description('Fork
|
|
89
|
+
.command('override [names...]')
|
|
90
|
+
.description('Fork one or more existing components for modification')
|
|
71
91
|
.addOption(new Option('-t, --type <type>', 'Override type').choices(['css-only', 'full']))
|
|
72
92
|
.option('-d, --description <desc>', 'Description')
|
|
73
|
-
.action(withErrorHandling(async (
|
|
74
|
-
|
|
93
|
+
.action(withErrorHandling(async (names, options) => {
|
|
94
|
+
if (names.length <= 1) {
|
|
95
|
+
await furnaceOverrideCommand(getProjectRoot(), names[0], options);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
await furnaceBatchOverrideCommand(getProjectRoot(), names, options);
|
|
99
|
+
}
|
|
75
100
|
}));
|
|
76
101
|
furnace
|
|
77
102
|
.command('list')
|
|
78
103
|
.description('List all registered components')
|
|
79
|
-
.
|
|
80
|
-
|
|
104
|
+
.option('-v, --verbose', 'Show per-component health indicators (clean/modified/not applied)')
|
|
105
|
+
.action(withErrorHandling(async (options) => {
|
|
106
|
+
await furnaceListCommand(getProjectRoot(), options);
|
|
81
107
|
}));
|
|
82
108
|
furnace
|
|
83
109
|
.command('remove <name>')
|
|
84
110
|
.description('Remove a component from the workspace')
|
|
85
|
-
.option('-
|
|
111
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
86
112
|
.action(withErrorHandling(async (name, options) => {
|
|
87
113
|
await furnaceRemoveCommand(getProjectRoot(), name, options);
|
|
88
114
|
}));
|
|
@@ -96,22 +122,47 @@ function registerFurnaceModifyCommands(furnace, context) {
|
|
|
96
122
|
furnace
|
|
97
123
|
.command('validate [name]')
|
|
98
124
|
.description('Run accessibility and compatibility checks')
|
|
99
|
-
.
|
|
100
|
-
|
|
125
|
+
.option('--fix', 'Auto-fix registration issues (missing jar.mn entries, customElements.js)')
|
|
126
|
+
.action(withErrorHandling(async (name, options) => {
|
|
127
|
+
await furnaceValidateCommand(getProjectRoot(), name, pickDefined(options ?? {}));
|
|
101
128
|
}));
|
|
102
129
|
furnace
|
|
103
|
-
.command('diff
|
|
104
|
-
.description('Show changes vs Firefox original
|
|
130
|
+
.command('diff [name]')
|
|
131
|
+
.description('Show changes vs baseline (overrides: vs Firefox original, custom: vs engine). Shows all components when name is omitted.')
|
|
105
132
|
.action(withErrorHandling(async (name) => {
|
|
106
133
|
await furnaceDiffCommand(getProjectRoot(), name);
|
|
107
134
|
}));
|
|
135
|
+
furnace
|
|
136
|
+
.command('refresh [name]')
|
|
137
|
+
.description('Merge upstream Firefox changes into overrides (three-way merge)')
|
|
138
|
+
.option('--dry-run', 'Show what would change without modifying files')
|
|
139
|
+
.option('-a, --all', 'Refresh all overrides in a single batch')
|
|
140
|
+
.addOption(new Option('-s, --strategy <strategy>', 'Auto-resolve conflicts (ours = keep local, theirs = accept upstream)').choices(['ours', 'theirs']))
|
|
141
|
+
.option('--reset-base', 'Reset baseline to current engine HEAD (skips three-way merge, recovers from missing baseCommit)')
|
|
142
|
+
.action(withErrorHandling(async (name, options) => {
|
|
143
|
+
await furnaceRefreshCommand(getProjectRoot(), name, pickDefined(options));
|
|
144
|
+
}));
|
|
145
|
+
furnace
|
|
146
|
+
.command('rename <old-name> <new-name>')
|
|
147
|
+
.description('Rename a component (updates files, config, and registrations)')
|
|
148
|
+
.action(withErrorHandling(async (oldName, newName) => {
|
|
149
|
+
await furnaceRenameCommand(getProjectRoot(), oldName, newName);
|
|
150
|
+
}));
|
|
151
|
+
furnace
|
|
152
|
+
.command('sync')
|
|
153
|
+
.description('Refresh drifted overrides and re-apply all components (recommended after fireforge download)')
|
|
154
|
+
.option('--dry-run', 'Show what would change without modifying files (reads may overlap concurrent mutations)')
|
|
155
|
+
.addOption(new Option('-s, --strategy <strategy>', 'Auto-resolve merge conflicts (ours = keep local, theirs = accept upstream)').choices(['ours', 'theirs']))
|
|
156
|
+
.action(withErrorHandling(async (options) => {
|
|
157
|
+
await furnaceSyncCommand(getProjectRoot(), pickDefined(options));
|
|
158
|
+
}));
|
|
108
159
|
}
|
|
109
160
|
/** Registers the furnace command on the CLI program. */
|
|
110
161
|
export function registerFurnace(program, context) {
|
|
111
162
|
const { getProjectRoot, withErrorHandling } = context;
|
|
112
163
|
const furnace = program
|
|
113
164
|
.command('furnace')
|
|
114
|
-
.description('Component management (
|
|
165
|
+
.description('Component management — create, override, apply, deploy, diff, validate, sync (run furnace --help for all subcommands)')
|
|
115
166
|
.action(withErrorHandling(async () => {
|
|
116
167
|
await furnaceStatusCommand(getProjectRoot());
|
|
117
168
|
}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs the furnace init command to create a default furnace.json with
|
|
3
|
+
* user-specified settings.
|
|
4
|
+
* @param projectRoot - Root directory of the project
|
|
5
|
+
* @param options - Init options
|
|
6
|
+
*/
|
|
7
|
+
export declare function furnaceInitCommand(projectRoot: string, options?: {
|
|
8
|
+
prefix?: string;
|
|
9
|
+
ftlBasePath?: string;
|
|
10
|
+
force?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { text } from '@clack/prompts';
|
|
3
|
+
import { createDefaultFurnaceConfig, furnaceConfigExists, writeFurnaceConfig, } from '../../core/furnace-config.js';
|
|
4
|
+
import { FurnaceError } from '../../errors/furnace.js';
|
|
5
|
+
import { cancel, info, intro, isCancel, note, outro, success } from '../../utils/logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* Runs the furnace init command to create a default furnace.json with
|
|
8
|
+
* user-specified settings.
|
|
9
|
+
* @param projectRoot - Root directory of the project
|
|
10
|
+
* @param options - Init options
|
|
11
|
+
*/
|
|
12
|
+
export async function furnaceInitCommand(projectRoot, options = {}) {
|
|
13
|
+
intro('Furnace Init');
|
|
14
|
+
if ((await furnaceConfigExists(projectRoot)) && !options.force) {
|
|
15
|
+
throw new FurnaceError('furnace.json already exists. Use --force to overwrite it.');
|
|
16
|
+
}
|
|
17
|
+
const config = createDefaultFurnaceConfig();
|
|
18
|
+
const isInteractive = process.stdin.isTTY;
|
|
19
|
+
// Resolve componentPrefix
|
|
20
|
+
if (options.prefix !== undefined) {
|
|
21
|
+
config.componentPrefix = options.prefix;
|
|
22
|
+
}
|
|
23
|
+
else if (isInteractive) {
|
|
24
|
+
const prefixResult = await text({
|
|
25
|
+
message: 'Component prefix (e.g. "moz-", "ff-")',
|
|
26
|
+
initialValue: config.componentPrefix,
|
|
27
|
+
validate: (value) => {
|
|
28
|
+
if (!value)
|
|
29
|
+
return 'Prefix is required';
|
|
30
|
+
return undefined;
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
if (isCancel(prefixResult)) {
|
|
34
|
+
cancel('Init cancelled');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
config.componentPrefix = prefixResult;
|
|
38
|
+
}
|
|
39
|
+
// Resolve ftlBasePath
|
|
40
|
+
if (options.ftlBasePath !== undefined) {
|
|
41
|
+
if (options.ftlBasePath.includes('..')) {
|
|
42
|
+
throw new FurnaceError('ftlBasePath must not contain ".." (path traversal)');
|
|
43
|
+
}
|
|
44
|
+
config.ftlBasePath = options.ftlBasePath;
|
|
45
|
+
}
|
|
46
|
+
else if (isInteractive) {
|
|
47
|
+
const ftlResult = await text({
|
|
48
|
+
message: 'Fluent l10n base path (leave empty for default)',
|
|
49
|
+
placeholder: 'toolkit/locales/en-US/toolkit/global',
|
|
50
|
+
});
|
|
51
|
+
if (isCancel(ftlResult)) {
|
|
52
|
+
cancel('Init cancelled');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const ftlValue = ftlResult.trim();
|
|
56
|
+
if (ftlValue) {
|
|
57
|
+
if (ftlValue.includes('..')) {
|
|
58
|
+
throw new FurnaceError('ftlBasePath must not contain ".." (path traversal)');
|
|
59
|
+
}
|
|
60
|
+
config.ftlBasePath = ftlValue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
await writeFurnaceConfig(projectRoot, config);
|
|
64
|
+
success('Created furnace.json');
|
|
65
|
+
const lines = [`Component prefix: ${config.componentPrefix}`];
|
|
66
|
+
if (config.ftlBasePath) {
|
|
67
|
+
lines.push(`FTL base path: ${config.ftlBasePath}`);
|
|
68
|
+
}
|
|
69
|
+
note(lines.join('\n'), 'Configuration');
|
|
70
|
+
info('Next steps:\n' +
|
|
71
|
+
' fireforge furnace scan — discover engine components\n' +
|
|
72
|
+
' fireforge furnace create — create a new custom component\n' +
|
|
73
|
+
' fireforge furnace override — fork an existing component');
|
|
74
|
+
outro('Init complete');
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runs the furnace list command to display all registered components.
|
|
3
3
|
* @param projectRoot - Root directory of the project
|
|
4
|
+
* @param options - List options
|
|
4
5
|
*/
|
|
5
|
-
export declare function furnaceListCommand(projectRoot: string
|
|
6
|
+
export declare function furnaceListCommand(projectRoot: string, options?: {
|
|
7
|
+
verbose?: boolean;
|
|
8
|
+
}): Promise<void>;
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { extractComponentChecksums, hasComponentChanged, } from '../../core/furnace-apply-helpers.js';
|
|
4
|
+
import { furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, loadFurnaceState, } from '../../core/furnace-config.js';
|
|
5
|
+
import { pathExists } from '../../utils/fs.js';
|
|
6
|
+
import { formatErrorText, formatSuccessText, info, intro, note, outro, } from '../../utils/logger.js';
|
|
7
|
+
/**
|
|
8
|
+
* Returns a short health indicator for a component directory based on whether
|
|
9
|
+
* its workspace checksums have changed since the last apply.
|
|
10
|
+
*/
|
|
11
|
+
async function getHealthIndicator(componentDir, type, name, appliedChecksums) {
|
|
12
|
+
if (!(await pathExists(componentDir))) {
|
|
13
|
+
return formatErrorText('missing');
|
|
14
|
+
}
|
|
15
|
+
const previous = extractComponentChecksums(appliedChecksums, type, name);
|
|
16
|
+
if (Object.keys(previous).length === 0) {
|
|
17
|
+
return formatErrorText('not applied');
|
|
18
|
+
}
|
|
19
|
+
const changed = await hasComponentChanged(componentDir, previous);
|
|
20
|
+
return changed ? formatErrorText('modified') : formatSuccessText('clean');
|
|
21
|
+
}
|
|
4
22
|
/**
|
|
5
23
|
* Runs the furnace list command to display all registered components.
|
|
6
24
|
* @param projectRoot - Root directory of the project
|
|
25
|
+
* @param options - List options
|
|
7
26
|
*/
|
|
8
|
-
export async function furnaceListCommand(projectRoot) {
|
|
27
|
+
export async function furnaceListCommand(projectRoot, options = {}) {
|
|
9
28
|
intro('Furnace List');
|
|
10
29
|
if (!(await furnaceConfigExists(projectRoot))) {
|
|
11
30
|
info('No components configured. Run "fireforge furnace create" or "fireforge furnace override" to get started.');
|
|
@@ -22,6 +41,9 @@ export async function furnaceListCommand(projectRoot) {
|
|
|
22
41
|
outro('Done');
|
|
23
42
|
return;
|
|
24
43
|
}
|
|
44
|
+
const showHealth = options.verbose ?? false;
|
|
45
|
+
const furnacePaths = showHealth ? getFurnacePaths(projectRoot) : undefined;
|
|
46
|
+
const state = showHealth ? await loadFurnaceState(projectRoot) : undefined;
|
|
25
47
|
// --- Stock ---
|
|
26
48
|
if (stockCount > 0) {
|
|
27
49
|
info('Stock:');
|
|
@@ -37,6 +59,11 @@ export async function furnaceListCommand(projectRoot) {
|
|
|
37
59
|
if (entry.description) {
|
|
38
60
|
line += ` — ${entry.description}`;
|
|
39
61
|
}
|
|
62
|
+
if (showHealth && furnacePaths && state) {
|
|
63
|
+
const componentDir = join(furnacePaths.overridesDir, name);
|
|
64
|
+
const health = await getHealthIndicator(componentDir, 'override', name, state.appliedChecksums);
|
|
65
|
+
line += ` [${health}]`;
|
|
66
|
+
}
|
|
40
67
|
info(line);
|
|
41
68
|
}
|
|
42
69
|
}
|
|
@@ -56,6 +83,11 @@ export async function furnaceListCommand(projectRoot) {
|
|
|
56
83
|
if (flags.length > 0) {
|
|
57
84
|
line += ` [${flags.join(', ')}]`;
|
|
58
85
|
}
|
|
86
|
+
if (showHealth && furnacePaths && state) {
|
|
87
|
+
const componentDir = join(furnacePaths.customDir, name);
|
|
88
|
+
const health = await getHealthIndicator(componentDir, 'custom', name, state.appliedChecksums);
|
|
89
|
+
line += ` [${health}]`;
|
|
90
|
+
}
|
|
59
91
|
info(line);
|
|
60
92
|
}
|
|
61
93
|
}
|
|
@@ -6,3 +6,11 @@ import type { FurnaceOverrideOptions } from '../../types/commands/index.js';
|
|
|
6
6
|
* @param options - CLI options for non-interactive mode
|
|
7
7
|
*/
|
|
8
8
|
export declare function furnaceOverrideCommand(projectRoot: string, name?: string, options?: FurnaceOverrideOptions): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Creates multiple overrides in a single invocation. Each component is validated
|
|
11
|
+
* and created sequentially; failures on one component do not block the rest.
|
|
12
|
+
* @param projectRoot - Root directory of the project
|
|
13
|
+
* @param names - Component tag names to override
|
|
14
|
+
* @param options - CLI options applied to all overrides
|
|
15
|
+
*/
|
|
16
|
+
export declare function furnaceBatchOverrideCommand(projectRoot: string, names: string[], options?: FurnaceOverrideOptions): Promise<void>;
|