@hominis/fireforge 0.47.1 → 0.49.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 +83 -0
- package/dist/bin/fireforge.js +5 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli.js +16 -28
- package/dist/src/commands/build.js +30 -4
- package/dist/src/commands/doctor-engine-eslint.d.ts +3 -0
- package/dist/src/commands/doctor-engine-eslint.js +12 -0
- package/dist/src/commands/doctor-macos-sdk.d.ts +8 -0
- package/dist/src/commands/doctor-macos-sdk.js +158 -0
- package/dist/src/commands/doctor-source-pin.js +9 -2
- package/dist/src/commands/doctor.js +4 -0
- package/dist/src/commands/download.js +64 -3
- package/dist/src/commands/furnace/apply.js +10 -4
- package/dist/src/commands/furnace/create-templates.js +0 -3
- package/dist/src/commands/import.js +5 -2
- package/dist/src/commands/lint-per-patch.js +44 -6
- package/dist/src/commands/lint-per-run-checkjs.d.ts +3 -0
- package/dist/src/commands/lint-per-run-checkjs.js +1 -0
- package/dist/src/commands/lint.js +14 -0
- package/dist/src/commands/manifest.d.ts +1 -2
- package/dist/src/commands/patch/delete.js +18 -4
- package/dist/src/commands/re-export-lint.d.ts +17 -2
- package/dist/src/commands/re-export-lint.js +33 -4
- package/dist/src/commands/re-export-options.js +7 -0
- package/dist/src/commands/re-export-register.js +1 -0
- package/dist/src/commands/re-export-removed.d.ts +4 -0
- package/dist/src/commands/re-export-removed.js +49 -0
- package/dist/src/commands/re-export-single.js +4 -1
- package/dist/src/commands/re-export.js +8 -2
- package/dist/src/commands/rebase/confirm.d.ts +12 -1
- package/dist/src/commands/rebase/confirm.js +6 -3
- package/dist/src/commands/rebase/index.js +77 -13
- package/dist/src/commands/run.js +43 -7
- package/dist/src/commands/status.js +5 -3
- package/dist/src/commands/test-browser-preflight.d.ts +28 -0
- package/dist/src/commands/test-browser-preflight.js +181 -0
- package/dist/src/commands/test-build-phase.js +30 -7
- package/dist/src/commands/test-checkjs.d.ts +3 -0
- package/dist/src/commands/test-checkjs.js +27 -0
- package/dist/src/commands/test-diagnose.js +3 -0
- package/dist/src/commands/test-harness-env.d.ts +5 -6
- package/dist/src/commands/test-harness-env.js +27 -7
- package/dist/src/commands/test-modes.d.ts +2 -4
- package/dist/src/commands/test-register.js +11 -5
- package/dist/src/commands/test-run.d.ts +2 -0
- package/dist/src/commands/test-run.js +21 -1
- package/dist/src/commands/test-verdict.d.ts +2 -2
- package/dist/src/commands/test.js +83 -211
- package/dist/src/commands/typecheck.d.ts +5 -0
- package/dist/src/commands/typecheck.js +7 -1
- package/dist/src/core/build-baseline-types.d.ts +12 -14
- package/dist/src/core/build-baseline-validate.d.ts +3 -0
- package/dist/src/core/build-baseline-validate.js +53 -0
- package/dist/src/core/build-baseline.d.ts +4 -3
- package/dist/src/core/build-baseline.js +23 -11
- package/dist/src/core/build-prepare.d.ts +2 -0
- package/dist/src/core/build-prepare.js +67 -35
- package/dist/src/core/component-source-guard.d.ts +20 -0
- package/dist/src/core/component-source-guard.js +68 -0
- package/dist/src/core/config-paths.d.ts +1 -1
- package/dist/src/core/config-paths.js +1 -0
- package/dist/src/core/config-validate.js +7 -0
- package/dist/src/core/coverage-extend.d.ts +5 -34
- package/dist/src/core/coverage-extend.js +26 -37
- package/dist/src/core/discard-baseline.js +2 -1
- package/dist/src/core/engine-write-boundary.d.ts +5 -0
- package/dist/src/core/engine-write-boundary.js +41 -0
- package/dist/src/core/forward-registrations.d.ts +33 -0
- package/dist/src/core/forward-registrations.js +16 -0
- package/dist/src/core/furnace-apply-helpers.js +8 -2
- package/dist/src/core/furnace-apply.js +7 -0
- package/dist/src/core/furnace-validate-accessibility.js +9 -12
- package/dist/src/core/furnace-validate.js +0 -43
- package/dist/src/core/gecko-dom-types.d.ts +2 -0
- package/dist/src/core/gecko-dom-types.js +17 -0
- package/dist/src/core/git-file-ops.d.ts +21 -0
- package/dist/src/core/git-file-ops.js +75 -0
- package/dist/src/core/git-status.d.ts +14 -0
- package/dist/src/core/git-status.js +41 -2
- package/dist/src/core/lit-template-regions.d.ts +4 -0
- package/dist/src/core/lit-template-regions.js +50 -0
- package/dist/src/core/mach-build-artifacts.d.ts +6 -6
- package/dist/src/core/mach-build-artifacts.js +13 -7
- package/dist/src/core/mach-mozconfig.d.ts +2 -1
- package/dist/src/core/mach-mozconfig.js +2 -0
- package/dist/src/core/mach-resource-shim.d.ts +1 -1
- package/dist/src/core/mach-resource-shim.js +2 -0
- package/dist/src/core/marionette-port.d.ts +2 -0
- package/dist/src/core/marionette-port.js +38 -53
- package/dist/src/core/mochitest-server-port.d.ts +1 -1
- package/dist/src/core/mochitest-server-port.js +20 -17
- package/dist/src/core/objdir-maintenance.d.ts +10 -0
- package/dist/src/core/objdir-maintenance.js +151 -0
- package/dist/src/core/patch-apply-fuzz.d.ts +12 -0
- package/dist/src/core/patch-apply-fuzz.js +27 -6
- package/dist/src/core/patch-apply.d.ts +13 -0
- package/dist/src/core/patch-apply.js +27 -2
- package/dist/src/core/patch-lint-checkjs-cache.d.ts +18 -0
- package/dist/src/core/patch-lint-checkjs-cache.js +45 -0
- package/dist/src/core/patch-lint-checkjs.d.ts +6 -0
- package/dist/src/core/patch-lint-checkjs.js +10 -2
- package/dist/src/core/patch-lint-cross.d.ts +7 -1
- package/dist/src/core/patch-lint-cross.js +13 -12
- package/dist/src/core/patch-lint-forward-registration-extended.d.ts +38 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +240 -0
- package/dist/src/core/patch-lint-forward-registration.d.ts +18 -5
- package/dist/src/core/patch-lint-forward-registration.js +83 -37
- package/dist/src/core/patch-lint.d.ts +11 -0
- package/dist/src/core/patch-lint.js +20 -2
- package/dist/src/core/patch-transform.d.ts +14 -13
- package/dist/src/core/patch-transform.js +29 -17
- package/dist/src/core/preflight-wait.d.ts +2 -0
- package/dist/src/core/preflight-wait.js +24 -0
- package/dist/src/core/process-owner.d.ts +9 -0
- package/dist/src/core/process-owner.js +43 -0
- package/dist/src/core/rebase-dry-run.d.ts +48 -0
- package/dist/src/core/rebase-dry-run.js +105 -0
- package/dist/src/core/run-profile.d.ts +50 -0
- package/dist/src/core/run-profile.js +131 -0
- package/dist/src/core/status-binary.d.ts +1 -1
- package/dist/src/core/status-binary.js +3 -1
- package/dist/src/core/status-classify.js +29 -2
- package/dist/src/core/test-harness-crash.d.ts +1 -1
- package/dist/src/core/test-harness-crash.js +5 -5
- package/dist/src/core/test-host-state.d.ts +14 -0
- package/dist/src/core/test-host-state.js +80 -0
- package/dist/src/core/test-profile-files.d.ts +17 -0
- package/dist/src/core/test-profile-files.js +75 -0
- package/dist/src/core/test-stale-check.d.ts +6 -7
- package/dist/src/core/test-stale-check.js +9 -32
- package/dist/src/core/token-category.d.ts +7 -26
- package/dist/src/core/token-category.js +68 -121
- package/dist/src/core/token-inventory.js +12 -7
- package/dist/src/core/toolchain-preflight.d.ts +6 -0
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/typecheck.d.ts +11 -1
- package/dist/src/core/typecheck.js +86 -12
- package/dist/src/errors/rebase.d.ts +9 -0
- package/dist/src/errors/rebase.js +20 -0
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +2 -1
- package/dist/src/types/commands/options.d.ts +29 -3
- package/dist/src/types/config.d.ts +10 -0
- package/dist/src/utils/assert.js +2 -7
- package/dist/src/utils/child-env.d.ts +0 -3
- package/dist/src/utils/child-env.js +0 -3
- package/dist/src/utils/concurrency.d.ts +2 -2
- package/dist/src/utils/concurrency.js +15 -4
- package/dist/src/utils/fs.js +11 -1
- package/dist/src/utils/options.d.ts +16 -1
- package/dist/src/utils/options.js +5 -3
- package/dist/src/utils/paths.js +3 -2
- package/dist/src/utils/process-group.d.ts +1 -2
- package/dist/src/utils/process-group.js +1 -2
- package/dist/src/utils/process-input.d.ts +27 -0
- package/dist/src/utils/process-input.js +46 -0
- package/dist/src/utils/process.d.ts +5 -2
- package/dist/src/utils/process.js +21 -8
- package/dist/src/utils/validation.d.ts +11 -0
- package/dist/src/utils/validation.js +43 -0
- package/docs/configuration.md +82 -0
- package/docs/exit-codes.md +1 -1
- package/docs/lifecycle-invariants.md +13 -8
- package/docs/machine-output.md +36 -19
- package/docs/patch-workflow.md +50 -1
- package/docs/testing.md +77 -12
- package/package.json +10 -5
|
@@ -18,11 +18,15 @@
|
|
|
18
18
|
* without installing it, the function returns a clear error pointing
|
|
19
19
|
* at `npm install typescript`.
|
|
20
20
|
*/
|
|
21
|
-
import {
|
|
21
|
+
import { mkdir, readdir, rm } from 'node:fs/promises';
|
|
22
|
+
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
22
23
|
import { toError } from '../utils/errors.js';
|
|
23
24
|
import { pathExists } from '../utils/fs.js';
|
|
25
|
+
import { sha256Hex } from '../utils/hash.js';
|
|
24
26
|
import { verbose } from '../utils/logger.js';
|
|
27
|
+
import { getPackageVersion } from '../utils/package-root.js';
|
|
25
28
|
import { normalizePathSlashes } from '../utils/paths.js';
|
|
29
|
+
import { withGeckoDomTypes } from './gecko-dom-types.js';
|
|
26
30
|
import { composeShimSource, SHIM_FILENAME, SUPPRESSED_DIAGNOSTIC_CODES, UNDEFINED_IDENTIFIER_CODES, UNDEFINED_IDENTIFIER_HINT, } from './typecheck-shim.js';
|
|
27
31
|
/**
|
|
28
32
|
* Sentinel string surfaced as a `TypecheckIssue.message` when the
|
|
@@ -60,7 +64,7 @@ export const CHECK_JS_DISABLED_NOTICE = 'Project sets "checkJs: false" — skipp
|
|
|
60
64
|
* in declared order. The CLI is responsible for printing the issues
|
|
61
65
|
* and computing the exit code.
|
|
62
66
|
*/
|
|
63
|
-
export async function runTypecheck(projectRoot, cfg) {
|
|
67
|
+
export async function runTypecheck(projectRoot, cfg, runOptions = {}) {
|
|
64
68
|
// Dynamic import, so typescript stays a dev dependency. Same pattern
|
|
65
69
|
// as `patch-lint-checkjs.ts`. The empty-projects-array case is
|
|
66
70
|
// already rejected by config-validate, so we don't gate the import
|
|
@@ -133,7 +137,9 @@ export async function runTypecheck(projectRoot, cfg) {
|
|
|
133
137
|
});
|
|
134
138
|
continue;
|
|
135
139
|
}
|
|
136
|
-
results.push(await runTypecheckForProject(ts, projectRoot, projectPath, shimSource, cfg.undefinedIdentifiers ?? 'warning'
|
|
140
|
+
results.push(await runTypecheckForProject(ts, projectRoot, projectPath, shimSource, cfg.undefinedIdentifiers ?? 'warning', runOptions.noCache === true
|
|
141
|
+
? undefined
|
|
142
|
+
: (runOptions.cacheDir ?? join(projectRoot, '.fireforge', 'typecheck'))));
|
|
137
143
|
}
|
|
138
144
|
return results;
|
|
139
145
|
}
|
|
@@ -151,7 +157,7 @@ function resolveProjectExtraShim(cfg, projectPath) {
|
|
|
151
157
|
return cfg.extraShim;
|
|
152
158
|
}
|
|
153
159
|
/** Runs typecheck for a single jsconfig path, isolating its failures. */
|
|
154
|
-
async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource, undefinedIdentifiers = 'warning') {
|
|
160
|
+
async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource, undefinedIdentifiers = 'warning', cacheDir) {
|
|
155
161
|
const absConfig = resolve(projectRoot, projectPath);
|
|
156
162
|
if (!(await pathExists(absConfig))) {
|
|
157
163
|
return {
|
|
@@ -223,12 +229,12 @@ async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource,
|
|
|
223
229
|
noEmit: true,
|
|
224
230
|
allowJs: parsed.options.allowJs ?? true,
|
|
225
231
|
checkJs: parsed.options.checkJs ?? true,
|
|
226
|
-
//
|
|
227
|
-
// `incremental` (or names a `tsBuildInfoFile`) would
|
|
228
|
-
// drop a `.tsbuildinfo` inside the primary engine
|
|
229
|
-
// writer that invalidates a concurrent
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
+
// Never the jsconfig's own incremental sidecar: a user jsconfig under
|
|
233
|
+
// `engine/` that sets `incremental` (or names a `tsBuildInfoFile`) would
|
|
234
|
+
// have this command drop a `.tsbuildinfo` inside the primary engine
|
|
235
|
+
// checkout, a second writer that invalidates a concurrent
|
|
236
|
+
// `fireforge test`'s engine fingerprint. FireForge's own build info
|
|
237
|
+
// lives under `.fireforge/typecheck/` instead (see below).
|
|
232
238
|
incremental: false,
|
|
233
239
|
// skipLibCheck is not forced. The user owns it via their jsconfig.
|
|
234
240
|
};
|
|
@@ -247,7 +253,7 @@ async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource,
|
|
|
247
253
|
// filtered out below, so the pass reports failures that do not exist.
|
|
248
254
|
const shimPath = normalizePathSlashes(resolve(projectDir, `.fireforge-${SHIM_FILENAME}`));
|
|
249
255
|
const rootFiles = [...parsed.fileNames, shimPath];
|
|
250
|
-
const defaultHost = ts.createCompilerHost(options);
|
|
256
|
+
const defaultHost = withGeckoDomTypes(ts, ts.createCompilerHost(options));
|
|
251
257
|
const host = {
|
|
252
258
|
...defaultHost,
|
|
253
259
|
getSourceFile(fileName, languageVersion, onError, shouldCreate) {
|
|
@@ -267,7 +273,37 @@ async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource,
|
|
|
267
273
|
return defaultHost.readFile(fileName);
|
|
268
274
|
},
|
|
269
275
|
};
|
|
270
|
-
|
|
276
|
+
// Incremental build info, keyed by everything outside the program's own
|
|
277
|
+
// files that can change a verdict: the composed shim, the TypeScript
|
|
278
|
+
// version, the compiler options, the undefined-identifier gate and the
|
|
279
|
+
// FireForge version. A change to any of them selects a different file,
|
|
280
|
+
// so a stale verdict is never replayed. Files inside the program are
|
|
281
|
+
// tracked by TypeScript's own per-file hashes in the build info.
|
|
282
|
+
const buildInfoFile = cacheDir === undefined
|
|
283
|
+
? undefined
|
|
284
|
+
: await prepareBuildInfoFile(cacheDir, projectPath, {
|
|
285
|
+
shim: sha256Hex(shimSource),
|
|
286
|
+
typescript: ts.version,
|
|
287
|
+
fireforge: getPackageVersion(),
|
|
288
|
+
undefinedIdentifiers,
|
|
289
|
+
options,
|
|
290
|
+
});
|
|
291
|
+
let program;
|
|
292
|
+
let writeBuildInfo = () => undefined;
|
|
293
|
+
if (buildInfoFile === undefined) {
|
|
294
|
+
program = ts.createProgram(rootFiles, options, host);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
// Always check the entire project in root order. Restoring TypeScript's
|
|
298
|
+
// per-file semantic cache makes checkJs inference depend on the first
|
|
299
|
+
// invalidated importer and can both invent and hide diagnostics.
|
|
300
|
+
const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(rootFiles, { ...options, incremental: true, tsBuildInfoFile: buildInfoFile }, withSourceFileVersions(host));
|
|
301
|
+
program = builder;
|
|
302
|
+
// Under noEmit, emit() writes the build info and nothing else.
|
|
303
|
+
writeBuildInfo = () => {
|
|
304
|
+
builder.emit();
|
|
305
|
+
};
|
|
306
|
+
}
|
|
271
307
|
// Collect the full diagnostic set. patchLint reads only semantic +
|
|
272
308
|
// syntactic, which is fine for hygiene but wrong for CI: a misconfigured
|
|
273
309
|
// `lib: ["es2015"]` or a missing `paths` target should fail
|
|
@@ -298,6 +334,7 @@ async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource,
|
|
|
298
334
|
}
|
|
299
335
|
issues.push(diagnosticToIssue(ts, diag, absConfig, projectPath));
|
|
300
336
|
}
|
|
337
|
+
writeBuildInfo();
|
|
301
338
|
verbose(`typecheck: ${projectPath} — analyzed ${parsed.fileNames.length} file(s), found ${issues.length} issue(s)`);
|
|
302
339
|
return {
|
|
303
340
|
project: projectPath,
|
|
@@ -305,6 +342,43 @@ async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource,
|
|
|
305
342
|
filesChecked: parsed.fileNames.length,
|
|
306
343
|
};
|
|
307
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Stamps every source file with a content-hash version, which the
|
|
347
|
+
* incremental builder requires and a plain compiler host never sets.
|
|
348
|
+
* `ts.createIncrementalCompilerHost` does the same internally, but the
|
|
349
|
+
* shim here is served by a custom host rather than read from disk.
|
|
350
|
+
*/
|
|
351
|
+
function withSourceFileVersions(host) {
|
|
352
|
+
const getSourceFile = host.getSourceFile.bind(host);
|
|
353
|
+
return {
|
|
354
|
+
...host,
|
|
355
|
+
getSourceFile(...args) {
|
|
356
|
+
const file = getSourceFile(...args);
|
|
357
|
+
if (file !== undefined) {
|
|
358
|
+
file.version = sha256Hex(file.text);
|
|
359
|
+
}
|
|
360
|
+
return file;
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Resolves the build-info path for one project and key, creating the cache
|
|
366
|
+
* directory and removing the project's build info for any other key: a
|
|
367
|
+
* shim or TypeScript change leaves the old file unreachable anyway.
|
|
368
|
+
* @returns Absolute build-info path
|
|
369
|
+
*/
|
|
370
|
+
async function prepareBuildInfoFile(cacheDir, projectPath, keyInputs) {
|
|
371
|
+
const slug = projectPath.replace(/[^A-Za-z0-9._-]+/g, '_');
|
|
372
|
+
const key = sha256Hex(JSON.stringify(keyInputs)).slice(0, 16);
|
|
373
|
+
const fileName = `${slug}-${key}.tsbuildinfo`;
|
|
374
|
+
await mkdir(cacheDir, { recursive: true });
|
|
375
|
+
for (const entry of await readdir(cacheDir)) {
|
|
376
|
+
if (entry !== fileName && entry.startsWith(`${slug}-`) && entry.endsWith('.tsbuildinfo')) {
|
|
377
|
+
await rm(join(cacheDir, entry), { force: true });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return join(cacheDir, fileName);
|
|
381
|
+
}
|
|
308
382
|
/**
|
|
309
383
|
* Converts a TS Diagnostic to a TypecheckIssue. The `fallbackFile`
|
|
310
384
|
* is used when the diagnostic carries no source file (typical for
|
|
@@ -31,3 +31,12 @@ export declare class NoRebaseSessionError extends RebaseError {
|
|
|
31
31
|
export declare class CorruptRebaseSessionError extends RebaseError {
|
|
32
32
|
constructor(sessionPath: string, reason: string);
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Thrown by `rebase --dry-run` when the replay rejects at least one patch.
|
|
36
|
+
* A dry run that exited 0 regardless used to read as "the rebase is ready".
|
|
37
|
+
*/
|
|
38
|
+
export declare class RebaseDryRunRejectError extends RebaseError {
|
|
39
|
+
readonly rejected: readonly string[];
|
|
40
|
+
constructor(rejected: readonly string[]);
|
|
41
|
+
get userMessage(): string;
|
|
42
|
+
}
|
|
@@ -49,3 +49,23 @@ export class CorruptRebaseSessionError extends RebaseError {
|
|
|
49
49
|
'or delete the file to start over.');
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Thrown by `rebase --dry-run` when the replay rejects at least one patch.
|
|
54
|
+
* A dry run that exited 0 regardless used to read as "the rebase is ready".
|
|
55
|
+
*/
|
|
56
|
+
export class RebaseDryRunRejectError extends RebaseError {
|
|
57
|
+
rejected;
|
|
58
|
+
constructor(rejected) {
|
|
59
|
+
super(`Dry run: ${rejected.length} patch(es) would not apply: ${rejected.join(', ')}. ` +
|
|
60
|
+
'engine/ was not modified.');
|
|
61
|
+
this.rejected = rejected;
|
|
62
|
+
}
|
|
63
|
+
get userMessage() {
|
|
64
|
+
return (`Rebase Error: ${this.message}` +
|
|
65
|
+
remedies([
|
|
66
|
+
'The per-patch lines above name the files that did not apply',
|
|
67
|
+
'Run "fireforge rebase" to start the real rebase; it stops at the first reject for manual resolution',
|
|
68
|
+
'Later rejects may cascade from an earlier one; resolve in queue order',
|
|
69
|
+
]));
|
|
70
|
+
}
|
|
71
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
* @packageDocumentation
|
|
17
17
|
*/
|
|
18
18
|
export { loadConfig, validateConfig } from './core/config.js';
|
|
19
|
+
export type { FindForwardRegistrationsOptions, ForwardRegistration, ForwardRegistrationKind, ForwardRegistrationScope, } from './core/forward-registrations.js';
|
|
20
|
+
export { findForwardRegistrations } from './core/forward-registrations.js';
|
|
19
21
|
export type { ApplyAllComponentsResult } from './core/furnace-apply.js';
|
|
20
22
|
export { applyAllComponents } from './core/furnace-apply.js';
|
|
21
23
|
export { ensureFurnaceConfig, loadFurnaceConfig, loadFurnaceState, saveFurnaceState, validateFurnaceConfig, } from './core/furnace-config.js';
|
|
22
24
|
export { validateAllComponents, validateComponent } from './core/furnace-validate.js';
|
|
23
25
|
export type { PatchSizeTierDecision } from './core/patch-lint.js';
|
|
24
|
-
export { countNonBinaryDiffLines, getPatchSizeThresholds, resolvePatchSizeTier, } from './core/patch-lint.js';
|
|
26
|
+
export { countNonBinaryDiffLines, filesMeasuredByFileTooLarge, getPatchSizeThresholds, resolvePatchSizeTier, } from './core/patch-lint.js';
|
|
25
27
|
export type { AddTokenOptions, AddTokenResult, TokenMode } from './core/token-manager.js';
|
|
26
28
|
export { addToken, getTokensCssPath, validateTokenAdd } from './core/token-manager.js';
|
|
27
29
|
export { CancellationError, CommandError, FireForgeError, GeneralError, InvalidArgumentError, ResolutionError, } from './errors/base.js';
|
package/dist/src/index.js
CHANGED
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
* @packageDocumentation
|
|
18
18
|
*/
|
|
19
19
|
export { loadConfig, validateConfig } from './core/config.js';
|
|
20
|
+
export { findForwardRegistrations } from './core/forward-registrations.js';
|
|
20
21
|
export { applyAllComponents } from './core/furnace-apply.js';
|
|
21
22
|
export { ensureFurnaceConfig, loadFurnaceConfig, loadFurnaceState, saveFurnaceState, validateFurnaceConfig, } from './core/furnace-config.js';
|
|
22
23
|
export { validateAllComponents, validateComponent } from './core/furnace-validate.js';
|
|
23
|
-
export { countNonBinaryDiffLines, getPatchSizeThresholds, resolvePatchSizeTier, } from './core/patch-lint.js';
|
|
24
|
+
export { countNonBinaryDiffLines, filesMeasuredByFileTooLarge, getPatchSizeThresholds, resolvePatchSizeTier, } from './core/patch-lint.js';
|
|
24
25
|
export { addToken, getTokensCssPath, validateTokenAdd } from './core/token-manager.js';
|
|
25
26
|
export { CancellationError, CommandError, FireForgeError, GeneralError, InvalidArgumentError, ResolutionError, } from './errors/base.js';
|
|
26
27
|
export { ExitCode } from './errors/codes.js';
|
|
@@ -85,6 +85,8 @@ export interface SetupOptions {
|
|
|
85
85
|
export interface DownloadOptions {
|
|
86
86
|
/** Force re-download, deleting existing engine/ */
|
|
87
87
|
force?: boolean;
|
|
88
|
+
/** With `force`: skip the confirmation for an engine with changes to lose */
|
|
89
|
+
yes?: boolean;
|
|
88
90
|
}
|
|
89
91
|
/**
|
|
90
92
|
* Options for the source command.
|
|
@@ -107,6 +109,8 @@ export interface SourceSetOptions {
|
|
|
107
109
|
* Options for the build command.
|
|
108
110
|
*/
|
|
109
111
|
export interface BuildOptions extends EngineLockWaitable {
|
|
112
|
+
/** Wait for the objdir browser before deploying/building. */
|
|
113
|
+
waitBrowser?: number | boolean;
|
|
110
114
|
/** Fast UI-only rebuild */
|
|
111
115
|
ui?: boolean;
|
|
112
116
|
/** Number of parallel jobs */
|
|
@@ -366,6 +370,8 @@ export interface ReExportOptions extends EngineLockWaitable {
|
|
|
366
370
|
* rather than hidden.
|
|
367
371
|
*/
|
|
368
372
|
expectUnmanaged?: string[];
|
|
373
|
+
/** Explicitly retire named absent patch-created files in a scan-less export. */
|
|
374
|
+
expectRemoved?: string[];
|
|
369
375
|
}
|
|
370
376
|
/**
|
|
371
377
|
* Options for the `fireforge patch tier` subcommand. Sets or clears the
|
|
@@ -497,6 +503,13 @@ export interface RebaseOptions {
|
|
|
497
503
|
export interface RunOptions {
|
|
498
504
|
/** Additional arguments to pass to the browser */
|
|
499
505
|
args?: string[];
|
|
506
|
+
/** Launch with this profile directory, passed to Firefox as `-profile`. */
|
|
507
|
+
profile?: string;
|
|
508
|
+
/**
|
|
509
|
+
* Launch with a fresh temporary profile, removed when the run ends.
|
|
510
|
+
* Implied by `smokeExit` unless `profile` is given.
|
|
511
|
+
*/
|
|
512
|
+
tempProfile?: boolean;
|
|
500
513
|
/**
|
|
501
514
|
* Enable smoke-run mode. Launches the browser, streams the console,
|
|
502
515
|
* sends SIGTERM to the whole process group after `smokeExit` seconds,
|
|
@@ -542,6 +555,12 @@ export interface RunOptions {
|
|
|
542
555
|
* Options for the test command.
|
|
543
556
|
*/
|
|
544
557
|
export interface TestOptions extends EngineLockWaitable {
|
|
558
|
+
/** Queue behind a browser using this objdir; bare flag means 60 seconds. */
|
|
559
|
+
waitBrowser?: number | boolean;
|
|
560
|
+
/** Queue behind the mochitest/Marionette port holder. */
|
|
561
|
+
waitPort?: number | boolean;
|
|
562
|
+
/** Copy a file into the profile at a relative destination (repeatable source=destination). */
|
|
563
|
+
profileFile?: string[];
|
|
545
564
|
/** Run tests in headless mode */
|
|
546
565
|
headless?: boolean;
|
|
547
566
|
/**
|
|
@@ -666,9 +685,10 @@ export interface TestOptions extends EngineLockWaitable {
|
|
|
666
685
|
*/
|
|
667
686
|
perfSamples?: string;
|
|
668
687
|
/**
|
|
669
|
-
* Seeded
|
|
670
|
-
* a number replays
|
|
671
|
-
*
|
|
688
|
+
* Seeded isolated path-argument order for mochitest runs. `true` draws a
|
|
689
|
+
* fresh seed; a number replays the shard permutation. Native mach
|
|
690
|
+
* `--shuffle` within a shard remains unseeded. Also exported as
|
|
691
|
+
* `FIREFORGE_SHUFFLE_SEED` for custom task code and recorded on the verdict.
|
|
672
692
|
*/
|
|
673
693
|
shuffle?: number | boolean;
|
|
674
694
|
}
|
|
@@ -1115,4 +1135,10 @@ export interface LintCommandOptions {
|
|
|
1115
1135
|
* {@link perPatch}.
|
|
1116
1136
|
*/
|
|
1117
1137
|
report?: string;
|
|
1138
|
+
/**
|
|
1139
|
+
* With `--per-patch`: `'full'` (default) prints every NOTICE line;
|
|
1140
|
+
* `'summary'` prints one line per check with its count and patch count.
|
|
1141
|
+
* Errors and warnings always print in full.
|
|
1142
|
+
*/
|
|
1143
|
+
notices?: 'summary' | 'full';
|
|
1118
1144
|
}
|
|
@@ -355,6 +355,16 @@ export interface PatchLintConfig {
|
|
|
355
355
|
* scope for the per-patch tier.
|
|
356
356
|
*/
|
|
357
357
|
prettier?: PatchLintSeverityGate;
|
|
358
|
+
/**
|
|
359
|
+
* Which carriers the `forward-registration` queue rule checks for a line
|
|
360
|
+
* that names a file only a later patch creates. `'support-files'` (the
|
|
361
|
+
* default) checks test-manifest `support-files`. `'extended'` also checks
|
|
362
|
+
* jar.mn pair lines, moz.build path tokens, test-manifest sections and
|
|
363
|
+
* `head`, and customElements.js chrome URLs resolved through the queue's
|
|
364
|
+
* own jar.mn lines. Opt-in: a queue that predates the wider rule can carry
|
|
365
|
+
* such edges, and a new error on install would red its gate.
|
|
366
|
+
*/
|
|
367
|
+
forwardRegistration?: 'support-files' | 'extended';
|
|
358
368
|
/** Enforce JSDoc on class-method exports in patch-owned .sys.mjs files. Default: 'off'. */
|
|
359
369
|
jsdocClassMethods?: PatchLintSeverityGate;
|
|
360
370
|
/** Require ≥1 assertion in any patch-touched browser_*.js test file (new or modified). Default: 'off'. */
|
package/dist/src/utils/assert.js
CHANGED
|
@@ -11,13 +11,8 @@
|
|
|
11
11
|
* says the opposite: nothing the operator could have done would have
|
|
12
12
|
* prevented it.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* permanently-uncovered branch to every function it appears in, and the
|
|
17
|
-
* per-module coverage floors in `scripts/check-coverage-thresholds.mjs`
|
|
18
|
-
* would pay for it in ~40 places. Routed through a call, the branch lives
|
|
19
|
-
* here once and is covered here once, and the `complexity` ceiling in
|
|
20
|
-
* `eslint.config.js` does not see it at all.
|
|
14
|
+
* Shared helpers provide consistent classification and TypeScript narrowing,
|
|
15
|
+
* and keep expensive failure descriptions lazy on successful paths.
|
|
21
16
|
*
|
|
22
17
|
* This module is a leaf: it imports the error class and nothing else, per
|
|
23
18
|
* the dependency-direction note in `src/utils/errors.ts`.
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Environment construction for spawned children.
|
|
3
|
-
*
|
|
4
|
-
* Its own module rather than a helper inside `process.ts` only because that
|
|
5
|
-
* file sits on a 500-line budget. The logic belongs to the exec layer.
|
|
6
3
|
*/
|
|
7
4
|
/** The `env` / `envUnset` slice of exec options this module needs. */
|
|
8
5
|
interface ChildEnvOptions {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
/**
|
|
3
3
|
* Environment construction for spawned children.
|
|
4
|
-
*
|
|
5
|
-
* Its own module rather than a helper inside `process.ts` only because that
|
|
6
|
-
* file sits on a 500-line budget. The logic belongs to the exec layer.
|
|
7
4
|
*/
|
|
8
5
|
/**
|
|
9
6
|
* The child's environment: `process.env`, with `env` merged over it, then
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bounded-concurrency helpers shared by commands that fan out per-file IO
|
|
3
3
|
* (status classification, import's unmanaged-dirty guard, re-export scans,
|
|
4
|
-
* the dry-run purity guard).
|
|
5
|
-
*
|
|
4
|
+
* the dry-run purity guard). On failure, stop scheduling new items and await
|
|
5
|
+
* every started operation before rejecting, so callers can safely clean up.
|
|
6
6
|
*/
|
|
7
7
|
/** Maps items with at most `limit` in-flight promises. Preserves order. */
|
|
8
8
|
export declare function mapWithConcurrency<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bounded-concurrency helpers shared by commands that fan out per-file IO
|
|
3
3
|
* (status classification, import's unmanaged-dirty guard, re-export scans,
|
|
4
|
-
* the dry-run purity guard).
|
|
5
|
-
*
|
|
4
|
+
* the dry-run purity guard). On failure, stop scheduling new items and await
|
|
5
|
+
* every started operation before rejecting, so callers can safely clean up.
|
|
6
6
|
*/
|
|
7
7
|
/** Maps items with at most `limit` in-flight promises. Preserves order. */
|
|
8
8
|
export async function mapWithConcurrency(items, limit, fn) {
|
|
9
|
+
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
10
|
+
throw new RangeError('Concurrency limit must be a positive integer');
|
|
11
|
+
}
|
|
9
12
|
const results = new Array(items.length);
|
|
10
13
|
let next = 0;
|
|
14
|
+
const state = {};
|
|
11
15
|
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
12
|
-
|
|
16
|
+
while (!state.failure) {
|
|
13
17
|
const index = next++;
|
|
14
18
|
if (index >= items.length)
|
|
15
19
|
return;
|
|
16
|
-
|
|
20
|
+
try {
|
|
21
|
+
results[index] = await fn(items[index], index);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
state.failure ??= { error };
|
|
25
|
+
}
|
|
17
26
|
}
|
|
18
27
|
});
|
|
19
28
|
await Promise.all(workers);
|
|
29
|
+
if (state.failure)
|
|
30
|
+
throw state.failure.error;
|
|
20
31
|
return results;
|
|
21
32
|
}
|
package/dist/src/utils/fs.js
CHANGED
|
@@ -99,7 +99,17 @@ export async function isSymlink(path) {
|
|
|
99
99
|
*/
|
|
100
100
|
export async function copyFile(src, dest) {
|
|
101
101
|
await ensureParentDir(dest);
|
|
102
|
-
|
|
102
|
+
// Never follow a destination symlink or truncate a shared hardlink:
|
|
103
|
+
// build/deploy copies must not write through into components/ or a peer.
|
|
104
|
+
const tempPath = createAtomicTempPath(dest);
|
|
105
|
+
try {
|
|
106
|
+
await fsCopyFile(src, tempPath);
|
|
107
|
+
await renameWithRetries(tempPath, dest);
|
|
108
|
+
await syncParentDir(dest);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
await rm(tempPath, { force: true });
|
|
112
|
+
}
|
|
103
113
|
}
|
|
104
114
|
/**
|
|
105
115
|
* Reads a JSON file and parses it.
|
|
@@ -89,7 +89,22 @@ export declare function resolveWaitLockSeconds(value: string | number | boolean
|
|
|
89
89
|
*/
|
|
90
90
|
export declare function sessionWaitLockMs(): number | undefined;
|
|
91
91
|
/**
|
|
92
|
-
*
|
|
92
|
+
* Registers `--wait-lock [seconds]` on a command that takes no lock, so a
|
|
93
|
+
* scripted sequence can blanket-append the flag without hitting a usage
|
|
94
|
+
* error. A command that rejects it with "unknown option" kills the whole
|
|
95
|
+
* sequence with a usage error instead of a lock message.
|
|
96
|
+
*
|
|
97
|
+
* The flag is accepted and ignored here, never silently repurposed, and
|
|
98
|
+
* the help text says so, so nobody reads its presence as evidence that this
|
|
99
|
+
* command waits for anything.
|
|
100
|
+
*
|
|
101
|
+
* The value is parsed and validated exactly as the honoring variant does:
|
|
102
|
+
* `--wait-lock nonsense` must still be a usage error everywhere, or the
|
|
103
|
+
* uniformity would be a lie.
|
|
104
|
+
*/
|
|
105
|
+
export declare function addAcceptedWaitLockOption(command: Command): Command;
|
|
106
|
+
/**
|
|
107
|
+
* Recursively gives every leaf command in the tree a `--wait-lock` flag: the
|
|
93
108
|
* honoring registration where one already exists, the accept-and-ignore
|
|
94
109
|
* one everywhere else.
|
|
95
110
|
*/
|
|
@@ -182,7 +182,7 @@ export function sessionWaitLockMs() {
|
|
|
182
182
|
* `--wait-lock nonsense` must still be a usage error everywhere, or the
|
|
183
183
|
* uniformity would be a lie.
|
|
184
184
|
*/
|
|
185
|
-
function addAcceptedWaitLockOption(command) {
|
|
185
|
+
export function addAcceptedWaitLockOption(command) {
|
|
186
186
|
return command.option('--wait-lock [seconds]', 'Accepted for scripting uniformity and ignored: this command takes no FireForge lock', commanderArgParser((raw) => resolveWaitLockSeconds(raw)));
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
@@ -194,13 +194,15 @@ function hasWaitLockOption(command) {
|
|
|
194
194
|
return command.options.some((option) => option.long === '--wait-lock');
|
|
195
195
|
}
|
|
196
196
|
/**
|
|
197
|
-
* Recursively gives every command in the tree a `--wait-lock` flag: the
|
|
197
|
+
* Recursively gives every leaf command in the tree a `--wait-lock` flag: the
|
|
198
198
|
* honoring registration where one already exists, the accept-and-ignore
|
|
199
199
|
* one everywhere else.
|
|
200
200
|
*/
|
|
201
201
|
export function ensureWaitLockOptionEverywhere(command) {
|
|
202
202
|
for (const sub of command.commands) {
|
|
203
|
-
|
|
203
|
+
// Commander groups otherwise capture a trailing leaf option before the
|
|
204
|
+
// action can see it. Register the uniform flag only on actionable leaves.
|
|
205
|
+
if (sub.commands.length === 0 && !hasWaitLockOption(sub))
|
|
204
206
|
addAcceptedWaitLockOption(sub);
|
|
205
207
|
ensureWaitLockOptionEverywhere(sub);
|
|
206
208
|
}
|
package/dist/src/utils/paths.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import { isAbsolute, relative, resolve } from 'node:path';
|
|
2
|
+
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
3
|
const WINDOWS_ABSOLUTE_PATH = /^[a-zA-Z]:[\\/]/;
|
|
4
4
|
const RELATIVE_PATH_ROOT = resolve('/__fireforge_path_root__');
|
|
5
5
|
/**
|
|
@@ -55,7 +55,8 @@ export function isPathInsideRoot(root, candidate) {
|
|
|
55
55
|
: resolve(resolvedRoot, candidate);
|
|
56
56
|
const relativePath = relative(resolvedRoot, resolvedCandidate);
|
|
57
57
|
return (relativePath === '' ||
|
|
58
|
-
(
|
|
58
|
+
(relativePath !== '..' &&
|
|
59
|
+
!relativePath.startsWith(`..${sep}`) &&
|
|
59
60
|
!isAbsolute(relativePath) &&
|
|
60
61
|
!WINDOWS_ABSOLUTE_PATH.test(relativePath)));
|
|
61
62
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Process-group kill and post-run sweep helpers for the exec layer.
|
|
3
|
-
* out of `process.ts` to keep that file within the per-file line budget.
|
|
2
|
+
* Process-group kill and post-run sweep helpers for the exec layer.
|
|
4
3
|
* Spawn-based rather than `exec`-based so the two modules do not import
|
|
5
4
|
* each other cyclically.
|
|
6
5
|
*/
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
/**
|
|
3
|
-
* Process-group kill and post-run sweep helpers for the exec layer.
|
|
4
|
-
* out of `process.ts` to keep that file within the per-file line budget.
|
|
3
|
+
* Process-group kill and post-run sweep helpers for the exec layer.
|
|
5
4
|
* Spawn-based rather than `exec`-based so the two modules do not import
|
|
6
5
|
* each other cyclically.
|
|
7
6
|
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Options for {@link execWithInput}. */
|
|
2
|
+
export interface ExecWithInputOptions {
|
|
3
|
+
/** Working directory for the command */
|
|
4
|
+
cwd?: string;
|
|
5
|
+
/** Environment variables merged over `process.env` */
|
|
6
|
+
env?: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
/** Result of {@link execWithInput}: stdout stays raw bytes. */
|
|
9
|
+
export interface ExecBufferResult {
|
|
10
|
+
/** Standard output, uncapped and undecoded */
|
|
11
|
+
stdout: Buffer;
|
|
12
|
+
/** Standard error, decoded as UTF-8 */
|
|
13
|
+
stderr: string;
|
|
14
|
+
/** Process exit code */
|
|
15
|
+
exitCode: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Runs a command, writes `input` to its stdin, and returns stdout as raw
|
|
19
|
+
* bytes. For batch protocols (`git cat-file --batch`) whose framing counts
|
|
20
|
+
* bytes: the decoded, 50 MB-capped collector `exec` uses would corrupt
|
|
21
|
+
* both. The caller bounds the input, and so the output.
|
|
22
|
+
* @param command - Command to execute
|
|
23
|
+
* @param args - Command arguments
|
|
24
|
+
* @param input - Bytes written to stdin, which is then closed
|
|
25
|
+
* @param options - Execution options
|
|
26
|
+
*/
|
|
27
|
+
export declare function execWithInput(command: string, args: string[], input: string | Buffer, options?: ExecWithInputOptions): Promise<ExecBufferResult>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Stdin-fed command execution with byte-exact stdout.
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { buildChildEnv } from './child-env.js';
|
|
7
|
+
import { exitCodeFromClose } from './process.js';
|
|
8
|
+
/**
|
|
9
|
+
* Runs a command, writes `input` to its stdin, and returns stdout as raw
|
|
10
|
+
* bytes. For batch protocols (`git cat-file --batch`) whose framing counts
|
|
11
|
+
* bytes: the decoded, 50 MB-capped collector `exec` uses would corrupt
|
|
12
|
+
* both. The caller bounds the input, and so the output.
|
|
13
|
+
* @param command - Command to execute
|
|
14
|
+
* @param args - Command arguments
|
|
15
|
+
* @param input - Bytes written to stdin, which is then closed
|
|
16
|
+
* @param options - Execution options
|
|
17
|
+
*/
|
|
18
|
+
export async function execWithInput(command, args, input, options = {}) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = spawn(command, args, {
|
|
21
|
+
cwd: options.cwd,
|
|
22
|
+
env: buildChildEnv(options),
|
|
23
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
24
|
+
});
|
|
25
|
+
const out = [];
|
|
26
|
+
const err = [];
|
|
27
|
+
child.stdout.on('data', (chunk) => {
|
|
28
|
+
out.push(chunk);
|
|
29
|
+
});
|
|
30
|
+
child.stderr.on('data', (chunk) => {
|
|
31
|
+
err.push(chunk);
|
|
32
|
+
});
|
|
33
|
+
// A child that exits before reading all of stdin (a bad ref, say) makes
|
|
34
|
+
// the write fail with EPIPE. The close handler reports the real outcome.
|
|
35
|
+
child.stdin.on('error', () => undefined);
|
|
36
|
+
child.stdin.end(input);
|
|
37
|
+
child.on('error', reject);
|
|
38
|
+
child.on('close', (code, signal) => {
|
|
39
|
+
resolve({
|
|
40
|
+
stdout: Buffer.concat(out),
|
|
41
|
+
stderr: Buffer.concat(err).toString('utf8'),
|
|
42
|
+
exitCode: exitCodeFromClose(code, signal),
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -104,6 +104,8 @@ export interface ProcessGroupOptions {
|
|
|
104
104
|
*/
|
|
105
105
|
postCloseSweep?: () => Promise<void>;
|
|
106
106
|
}
|
|
107
|
+
/** A child's exit code, with a signal death mapped to 128 + its number. */
|
|
108
|
+
export declare function exitCodeFromClose(code: number | null, signal: NodeJS.Signals | null): number;
|
|
107
109
|
/**
|
|
108
110
|
* Executes a command and returns its output.
|
|
109
111
|
* @param command - Command to execute
|
|
@@ -148,8 +150,9 @@ export declare function execStream(command: string, args: string[], options?: St
|
|
|
148
150
|
* Registers a child whose shutdown {@link waitForActiveChildShutdown} must
|
|
149
151
|
* wait for, and returns the handle that deregisters it.
|
|
150
152
|
*
|
|
151
|
-
* @returns `settle()`. Call
|
|
152
|
-
*
|
|
153
|
+
* @returns `settle()`. Call after child closure and required teardown finish.
|
|
154
|
+
* An abort's `error` event does not prove closure; do not settle there.
|
|
155
|
+
* A failed start may settle when no live child or teardown remains.
|
|
153
156
|
*/
|
|
154
157
|
export declare function trackChildClosure(): {
|
|
155
158
|
settle: () => void;
|