@hominis/fireforge 0.10.1 → 0.11.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 +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 +5 -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
|
@@ -56,13 +56,35 @@ export interface ExportOptions {
|
|
|
56
56
|
supersede?: boolean;
|
|
57
57
|
/** Skip patch lint checks (downgrade errors to warnings) */
|
|
58
58
|
skipLint?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Print the computed export plan without writing anything. With
|
|
61
|
+
* `--supersede`, the dry-run output includes which existing patches would
|
|
62
|
+
* be superseded and which files caused the coverage.
|
|
63
|
+
*/
|
|
64
|
+
dryRun?: boolean;
|
|
65
|
+
/** Place the new patch at a specific ordinal, shifting subsequent patches. */
|
|
66
|
+
order?: number;
|
|
67
|
+
/** Place the new patch immediately before the named patch. */
|
|
68
|
+
before?: string;
|
|
69
|
+
/** Place the new patch immediately after the named patch. */
|
|
70
|
+
after?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Skip the confirmation prompt when placement forces a renumber of more
|
|
73
|
+
* than one existing patch. Required for non-TTY runs that use placement
|
|
74
|
+
* flags.
|
|
75
|
+
*/
|
|
76
|
+
yes?: boolean;
|
|
77
|
+
/** Bypass cross-patch lint refusal for projected placement state. */
|
|
78
|
+
forceUnsafe?: boolean;
|
|
79
|
+
/** Exclude furnace-managed file paths from the export. */
|
|
80
|
+
excludeFurnace?: boolean;
|
|
59
81
|
}
|
|
60
82
|
/**
|
|
61
83
|
* Options for the reset command.
|
|
62
84
|
*/
|
|
63
85
|
export interface ResetOptions {
|
|
64
86
|
/** Skip confirmation prompt */
|
|
65
|
-
|
|
87
|
+
yes?: boolean;
|
|
66
88
|
/** Show what would be reset without doing it */
|
|
67
89
|
dryRun?: boolean;
|
|
68
90
|
}
|
|
@@ -73,7 +95,7 @@ export interface DiscardOptions {
|
|
|
73
95
|
/** Show what would be discarded without doing it */
|
|
74
96
|
dryRun?: boolean;
|
|
75
97
|
/** Skip confirmation prompt */
|
|
76
|
-
|
|
98
|
+
yes?: boolean;
|
|
77
99
|
}
|
|
78
100
|
/**
|
|
79
101
|
* Options for the package command.
|
|
@@ -92,6 +114,14 @@ export interface ImportOptions {
|
|
|
92
114
|
continue?: boolean;
|
|
93
115
|
/** Force import even when engine HEAD has drifted from base commit */
|
|
94
116
|
force?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Apply patches only up to and including this patch (by name or ordinal).
|
|
119
|
+
* Subsequent patches are left unapplied. Useful for bisection and curated
|
|
120
|
+
* rebuild workflows.
|
|
121
|
+
*/
|
|
122
|
+
until?: string;
|
|
123
|
+
/** Preview which patches would be applied without modifying the engine */
|
|
124
|
+
dryRun?: boolean;
|
|
95
125
|
}
|
|
96
126
|
/**
|
|
97
127
|
* Options for the re-export command.
|
|
@@ -101,10 +131,22 @@ export interface ReExportOptions {
|
|
|
101
131
|
all?: boolean;
|
|
102
132
|
/** Scan directories for new/removed files and update filesAffected */
|
|
103
133
|
scan?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Restrict the re-exported patch's filesAffected to this explicit list.
|
|
136
|
+
* Files currently in the patch but not in this list are dropped (shrink);
|
|
137
|
+
* files in this list but not currently in the patch are added. Mutually
|
|
138
|
+
* exclusive with `--scan` and `--all`; applies to a single target patch
|
|
139
|
+
* at a time.
|
|
140
|
+
*/
|
|
141
|
+
files?: string[];
|
|
104
142
|
/** Show what would change without writing */
|
|
105
143
|
dryRun?: boolean;
|
|
106
144
|
/** Skip patch lint checks (downgrade errors to warnings) */
|
|
107
145
|
skipLint?: boolean;
|
|
146
|
+
/** Skip confirmation prompt on shrink (required for non-TTY) */
|
|
147
|
+
yes?: boolean;
|
|
148
|
+
/** Bypass cross-patch lint refusal on projected shrink state */
|
|
149
|
+
forceUnsafe?: boolean;
|
|
108
150
|
}
|
|
109
151
|
/**
|
|
110
152
|
* Options for the rebase command.
|
|
@@ -119,7 +161,7 @@ export interface RebaseOptions {
|
|
|
119
161
|
/** Maximum fuzz factor for git apply (default 3) */
|
|
120
162
|
maxFuzz?: number;
|
|
121
163
|
/** Skip dirty-tree confirmation prompt */
|
|
122
|
-
|
|
164
|
+
yes?: boolean;
|
|
123
165
|
}
|
|
124
166
|
/**
|
|
125
167
|
* Options for the run command.
|
|
@@ -143,6 +185,10 @@ export interface TestOptions {
|
|
|
143
185
|
export interface FurnaceApplyOptions {
|
|
144
186
|
/** Show what would be changed without writing */
|
|
145
187
|
dryRun?: boolean;
|
|
188
|
+
/** Proceed despite baseVersion drift (stale overrides) */
|
|
189
|
+
force?: boolean;
|
|
190
|
+
/** Watch component directories and re-apply on changes */
|
|
191
|
+
watch?: boolean;
|
|
146
192
|
}
|
|
147
193
|
/**
|
|
148
194
|
* Options for the furnace preview command.
|
|
@@ -157,6 +203,39 @@ export interface FurnacePreviewOptions {
|
|
|
157
203
|
export interface FurnaceDeployOptions {
|
|
158
204
|
/** Show what would be changed without writing */
|
|
159
205
|
dryRun?: boolean;
|
|
206
|
+
/** Proceed despite baseVersion drift (stale overrides) */
|
|
207
|
+
force?: boolean;
|
|
208
|
+
/** Skip the validation step (apply only, no accessibility/compatibility checks) */
|
|
209
|
+
skipValidate?: boolean;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Options for the furnace refresh command.
|
|
213
|
+
*/
|
|
214
|
+
export interface FurnaceRefreshOptions {
|
|
215
|
+
/** Show what would change without modifying files */
|
|
216
|
+
dryRun?: boolean;
|
|
217
|
+
/** Refresh all overrides in a single batch */
|
|
218
|
+
all?: boolean;
|
|
219
|
+
/** Conflict resolution strategy for automated use (ours = keep local, theirs = accept upstream) */
|
|
220
|
+
strategy?: 'ours' | 'theirs';
|
|
221
|
+
/** Reset the override's baseline to the current engine HEAD, skipping three-way merge */
|
|
222
|
+
resetBase?: boolean;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Options for the furnace sync command.
|
|
226
|
+
*/
|
|
227
|
+
export interface FurnaceSyncOptions {
|
|
228
|
+
/** Show what would change without modifying files */
|
|
229
|
+
dryRun?: boolean;
|
|
230
|
+
/** Conflict resolution strategy for three-way merge (ours = keep local, theirs = accept upstream) */
|
|
231
|
+
strategy?: 'ours' | 'theirs';
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Options for the furnace validate command.
|
|
235
|
+
*/
|
|
236
|
+
export interface FurnaceValidateOptions {
|
|
237
|
+
/** Auto-fix registration issues (missing jar.mn entries, customElements.js registration) */
|
|
238
|
+
fix?: boolean;
|
|
160
239
|
}
|
|
161
240
|
/**
|
|
162
241
|
* Options for the furnace override command.
|
|
@@ -172,7 +251,7 @@ export interface FurnaceOverrideOptions {
|
|
|
172
251
|
*/
|
|
173
252
|
export interface FurnaceRemoveOptions {
|
|
174
253
|
/** Skip confirmation prompt */
|
|
175
|
-
|
|
254
|
+
yes?: boolean;
|
|
176
255
|
}
|
|
177
256
|
/**
|
|
178
257
|
* Options for the furnace create command.
|
|
@@ -207,12 +286,43 @@ export interface RegisterOptions {
|
|
|
207
286
|
dryRun?: boolean;
|
|
208
287
|
after?: string;
|
|
209
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Options for the patch delete command.
|
|
291
|
+
*/
|
|
292
|
+
export interface PatchDeleteOptions {
|
|
293
|
+
/** Skip confirmation prompt; required for non-TTY runs. */
|
|
294
|
+
yes?: boolean;
|
|
295
|
+
/** Print what would happen without writing anything. */
|
|
296
|
+
dryRun?: boolean;
|
|
297
|
+
/** Bypass the hard refusal when later patches depend on the target. */
|
|
298
|
+
forceUnsafe?: boolean;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Options for the patch reorder command.
|
|
302
|
+
*/
|
|
303
|
+
export interface PatchReorderOptions {
|
|
304
|
+
to?: number;
|
|
305
|
+
before?: string;
|
|
306
|
+
after?: string;
|
|
307
|
+
yes?: boolean;
|
|
308
|
+
dryRun?: boolean;
|
|
309
|
+
forceUnsafe?: boolean;
|
|
310
|
+
}
|
|
210
311
|
/**
|
|
211
312
|
* Options for the status command.
|
|
212
313
|
*/
|
|
213
314
|
export interface StatusOptions {
|
|
214
315
|
raw?: boolean;
|
|
215
316
|
unmanaged?: boolean;
|
|
317
|
+
/**
|
|
318
|
+
* Render a flat file→owning-patch ownership table instead of the three-
|
|
319
|
+
* bucket classification. Sources the path list from the manifest's
|
|
320
|
+
* `filesAffected` per patch and flags any path claimed by more than one
|
|
321
|
+
* patch as an ownership conflict.
|
|
322
|
+
*/
|
|
323
|
+
ownership?: boolean;
|
|
324
|
+
/** Output machine-readable JSON instead of human-readable text. */
|
|
325
|
+
json?: boolean;
|
|
216
326
|
}
|
|
217
327
|
/**
|
|
218
328
|
* Options for the token add command.
|
|
@@ -229,6 +339,17 @@ export interface TokenAddOptions {
|
|
|
229
339
|
*/
|
|
230
340
|
export interface DoctorOptions {
|
|
231
341
|
repairPatchesManifest?: boolean;
|
|
342
|
+
/**
|
|
343
|
+
* Opt-in repair path for furnace-specific checks. When true, doctor will:
|
|
344
|
+
* - clear stale `.fireforge/furnace-state.json` entries whose component is
|
|
345
|
+
* no longer in `furnace.json`,
|
|
346
|
+
* - run `applyAllComponents` to reconcile any engine drift,
|
|
347
|
+
* - clear the `pendingRepair` marker on success.
|
|
348
|
+
* Mirrors `repairPatchesManifest` in that the repair is only attempted when
|
|
349
|
+
* the caller explicitly asks for it, so a read-only `doctor` run stays cheap
|
|
350
|
+
* and side-effect-free.
|
|
351
|
+
*/
|
|
352
|
+
repairFurnace?: boolean;
|
|
232
353
|
}
|
|
233
354
|
/**
|
|
234
355
|
* Global CLI options available to all commands.
|
|
@@ -47,7 +47,7 @@ export interface PatchMetadata {
|
|
|
47
47
|
description: string;
|
|
48
48
|
/** ISO timestamp of when the patch was created */
|
|
49
49
|
createdAt: string;
|
|
50
|
-
/** ESR version the patch was created against (e.g., "
|
|
50
|
+
/** ESR version the patch was created against (e.g., "146.0esr") */
|
|
51
51
|
sourceEsrVersion: string;
|
|
52
52
|
/** Array of file paths affected by this patch */
|
|
53
53
|
filesAffected: string[];
|
|
@@ -82,6 +82,16 @@ export interface PatchLintIssue {
|
|
|
82
82
|
file: string;
|
|
83
83
|
/** Check identifier (e.g. "raw-color-value", "token-prefix-violation") */
|
|
84
84
|
check: string;
|
|
85
|
+
/**
|
|
86
|
+
* Stable machine-readable identity for this finding.
|
|
87
|
+
*
|
|
88
|
+
* Use when the human-readable `message` may drift between otherwise
|
|
89
|
+
* equivalent runs (for example because a rule embeds line numbers,
|
|
90
|
+
* rename-sensitive patch filenames, or other contextual detail).
|
|
91
|
+
* Consumers that diff lint outputs should prefer this over `message`
|
|
92
|
+
* when it is present.
|
|
93
|
+
*/
|
|
94
|
+
fingerprint?: string;
|
|
85
95
|
/** Human-readable description of the issue */
|
|
86
96
|
message: string;
|
|
87
97
|
/** Severity: errors block export, warnings are advisory */
|
|
@@ -6,7 +6,7 @@ export type FirefoxProduct = 'firefox' | 'firefox-esr' | 'firefox-beta';
|
|
|
6
6
|
* Firefox version configuration.
|
|
7
7
|
*/
|
|
8
8
|
export interface FirefoxConfig {
|
|
9
|
-
/** Firefox release version (e.g., "
|
|
9
|
+
/** Firefox release version (e.g., "146.0esr") */
|
|
10
10
|
version: string;
|
|
11
11
|
/** Firefox product type */
|
|
12
12
|
product: FirefoxProduct;
|
|
@@ -33,6 +33,8 @@ export interface OverrideComponentConfig {
|
|
|
33
33
|
basePath: string;
|
|
34
34
|
/** Firefox version this override was based on */
|
|
35
35
|
baseVersion: string;
|
|
36
|
+
/** Git commit SHA the override was based on. Older overrides may lack this field. */
|
|
37
|
+
baseCommit?: string;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* Metadata for a custom component in the workspace.
|
|
@@ -61,6 +63,16 @@ export interface FurnaceConfig {
|
|
|
61
63
|
tokenPrefix?: string;
|
|
62
64
|
/** Custom properties allowed even though they don't match tokenPrefix (e.g. ["--background-color-box"]) */
|
|
63
65
|
tokenAllowlist?: string[];
|
|
66
|
+
/**
|
|
67
|
+
* Override the default Fluent (.ftl) base path within the engine.
|
|
68
|
+
* Defaults to `toolkit/locales/en-US/toolkit/global` when not set.
|
|
69
|
+
*/
|
|
70
|
+
ftlBasePath?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Additional directories to scan for components (relative to engine root).
|
|
73
|
+
* Always includes `toolkit/content/widgets` by default.
|
|
74
|
+
*/
|
|
75
|
+
scanPaths?: string[];
|
|
64
76
|
/** Stock components tracked for preview */
|
|
65
77
|
stock: string[];
|
|
66
78
|
/** Override components */
|
|
@@ -68,6 +80,28 @@ export interface FurnaceConfig {
|
|
|
68
80
|
/** Custom components */
|
|
69
81
|
custom: Record<string, CustomComponentConfig>;
|
|
70
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Operations that can leave a pending-repair marker when they fail to roll
|
|
85
|
+
* back cleanly. The marker is consumed by `fireforge doctor`, which either
|
|
86
|
+
* re-runs apply for engine-side failures or validates the current authoring
|
|
87
|
+
* state before clearing authoring markers. The string is surfaced in doctor's
|
|
88
|
+
* failure message verbatim, so new entries should be self-explanatory.
|
|
89
|
+
*/
|
|
90
|
+
export type FurnacePendingRepairOperation = 'preview-teardown' | 'apply-rollback' | 'deploy-rollback' | 'remove-rollback' | 'create-rollback' | 'override-rollback' | 'scan-rollback' | 'rename-rollback' | 'refresh-rollback';
|
|
91
|
+
/**
|
|
92
|
+
* Marker persisted into `.fireforge/furnace-state.json` when a furnace
|
|
93
|
+
* mutation failed to roll back cleanly. Its presence tells the next
|
|
94
|
+
* `fireforge doctor` run that the engine and workspace may have drifted
|
|
95
|
+
* out-of-band from what the state file records.
|
|
96
|
+
*/
|
|
97
|
+
export interface FurnacePendingRepair {
|
|
98
|
+
/** The operation that failed to clean up; used by doctor to route the fix. */
|
|
99
|
+
operation: FurnacePendingRepairOperation;
|
|
100
|
+
/** ISO timestamp of when the repair marker was written. */
|
|
101
|
+
timestamp: string;
|
|
102
|
+
/** Human-readable summary of the failure; shown by doctor. */
|
|
103
|
+
reason: string;
|
|
104
|
+
}
|
|
71
105
|
/**
|
|
72
106
|
* State tracking for apply operations (stored in .fireforge/furnace-state.json).
|
|
73
107
|
*/
|
|
@@ -76,6 +110,20 @@ export interface FurnaceState {
|
|
|
76
110
|
lastApply?: string;
|
|
77
111
|
/** Checksums of component files at last apply, keyed by relative path */
|
|
78
112
|
appliedChecksums?: Record<string, string>;
|
|
113
|
+
/**
|
|
114
|
+
* SHA-256 hashes of engine-side files written during the last apply, keyed
|
|
115
|
+
* by engine-relative path. Used by drift detection to avoid byte-comparing
|
|
116
|
+
* engine files against workspace sources when the cached hash still matches
|
|
117
|
+
* the on-disk content. Populated alongside `appliedChecksums` on successful
|
|
118
|
+
* apply.
|
|
119
|
+
*/
|
|
120
|
+
engineChecksums?: Record<string, string>;
|
|
121
|
+
/**
|
|
122
|
+
* Set when a furnace mutation failed to roll back cleanly and the engine
|
|
123
|
+
* may be in an inconsistent state. Cleared by `fireforge doctor` after
|
|
124
|
+
* reconciliation. See {@link FurnacePendingRepair}.
|
|
125
|
+
*/
|
|
126
|
+
pendingRepair?: FurnacePendingRepair;
|
|
79
127
|
}
|
|
80
128
|
/**
|
|
81
129
|
* A registration-step error captured while applying a component.
|
|
@@ -107,13 +155,19 @@ export interface ApplyResult {
|
|
|
107
155
|
name: string;
|
|
108
156
|
error: string;
|
|
109
157
|
}>;
|
|
158
|
+
/**
|
|
159
|
+
* Set to true when the rollback journal was restored after a partial failure.
|
|
160
|
+
* When true, entries in `applied` reflect what was attempted, not what
|
|
161
|
+
* persisted — the engine has been restored to its pre-apply state.
|
|
162
|
+
*/
|
|
163
|
+
rolledBack?: boolean;
|
|
110
164
|
}
|
|
111
165
|
/**
|
|
112
166
|
* An action that would be performed during a dry-run deploy.
|
|
113
167
|
*/
|
|
114
168
|
export interface DryRunAction {
|
|
115
169
|
component: string;
|
|
116
|
-
action: 'copy' | 'register-ce' | 'register-jar' | 'copy-ftl';
|
|
170
|
+
action: 'copy' | 'register-ce' | 'register-jar' | 'copy-ftl' | 'undeploy-remove' | 'undeploy-restore' | 'unregister-ce' | 'unregister-jar';
|
|
117
171
|
source?: string;
|
|
118
172
|
target?: string;
|
|
119
173
|
description: string;
|
package/dist/src/utils/fs.d.ts
CHANGED
|
@@ -70,3 +70,15 @@ export declare function writeFileAtomic(path: string, content: string | Buffer):
|
|
|
70
70
|
* @param dest - Destination directory path
|
|
71
71
|
*/
|
|
72
72
|
export declare function copyDir(src: string, dest: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Checks available disk space at a path and warns via the provided
|
|
75
|
+
* callback when it falls below `minBytes`.
|
|
76
|
+
*
|
|
77
|
+
* @param path - Directory to check (must exist)
|
|
78
|
+
* @param minBytes - Minimum free bytes before emitting a warning
|
|
79
|
+
* @param onLowSpace - Callback invoked with a human-readable message
|
|
80
|
+
* when available space is below the threshold
|
|
81
|
+
* @returns The available bytes, or `undefined` when the check could not
|
|
82
|
+
* be performed (unsupported platform, permission error, etc.)
|
|
83
|
+
*/
|
|
84
|
+
export declare function checkDiskSpace(path: string, minBytes: number, onLowSpace: (message: string) => void): Promise<number | undefined>;
|
package/dist/src/utils/fs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { access, copyFile as fsCopyFile, mkdir, open, readdir, readFile, rename, rm, } from 'node:fs/promises';
|
|
3
|
+
import { access, copyFile as fsCopyFile, mkdir, open, readdir, readFile, rename, rm, statfs, } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
const RETRIABLE_REMOVE_ERRORS = new Set(['ENOTEMPTY', 'EBUSY', 'EPERM']);
|
|
6
6
|
function sleep(ms) {
|
|
@@ -176,4 +176,33 @@ function createAtomicTempPath(path) {
|
|
|
176
176
|
const filename = path.slice(directory.length + 1);
|
|
177
177
|
return join(directory, `.${filename}.fireforge-tmp-${process.pid}-${randomUUID()}`);
|
|
178
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Checks available disk space at a path and warns via the provided
|
|
181
|
+
* callback when it falls below `minBytes`.
|
|
182
|
+
*
|
|
183
|
+
* @param path - Directory to check (must exist)
|
|
184
|
+
* @param minBytes - Minimum free bytes before emitting a warning
|
|
185
|
+
* @param onLowSpace - Callback invoked with a human-readable message
|
|
186
|
+
* when available space is below the threshold
|
|
187
|
+
* @returns The available bytes, or `undefined` when the check could not
|
|
188
|
+
* be performed (unsupported platform, permission error, etc.)
|
|
189
|
+
*/
|
|
190
|
+
export async function checkDiskSpace(path, minBytes, onLowSpace) {
|
|
191
|
+
try {
|
|
192
|
+
const stats = await statfs(path);
|
|
193
|
+
const availableBytes = stats.bfree * stats.bsize;
|
|
194
|
+
if (availableBytes < minBytes) {
|
|
195
|
+
const availableGB = (availableBytes / (1024 * 1024 * 1024)).toFixed(1);
|
|
196
|
+
const requiredGB = (minBytes / (1024 * 1024 * 1024)).toFixed(1);
|
|
197
|
+
onLowSpace(`Low disk space: ${availableGB} GB available, ${requiredGB} GB recommended. ` +
|
|
198
|
+
'The operation may fail if the disk fills up.');
|
|
199
|
+
}
|
|
200
|
+
return availableBytes;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// statfs may not be available on all platforms or the path may
|
|
204
|
+
// not exist yet — silently degrade rather than blocking the operation.
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
179
208
|
//# sourceMappingURL=fs.js.map
|
|
@@ -9,8 +9,13 @@ interface PackageMetadata {
|
|
|
9
9
|
* Works from both the source tree (`src/utils/`) and the compiled
|
|
10
10
|
* tree (`dist/src/utils/`) by looking for a `package.json` that exposes
|
|
11
11
|
* the `fireforge` CLI entrypoint, regardless of the npm package scope.
|
|
12
|
+
*
|
|
13
|
+
* The result is cached after the first call since it is deterministic
|
|
14
|
+
* within a process.
|
|
12
15
|
*/
|
|
13
16
|
export declare function getPackageRoot(): string;
|
|
17
|
+
/** Clears the cached package root for testing. */
|
|
18
|
+
export declare function resetPackageRootCacheForTests(): void;
|
|
14
19
|
/** @internal */
|
|
15
20
|
export declare function isFireForgePackageMetadata(pkg: PackageMetadata): boolean;
|
|
16
21
|
/** Reads the current package version from the repository root package manifest. */
|
|
@@ -17,20 +17,28 @@ function readPackageMetadata(filePath) {
|
|
|
17
17
|
const raw = readFileSync(filePath, 'utf-8');
|
|
18
18
|
return validatePackageMetadata(JSON.parse(raw), filePath);
|
|
19
19
|
}
|
|
20
|
+
let cachedPackageRoot;
|
|
20
21
|
/**
|
|
21
22
|
* Finds the fireforge package root by walking up from the current module.
|
|
22
23
|
*
|
|
23
24
|
* Works from both the source tree (`src/utils/`) and the compiled
|
|
24
25
|
* tree (`dist/src/utils/`) by looking for a `package.json` that exposes
|
|
25
26
|
* the `fireforge` CLI entrypoint, regardless of the npm package scope.
|
|
27
|
+
*
|
|
28
|
+
* The result is cached after the first call since it is deterministic
|
|
29
|
+
* within a process.
|
|
26
30
|
*/
|
|
27
31
|
export function getPackageRoot() {
|
|
32
|
+
if (cachedPackageRoot !== undefined) {
|
|
33
|
+
return cachedPackageRoot;
|
|
34
|
+
}
|
|
28
35
|
let current = dirname(fileURLToPath(import.meta.url));
|
|
29
36
|
for (;;) {
|
|
30
37
|
try {
|
|
31
38
|
const packagePath = join(current, 'package.json');
|
|
32
39
|
const pkg = readPackageMetadata(packagePath);
|
|
33
40
|
if (isFireForgePackageMetadata(pkg)) {
|
|
41
|
+
cachedPackageRoot = current;
|
|
34
42
|
return current;
|
|
35
43
|
}
|
|
36
44
|
}
|
|
@@ -45,6 +53,10 @@ export function getPackageRoot() {
|
|
|
45
53
|
current = parent;
|
|
46
54
|
}
|
|
47
55
|
}
|
|
56
|
+
/** Clears the cached package root for testing. */
|
|
57
|
+
export function resetPackageRootCacheForTests() {
|
|
58
|
+
cachedPackageRoot = undefined;
|
|
59
|
+
}
|
|
48
60
|
/** @internal */
|
|
49
61
|
export function isFireForgePackageMetadata(pkg) {
|
|
50
62
|
if (typeof pkg.bin !== 'object' || pkg.bin === null || Array.isArray(pkg.bin)) {
|
|
@@ -29,6 +29,11 @@ function createStreamCollector(mirror) {
|
|
|
29
29
|
getText: () => chunks.join(''),
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
function buildSignalFromTimeout(timeout) {
|
|
33
|
+
if (timeout === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
return AbortSignal.timeout(timeout);
|
|
36
|
+
}
|
|
32
37
|
function exitCodeFromClose(code, signal) {
|
|
33
38
|
if (code !== null) {
|
|
34
39
|
return code;
|
|
@@ -54,7 +59,7 @@ export async function exec(command, args, options = {}) {
|
|
|
54
59
|
cwd: options.cwd,
|
|
55
60
|
env: { ...process.env, ...options.env },
|
|
56
61
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
57
|
-
|
|
62
|
+
signal: buildSignalFromTimeout(options.timeout),
|
|
58
63
|
});
|
|
59
64
|
const out = createStreamCollector();
|
|
60
65
|
const err = createStreamCollector();
|
|
@@ -85,7 +90,7 @@ export async function execStream(command, args, options = {}) {
|
|
|
85
90
|
cwd: options.cwd,
|
|
86
91
|
env: { ...process.env, ...options.env },
|
|
87
92
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
88
|
-
|
|
93
|
+
signal: buildSignalFromTimeout(options.timeout),
|
|
89
94
|
});
|
|
90
95
|
child.stdout.on('data', (data) => {
|
|
91
96
|
options.onStdout?.(data.toString());
|
|
@@ -114,7 +119,7 @@ export async function execInherit(command, args, options = {}) {
|
|
|
114
119
|
cwd: options.cwd,
|
|
115
120
|
env: { ...process.env, ...options.env },
|
|
116
121
|
stdio: 'inherit',
|
|
117
|
-
|
|
122
|
+
signal: buildSignalFromTimeout(options.timeout),
|
|
118
123
|
});
|
|
119
124
|
child.on('error', (error) => {
|
|
120
125
|
reject(error);
|
|
@@ -138,7 +143,7 @@ export async function execInheritCapture(command, args, options = {}) {
|
|
|
138
143
|
cwd: options.cwd,
|
|
139
144
|
env: { ...process.env, ...options.env },
|
|
140
145
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
141
|
-
|
|
146
|
+
signal: buildSignalFromTimeout(options.timeout),
|
|
142
147
|
});
|
|
143
148
|
const out = createStreamCollector(process.stdout);
|
|
144
149
|
const err = createStreamCollector(process.stderr);
|
|
@@ -16,6 +16,24 @@ export declare function isNumber(value: unknown): value is number;
|
|
|
16
16
|
* @returns True if value is a positive integer
|
|
17
17
|
*/
|
|
18
18
|
export declare function isPositiveInteger(value: unknown): value is number;
|
|
19
|
+
/**
|
|
20
|
+
* Parses a CLI flag value as a positive integer. Throws
|
|
21
|
+
* {@link InvalidArgumentError} on NaN, non-integer, or non-positive input.
|
|
22
|
+
*
|
|
23
|
+
* Intended for use inside Commander `argParser` bodies where the raw
|
|
24
|
+
* input arrives as a string. Without this wrapper the default pattern
|
|
25
|
+
* (`parseInt(v, 10)`) silently hands NaN to downstream planners, which
|
|
26
|
+
* then embed it into filenames / orders instead of failing fast.
|
|
27
|
+
*
|
|
28
|
+
* Rejects leading-zero forms ("01"), decimals ("1.5"), whitespace, and
|
|
29
|
+
* non-numeric garbage via a strict regex — we only accept the canonical
|
|
30
|
+
* representation so there is no ambiguity between what the user typed
|
|
31
|
+
* and what the value becomes on disk.
|
|
32
|
+
*
|
|
33
|
+
* @param flagName - Flag name to include in the error (e.g. `--order`)
|
|
34
|
+
* @param rawValue - Raw string value from Commander
|
|
35
|
+
*/
|
|
36
|
+
export declare function parsePositiveIntegerFlag(flagName: string, rawValue: string): number;
|
|
19
37
|
/**
|
|
20
38
|
* Checks whether a value is a boolean.
|
|
21
39
|
* @param value - Value to check
|
|
@@ -48,7 +66,7 @@ export declare function assertString(value: unknown, name: string): asserts valu
|
|
|
48
66
|
export declare function assertObject(value: unknown, name: string): asserts value is Record<string, unknown>;
|
|
49
67
|
/**
|
|
50
68
|
* Validates a Firefox version string.
|
|
51
|
-
* Accepts formats like "146.0", "146.0.1", "
|
|
69
|
+
* Accepts formats like "146.0", "146.0.1", "146.0esr", "147.0b1"
|
|
52
70
|
*/
|
|
53
71
|
export declare function isValidFirefoxVersion(version: string): boolean;
|
|
54
72
|
/**
|
|
@@ -89,7 +107,7 @@ export declare function inferProductFromVersion(version: string): 'firefox' | 'f
|
|
|
89
107
|
* Validates that a Firefox product and version are compatible.
|
|
90
108
|
*
|
|
91
109
|
* Rules:
|
|
92
|
-
* - `firefox-esr` requires an ESR version (e.g. "
|
|
110
|
+
* - `firefox-esr` requires an ESR version (e.g. "146.0esr", "128.0.1esr").
|
|
93
111
|
* - `firefox-beta` requires a beta version (e.g. "147.0b1").
|
|
94
112
|
* - `firefox` (stable) rejects both ESR and beta version strings.
|
|
95
113
|
*
|
|
@@ -28,6 +28,29 @@ export function isNumber(value) {
|
|
|
28
28
|
export function isPositiveInteger(value) {
|
|
29
29
|
return isNumber(value) && Number.isInteger(value) && value > 0;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Parses a CLI flag value as a positive integer. Throws
|
|
33
|
+
* {@link InvalidArgumentError} on NaN, non-integer, or non-positive input.
|
|
34
|
+
*
|
|
35
|
+
* Intended for use inside Commander `argParser` bodies where the raw
|
|
36
|
+
* input arrives as a string. Without this wrapper the default pattern
|
|
37
|
+
* (`parseInt(v, 10)`) silently hands NaN to downstream planners, which
|
|
38
|
+
* then embed it into filenames / orders instead of failing fast.
|
|
39
|
+
*
|
|
40
|
+
* Rejects leading-zero forms ("01"), decimals ("1.5"), whitespace, and
|
|
41
|
+
* non-numeric garbage via a strict regex — we only accept the canonical
|
|
42
|
+
* representation so there is no ambiguity between what the user typed
|
|
43
|
+
* and what the value becomes on disk.
|
|
44
|
+
*
|
|
45
|
+
* @param flagName - Flag name to include in the error (e.g. `--order`)
|
|
46
|
+
* @param rawValue - Raw string value from Commander
|
|
47
|
+
*/
|
|
48
|
+
export function parsePositiveIntegerFlag(flagName, rawValue) {
|
|
49
|
+
if (!/^[1-9]\d*$/.test(rawValue)) {
|
|
50
|
+
throw new InvalidArgumentError(`${flagName} must be a positive integer, got "${rawValue}".`, flagName);
|
|
51
|
+
}
|
|
52
|
+
return Number.parseInt(rawValue, 10);
|
|
53
|
+
}
|
|
31
54
|
/**
|
|
32
55
|
* Checks whether a value is a boolean.
|
|
33
56
|
* @param value - Value to check
|
|
@@ -74,10 +97,10 @@ export function assertObject(value, name) {
|
|
|
74
97
|
}
|
|
75
98
|
/**
|
|
76
99
|
* Validates a Firefox version string.
|
|
77
|
-
* Accepts formats like "146.0", "146.0.1", "
|
|
100
|
+
* Accepts formats like "146.0", "146.0.1", "146.0esr", "147.0b1"
|
|
78
101
|
*/
|
|
79
102
|
export function isValidFirefoxVersion(version) {
|
|
80
|
-
// Stable/ESR: 146.0, 146.0.1,
|
|
103
|
+
// Stable/ESR: 146.0, 146.0.1, 146.0esr, 128.0.1esr
|
|
81
104
|
// Beta: 147.0b1, 147.0b2
|
|
82
105
|
return /^[1-9]\d{0,2}\.\d+(?:b[1-9]\d*|\.\d+(?:esr)?|esr)?$/.test(version);
|
|
83
106
|
}
|
|
@@ -137,7 +160,7 @@ export function inferProductFromVersion(version) {
|
|
|
137
160
|
* Validates that a Firefox product and version are compatible.
|
|
138
161
|
*
|
|
139
162
|
* Rules:
|
|
140
|
-
* - `firefox-esr` requires an ESR version (e.g. "
|
|
163
|
+
* - `firefox-esr` requires an ESR version (e.g. "146.0esr", "128.0.1esr").
|
|
141
164
|
* - `firefox-beta` requires a beta version (e.g. "147.0b1").
|
|
142
165
|
* - `firefox` (stable) rejects both ESR and beta version strings.
|
|
143
166
|
*
|