@hominis/fireforge 0.14.0 → 0.15.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 +31 -0
- package/README.md +20 -1
- package/dist/src/commands/furnace/create-templates.d.ts +26 -0
- package/dist/src/commands/furnace/create-templates.js +86 -0
- package/dist/src/commands/furnace/create.js +64 -101
- package/dist/src/commands/furnace/deploy.js +3 -3
- package/dist/src/commands/test.js +20 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +2 -0
- package/dist/src/core/config-validate.js +32 -0
- package/dist/src/core/furnace-apply-ftl.d.ts +33 -0
- package/dist/src/core/furnace-apply-ftl.js +102 -0
- package/dist/src/core/furnace-apply-helpers.d.ts +10 -1
- package/dist/src/core/furnace-apply-helpers.js +16 -12
- package/dist/src/core/furnace-apply.js +7 -4
- package/dist/src/core/furnace-config-tokens.d.ts +11 -0
- package/dist/src/core/furnace-config-tokens.js +28 -0
- package/dist/src/core/furnace-config.d.ts +6 -0
- package/dist/src/core/furnace-config.js +8 -1
- package/dist/src/core/furnace-constants.d.ts +20 -0
- package/dist/src/core/furnace-constants.js +32 -0
- package/dist/src/core/furnace-registration-ast.d.ts +13 -1
- package/dist/src/core/furnace-registration-ast.js +58 -25
- package/dist/src/core/furnace-registration.d.ts +27 -0
- package/dist/src/core/furnace-registration.js +96 -0
- package/dist/src/core/furnace-validate-accessibility.js +8 -2
- package/dist/src/core/furnace-validate-helpers.d.ts +8 -0
- package/dist/src/core/furnace-validate-helpers.js +81 -0
- package/dist/src/core/furnace-validate-registration.d.ts +8 -2
- package/dist/src/core/furnace-validate-registration.js +34 -9
- package/dist/src/core/furnace-validate.js +2 -2
- package/dist/src/core/marionette-preflight.d.ts +39 -0
- package/dist/src/core/marionette-preflight.js +210 -0
- package/dist/src/types/commands/options.d.ts +6 -0
- package/dist/src/types/config.d.ts +7 -0
- package/dist/src/types/furnace.d.ts +8 -0
- package/dist/src/utils/process.d.ts +15 -2
- package/dist/src/utils/process.js +73 -0
- package/package.json +1 -1
|
@@ -108,6 +108,15 @@ export async function execStream(command, args, options = {}) {
|
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
110
110
|
* Executes a command and inherits stdio (shows output directly).
|
|
111
|
+
*
|
|
112
|
+
* Graceful shutdown: when the FireForge process receives SIGINT/SIGTERM, the
|
|
113
|
+
* signal is forwarded to the child as SIGTERM and a short grace timer (default
|
|
114
|
+
* 1500ms) runs before escalating to SIGKILL. A second matching signal during
|
|
115
|
+
* the grace period triggers an immediate SIGKILL — matching the usual
|
|
116
|
+
* "hit Ctrl-C again to force-quit" UX. Without this, Firefox's AsyncShutdown
|
|
117
|
+
* / profileBeforeChange blockers (which flush in-memory state to disk) can be
|
|
118
|
+
* racing the OS child-exit path, losing the last few seconds of edits.
|
|
119
|
+
*
|
|
111
120
|
* @param command - Command to execute
|
|
112
121
|
* @param args - Command arguments
|
|
113
122
|
* @param options - Execution options
|
|
@@ -121,14 +130,74 @@ export async function execInherit(command, args, options = {}) {
|
|
|
121
130
|
stdio: 'inherit',
|
|
122
131
|
signal: buildSignalFromTimeout(options.timeout),
|
|
123
132
|
});
|
|
133
|
+
const graceMs = options.shutdownGraceMs ?? 1500;
|
|
134
|
+
const { dispose } = installGracefulShutdownForwarder(child, graceMs);
|
|
124
135
|
child.on('error', (error) => {
|
|
136
|
+
dispose();
|
|
125
137
|
reject(error);
|
|
126
138
|
});
|
|
127
139
|
child.on('close', (code, signal) => {
|
|
140
|
+
dispose();
|
|
128
141
|
resolve(exitCodeFromClose(code, signal));
|
|
129
142
|
});
|
|
130
143
|
});
|
|
131
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Wires parent-process SIGINT/SIGTERM to a child: first signal → child.kill
|
|
147
|
+
* (SIGTERM) + grace timer; second matching signal → immediate SIGKILL; grace
|
|
148
|
+
* timer expiry → SIGKILL. Returns a `dispose()` that clears the listeners and
|
|
149
|
+
* any outstanding timer. Callers must invoke `dispose()` from both the child's
|
|
150
|
+
* `close` and `error` handlers so the process does not accumulate signal
|
|
151
|
+
* listeners across repeated spawns.
|
|
152
|
+
*/
|
|
153
|
+
function installGracefulShutdownForwarder(child, graceMs) {
|
|
154
|
+
let graceTimer;
|
|
155
|
+
const forwarded = new Set();
|
|
156
|
+
const escalate = () => {
|
|
157
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
158
|
+
return;
|
|
159
|
+
try {
|
|
160
|
+
child.kill('SIGKILL');
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Child is already gone — nothing to do.
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const handleSignal = (signal) => {
|
|
167
|
+
if (forwarded.has(signal)) {
|
|
168
|
+
// Second receipt of the same signal while still running: escalate now.
|
|
169
|
+
escalate();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
forwarded.add(signal);
|
|
173
|
+
try {
|
|
174
|
+
child.kill('SIGTERM');
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// If the child can't accept SIGTERM (already dead), nothing to do.
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
graceTimer = setTimeout(escalate, graceMs);
|
|
181
|
+
graceTimer.unref();
|
|
182
|
+
};
|
|
183
|
+
const onSigint = () => {
|
|
184
|
+
handleSignal('SIGINT');
|
|
185
|
+
};
|
|
186
|
+
const onSigterm = () => {
|
|
187
|
+
handleSignal('SIGTERM');
|
|
188
|
+
};
|
|
189
|
+
process.on('SIGINT', onSigint);
|
|
190
|
+
process.on('SIGTERM', onSigterm);
|
|
191
|
+
const dispose = () => {
|
|
192
|
+
process.off('SIGINT', onSigint);
|
|
193
|
+
process.off('SIGTERM', onSigterm);
|
|
194
|
+
if (graceTimer) {
|
|
195
|
+
clearTimeout(graceTimer);
|
|
196
|
+
graceTimer = undefined;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
return { dispose };
|
|
200
|
+
}
|
|
132
201
|
/**
|
|
133
202
|
* Executes a command while inheriting stdin, streaming stdout/stderr live,
|
|
134
203
|
* and capturing the emitted output for diagnostics.
|
|
@@ -149,10 +218,14 @@ export async function execInheritCapture(command, args, options = {}) {
|
|
|
149
218
|
const err = createStreamCollector(process.stderr);
|
|
150
219
|
child.stdout.on('data', out.onData);
|
|
151
220
|
child.stderr.on('data', err.onData);
|
|
221
|
+
const graceMs = options.shutdownGraceMs ?? 1500;
|
|
222
|
+
const { dispose } = installGracefulShutdownForwarder(child, graceMs);
|
|
152
223
|
child.on('error', (error) => {
|
|
224
|
+
dispose();
|
|
153
225
|
reject(error);
|
|
154
226
|
});
|
|
155
227
|
child.on('close', (code, signal) => {
|
|
228
|
+
dispose();
|
|
156
229
|
resolve({
|
|
157
230
|
stdout: out.getText(),
|
|
158
231
|
stderr: err.getText(),
|