@ac-bench/lib 0.1.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.
Files changed (63) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +41 -0
  3. package/dist/child/channel.d.ts +28 -0
  4. package/dist/child/channel.d.ts.map +1 -0
  5. package/dist/child/channel.js +68 -0
  6. package/dist/child/channel.js.map +1 -0
  7. package/dist/child/event-sink.d.ts +13 -0
  8. package/dist/child/event-sink.d.ts.map +1 -0
  9. package/dist/child/event-sink.js +34 -0
  10. package/dist/child/event-sink.js.map +1 -0
  11. package/dist/child/main.d.ts +2 -0
  12. package/dist/child/main.d.ts.map +1 -0
  13. package/dist/child/main.js +157 -0
  14. package/dist/child/main.js.map +1 -0
  15. package/dist/cooldown.d.ts +51 -0
  16. package/dist/cooldown.d.ts.map +1 -0
  17. package/dist/cooldown.js +118 -0
  18. package/dist/cooldown.js.map +1 -0
  19. package/dist/current-environment.d.ts +8 -0
  20. package/dist/current-environment.d.ts.map +1 -0
  21. package/dist/current-environment.js +10 -0
  22. package/dist/current-environment.js.map +1 -0
  23. package/dist/environment-monitor.d.ts +59 -0
  24. package/dist/environment-monitor.d.ts.map +1 -0
  25. package/dist/environment-monitor.js +163 -0
  26. package/dist/environment-monitor.js.map +1 -0
  27. package/dist/escalation.d.ts +24 -0
  28. package/dist/escalation.d.ts.map +1 -0
  29. package/dist/escalation.js +73 -0
  30. package/dist/escalation.js.map +1 -0
  31. package/dist/fork-runner.d.ts +52 -0
  32. package/dist/fork-runner.d.ts.map +1 -0
  33. package/dist/fork-runner.js +348 -0
  34. package/dist/fork-runner.js.map +1 -0
  35. package/dist/index.d.ts +12 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +12 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/ipc-report-receiver.d.ts +47 -0
  40. package/dist/ipc-report-receiver.d.ts.map +1 -0
  41. package/dist/ipc-report-receiver.js +149 -0
  42. package/dist/ipc-report-receiver.js.map +1 -0
  43. package/dist/isolated-runner.d.ts +67 -0
  44. package/dist/isolated-runner.d.ts.map +1 -0
  45. package/dist/isolated-runner.js +537 -0
  46. package/dist/isolated-runner.js.map +1 -0
  47. package/dist/measure-diagnostic-proxy.d.ts +15 -0
  48. package/dist/measure-diagnostic-proxy.d.ts.map +1 -0
  49. package/dist/measure-diagnostic-proxy.js +50 -0
  50. package/dist/measure-diagnostic-proxy.js.map +1 -0
  51. package/dist/measurement-plan.d.ts +50 -0
  52. package/dist/measurement-plan.d.ts.map +1 -0
  53. package/dist/measurement-plan.js +2 -0
  54. package/dist/measurement-plan.js.map +1 -0
  55. package/dist/reporter-context.d.ts +19 -0
  56. package/dist/reporter-context.d.ts.map +1 -0
  57. package/dist/reporter-context.js +21 -0
  58. package/dist/reporter-context.js.map +1 -0
  59. package/dist/run-bench.d.ts +61 -0
  60. package/dist/run-bench.d.ts.map +1 -0
  61. package/dist/run-bench.js +151 -0
  62. package/dist/run-bench.js.map +1 -0
  63. package/package.json +79 -0
@@ -0,0 +1,59 @@
1
+ import { ReportDiagnostic } from "@ac-kit/app-report";
2
+ /** One observation of everything a child process does not isolate. */
3
+ export type EnvironmentSample = {
4
+ at: number;
5
+ /** 1-minute load average divided by CPU count. */
6
+ loadRatio: number;
7
+ /** Mean of `scaling_cur_freq / cpuinfo_max_freq` across cores, when readable. */
8
+ frequencyRatio: number | null;
9
+ /** Hottest thermal zone in °C, when readable. */
10
+ temperatureC: number | null;
11
+ /** The parent's own CPU time in this interval, as a fraction of it. */
12
+ parentCpuRatio: number;
13
+ /** Resident set of the measuring child. */
14
+ childRssBytes: number | null;
15
+ };
16
+ export type EnvironmentStability = "stable" | "drifting" | "unstable";
17
+ export type EnvironmentVerdict = {
18
+ stability: EnvironmentStability;
19
+ diagnostics: ReportDiagnostic[];
20
+ samples: EnvironmentSample[];
21
+ };
22
+ /**
23
+ * Classifies a scripted or collected series.
24
+ *
25
+ * A monotone ramp and an oscillation of the same amplitude look identical to a
26
+ * variance threshold and mean completely different things, so the two tests are
27
+ * separated: `mannKendallTrend` answers "is it going somewhere" and
28
+ * `reversalRate` answers "does it keep changing its mind".
29
+ */
30
+ export declare function classifyEnvironment(samples: readonly EnvironmentSample[]): EnvironmentVerdict;
31
+ export type EnvironmentProbe = () => Promise<EnvironmentSample>;
32
+ export type EnvironmentMonitorOptions = {
33
+ readonly intervalMs?: number;
34
+ };
35
+ /**
36
+ * Samples the machine while a child measures, and judges what it saw.
37
+ *
38
+ * The parent is idle during a measurement, which makes it the only process in a
39
+ * position to watch the things a fork cannot isolate.
40
+ */
41
+ export declare class EnvironmentMonitor {
42
+ private readonly probe;
43
+ private readonly intervalMs;
44
+ private samples;
45
+ private controller;
46
+ private loop;
47
+ constructor(probe: EnvironmentProbe, options?: EnvironmentMonitorOptions);
48
+ start(): void;
49
+ /** Stops sampling and classifies everything collected since `start()`. */
50
+ stop(): Promise<EnvironmentVerdict>;
51
+ private collect;
52
+ }
53
+ export type NodeEnvironmentProbeOptions = {
54
+ /** Resolved per sample, because the child outlives no single one of them. */
55
+ readonly pidOf?: () => number | undefined;
56
+ };
57
+ /** Reads the host metrics this platform exposes; unreadable ones are `null`. */
58
+ export declare function nodeEnvironmentProbe(options?: NodeEnvironmentProbeOptions): EnvironmentProbe;
59
+ //# sourceMappingURL=environment-monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-monitor.d.ts","sourceRoot":"","sources":["../src/environment-monitor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAQtD,sEAAsE;AACtE,MAAM,MAAM,iBAAiB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iDAAiD;IACjD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC;IACvB,2CAA2C;IAC3C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG;IAChC,SAAS,EAAE,oBAAoB,CAAC;IAChC,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC7B,CAAC;AAiBF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACnC,kBAAkB,CAkEpB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEhE,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAIF;;;;;GAKG;AACH,qBAAa,kBAAkB;IAO7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IANvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,IAAI,CAA8B;IAE1C,YACkB,KAAK,EAAE,gBAAgB,EACxC,OAAO,CAAC,EAAE,yBAAyB,EAGnC;IAED,KAAK,IAAI,IAAI,CAQZ;IAED,0EAA0E;IACpE,IAAI,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAQxC;YAEa,OAAO;CAWrB;AAED,MAAM,MAAM,2BAA2B,GAAG;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,oBAAoB,CACnC,OAAO,CAAC,EAAE,2BAA2B,GACnC,gBAAgB,CA6BlB"}
@@ -0,0 +1,163 @@
1
+ import os from "node:os";
2
+ import { setTimeout as delay } from "node:timers/promises";
3
+ import { readCpuFrequencyRatio, readMaxTemperatureC, readProcessRssBytes, } from "@ac-kit/app-system";
4
+ import { mannKendallTrend, reversalRate } from "@ac-kit/math-stats";
5
+ /** Two-sided 5% normal deviate: the trend is real rather than sampling noise. */
6
+ const TREND_SIGNIFICANT_ = 1.96;
7
+ /** Above this fraction of reversals a series is oscillating, not drifting. */
8
+ const REVERSALS_UNSTABLE_ = 0.4;
9
+ /** Below this fraction a series is monotone enough to call a drift. */
10
+ const REVERSALS_DRIFTING_ = 0.25;
11
+ /** A core dropping this far below the run's own best is throttling. */
12
+ const FREQUENCY_FLOOR_ = 0.9;
13
+ /** Load per core above which the machine is contended. */
14
+ const LOAD_CONTENDED_ = 0.7;
15
+ /**
16
+ * Classifies a scripted or collected series.
17
+ *
18
+ * A monotone ramp and an oscillation of the same amplitude look identical to a
19
+ * variance threshold and mean completely different things, so the two tests are
20
+ * separated: `mannKendallTrend` answers "is it going somewhere" and
21
+ * `reversalRate` answers "does it keep changing its mind".
22
+ */
23
+ export function classifyEnvironment(samples) {
24
+ const metrics = [
25
+ {
26
+ name: "load",
27
+ values: samples.map((sample) => sample.loadRatio),
28
+ unstable: (values) => Math.min(...values) < LOAD_CONTENDED_ &&
29
+ Math.max(...values) > LOAD_CONTENDED_,
30
+ },
31
+ {
32
+ name: "frequency",
33
+ values: definedValues_(samples, (sample) => sample.frequencyRatio),
34
+ unstable: (values) => Math.min(...values) < FREQUENCY_FLOOR_ * Math.max(...values),
35
+ },
36
+ {
37
+ name: "temperature",
38
+ values: definedValues_(samples, (sample) => sample.temperatureC),
39
+ // Temperature climbs and does not oscillate; only its drift is meaningful.
40
+ unstable: () => false,
41
+ },
42
+ ];
43
+ const diagnostics = [];
44
+ let stability = "stable";
45
+ for (const metric of metrics) {
46
+ if (metric.values.length < 3) {
47
+ continue;
48
+ }
49
+ const reversals = reversalRate(metric.values);
50
+ const { normalised } = mannKendallTrend(metric.values);
51
+ const low = Math.min(...metric.values);
52
+ const high = Math.max(...metric.values);
53
+ if (reversals >= REVERSALS_UNSTABLE_ && metric.unstable(metric.values)) {
54
+ stability = "unstable";
55
+ diagnostics.push({
56
+ severity: "warning",
57
+ code: "environment-unstable",
58
+ message: `${metric.name} oscillated between ${format_(low)} and ${format_(high)} during this measurement; the numbers are contended, not merely shifted`,
59
+ attributes: { metric: metric.name, low, high, reversals },
60
+ });
61
+ continue;
62
+ }
63
+ if (Math.abs(normalised) >= TREND_SIGNIFICANT_ &&
64
+ reversals <= REVERSALS_DRIFTING_) {
65
+ if (stability === "stable") {
66
+ stability = "drifting";
67
+ }
68
+ diagnostics.push({
69
+ severity: "info",
70
+ code: "environment-drift",
71
+ message: `${metric.name} drifted ${normalised > 0 ? "up" : "down"} from ${format_(metric.values[0] ?? 0)} to ${format_(metric.values[metric.values.length - 1] ?? 0)} during this measurement`,
72
+ attributes: { metric: metric.name, low, high, normalised },
73
+ });
74
+ }
75
+ }
76
+ // A clean run stays quiet.
77
+ return { stability, diagnostics, samples: [...samples] };
78
+ }
79
+ const DEFAULT_INTERVAL_MS_ = 1000;
80
+ /**
81
+ * Samples the machine while a child measures, and judges what it saw.
82
+ *
83
+ * The parent is idle during a measurement, which makes it the only process in a
84
+ * position to watch the things a fork cannot isolate.
85
+ */
86
+ export class EnvironmentMonitor {
87
+ probe;
88
+ intervalMs;
89
+ samples = [];
90
+ controller = null;
91
+ loop = null;
92
+ constructor(probe, options) {
93
+ this.probe = probe;
94
+ this.intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS_;
95
+ }
96
+ start() {
97
+ if (this.controller !== null) {
98
+ return;
99
+ }
100
+ this.samples = [];
101
+ this.controller = new AbortController();
102
+ this.loop = this.collect(this.controller.signal);
103
+ }
104
+ /** Stops sampling and classifies everything collected since `start()`. */
105
+ async stop() {
106
+ this.controller?.abort();
107
+ await this.loop;
108
+ this.controller = null;
109
+ this.loop = null;
110
+ return classifyEnvironment(this.samples);
111
+ }
112
+ async collect(signal) {
113
+ while (!signal.aborted) {
114
+ try {
115
+ this.samples.push(await this.probe());
116
+ await delay(this.intervalMs, undefined, { signal });
117
+ }
118
+ catch {
119
+ // An unreadable metric or an aborted wait ends the watch, never the run.
120
+ return;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ /** Reads the host metrics this platform exposes; unreadable ones are `null`. */
126
+ export function nodeEnvironmentProbe(options) {
127
+ const cpuCount = Math.max(1, os.cpus().length);
128
+ let lastCpu = process.cpuUsage();
129
+ let lastAt = Date.now();
130
+ return async () => {
131
+ const at = Date.now();
132
+ const cpu = process.cpuUsage();
133
+ const elapsedUs = Math.max(1, (at - lastAt) * 1000);
134
+ const usedUs = cpu.user - lastCpu.user + (cpu.system - lastCpu.system);
135
+ lastCpu = cpu;
136
+ lastAt = at;
137
+ const [frequencyRatio, temperatureC, childRssBytes] = await Promise.all([
138
+ readCpuFrequencyRatio(),
139
+ readMaxTemperatureC(),
140
+ readChildRss_(options?.pidOf?.()),
141
+ ]);
142
+ return {
143
+ at,
144
+ loadRatio: (os.loadavg()[0] ?? 0) / cpuCount,
145
+ frequencyRatio,
146
+ temperatureC,
147
+ parentCpuRatio: usedUs / elapsedUs,
148
+ childRssBytes,
149
+ };
150
+ };
151
+ }
152
+ function readChildRss_(pid) {
153
+ return pid === undefined ? Promise.resolve(null) : readProcessRssBytes(pid);
154
+ }
155
+ function definedValues_(samples, pick) {
156
+ return samples
157
+ .map((sample) => pick(sample))
158
+ .filter((value) => value !== null);
159
+ }
160
+ function format_(value) {
161
+ return value.toFixed(2);
162
+ }
163
+ //# sourceMappingURL=environment-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-monitor.js","sourceRoot":"","sources":["../src/environment-monitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { MeasureComparison } from "@ac-bench/core/plugin";
2
+ import { type DataFrame } from "@ac-kit/model-dataset";
3
+ /**
4
+ * Whether the measurements taken so far settle the comparison.
5
+ *
6
+ * A benchmark's job is to order its cases. Two cases whose intervals overlap
7
+ * are not ordered by the numbers: whichever came out ahead did so within the
8
+ * noise, and another process could reverse it. That is the only signal worth
9
+ * spending more machine time on, and it is measure-agnostic — the parent reads
10
+ * it off the frame and the comparison every plugin already declares, rather
11
+ * than knowing what any measure's numbers mean.
12
+ *
13
+ * Cases without an interval are ignored: an unknown interval is not an
14
+ * overlapping one, and treating it as such would escalate forever.
15
+ *
16
+ * O(n log n) in the case count, for the sort.
17
+ *
18
+ * @param frame The condition's frame, from `MeasurePlugin.conditionFrame`.
19
+ * @param comparison What the measure says orders its cases. A measure with no
20
+ * interval bounds makes no claim tight enough to escalate on.
21
+ * @returns `true` when some adjacent pair cannot be told apart.
22
+ */
23
+ export declare function comparisonUnresolved(frame: DataFrame, comparison: MeasureComparison | undefined): boolean;
24
+ //# sourceMappingURL=escalation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escalation.d.ts","sourceRoot":"","sources":["../src/escalation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAGN,KAAK,SAAS,EACd,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,iBAAiB,GAAG,SAAS,GACvC,OAAO,CA2DT"}
@@ -0,0 +1,73 @@
1
+ import { createComparatorBy } from "@ac-kit/core";
2
+ import { columnCell, findDataFrameFieldIndexByName, } from "@ac-kit/model-dataset";
3
+ /**
4
+ * Whether the measurements taken so far settle the comparison.
5
+ *
6
+ * A benchmark's job is to order its cases. Two cases whose intervals overlap
7
+ * are not ordered by the numbers: whichever came out ahead did so within the
8
+ * noise, and another process could reverse it. That is the only signal worth
9
+ * spending more machine time on, and it is measure-agnostic — the parent reads
10
+ * it off the frame and the comparison every plugin already declares, rather
11
+ * than knowing what any measure's numbers mean.
12
+ *
13
+ * Cases without an interval are ignored: an unknown interval is not an
14
+ * overlapping one, and treating it as such would escalate forever.
15
+ *
16
+ * O(n log n) in the case count, for the sort.
17
+ *
18
+ * @param frame The condition's frame, from `MeasurePlugin.conditionFrame`.
19
+ * @param comparison What the measure says orders its cases. A measure with no
20
+ * interval bounds makes no claim tight enough to escalate on.
21
+ * @returns `true` when some adjacent pair cannot be told apart.
22
+ */
23
+ export function comparisonUnresolved(frame, comparison) {
24
+ if (comparison?.interval === undefined) {
25
+ return false;
26
+ }
27
+ const intervals = [];
28
+ if (Array.isArray(comparison.interval)) {
29
+ const [lowerField, upperField] = comparison.interval;
30
+ const lowerIndex = findDataFrameFieldIndexByName(frame, lowerField);
31
+ const upperIndex = findDataFrameFieldIndexByName(frame, upperField);
32
+ const lowerColumn = frame.columns[lowerIndex];
33
+ const upperColumn = frame.columns[upperIndex];
34
+ if (lowerColumn === undefined || upperColumn === undefined) {
35
+ return false;
36
+ }
37
+ for (let rowIndex = 0; rowIndex < frame.rowCount; rowIndex++) {
38
+ const lower = columnCell(lowerColumn, rowIndex);
39
+ const upper = columnCell(upperColumn, rowIndex);
40
+ if (typeof lower === "number" && typeof upper === "number") {
41
+ intervals.push({ lower, upper });
42
+ }
43
+ }
44
+ }
45
+ else if (typeof comparison.interval === "string") {
46
+ const halfWidthField = comparison.interval;
47
+ const fieldIndex = findDataFrameFieldIndexByName(frame, halfWidthField);
48
+ const column = frame.columns[fieldIndex];
49
+ if (column === undefined) {
50
+ return false;
51
+ }
52
+ for (let rowIndex = 0; rowIndex < frame.rowCount; rowIndex++) {
53
+ const halfWidth = columnCell(column, rowIndex);
54
+ if (typeof halfWidth === "number") {
55
+ intervals.push({ lower: halfWidth, upper: halfWidth });
56
+ }
57
+ }
58
+ }
59
+ // One case is not a comparison, and neither is none.
60
+ if (intervals.length < 2) {
61
+ return false;
62
+ }
63
+ intervals.sort(createComparatorBy((v) => v.lower));
64
+ for (let index = 1; index < intervals.length; index++) {
65
+ const previous = intervals[index - 1];
66
+ const current = intervals[index];
67
+ if (current.lower <= previous.upper) {
68
+ return true;
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+ //# sourceMappingURL=escalation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escalation.js","sourceRoot":"","sources":["../src/escalation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { DiscoveredCondition, MeasureData, StartMessage } from "@ac-bench/core/runner";
2
+ import { ISink, ReportDiagnostic, ReportScopeStatus } from "@ac-kit/app-report";
3
+ /** No message from a child for this long means it is no longer making progress. */
4
+ export declare const DEFAULT_HEARTBEAT_TIMEOUT_MS = 30000;
5
+ /** A child stopped producing messages without having finished. */
6
+ export declare class ChildTimeoutError extends Error {
7
+ readonly timeoutMs: number;
8
+ constructor(timeoutMs: number);
9
+ }
10
+ /** A fork unit outlived its wall-clock budget. */
11
+ export declare class ConditionTimeoutError extends Error {
12
+ readonly timeoutMs: number;
13
+ constructor(timeoutMs: number);
14
+ }
15
+ /** A child exited before reporting that it was done. */
16
+ export declare class ChildExitedError extends Error {
17
+ readonly code: number | null;
18
+ readonly signal: NodeJS.Signals | null;
19
+ constructor(code: number | null, signal: NodeJS.Signals | null, options?: ErrorOptions);
20
+ }
21
+ export type ForkOptions = {
22
+ readonly runId: string;
23
+ readonly artifactCacheRoot: string;
24
+ readonly signal: AbortSignal;
25
+ readonly heartbeatTimeoutMs?: number;
26
+ /** Wall-clock cap on one fork unit. `0` (the default) disables it. */
27
+ readonly conditionTimeoutMs?: number;
28
+ /** Imported by the child before any bench file, in order. */
29
+ readonly setupFiles?: readonly string[];
30
+ readonly execArgv?: readonly string[];
31
+ readonly env?: Readonly<Record<string, string>>;
32
+ /** Called once the child exists, and again with `undefined` once it is gone. */
33
+ readonly onChildPid?: (pid: number | undefined) => void;
34
+ };
35
+ export type ChildOutcome = {
36
+ status: ReportScopeStatus;
37
+ error: unknown;
38
+ };
39
+ /**
40
+ * Forks a discovery child for `file` and returns the condition tree it
41
+ * registered, without executing a single case.
42
+ */
43
+ export declare function discoverConditionsInFile(file: string, options: ForkOptions): Promise<DiscoveredCondition[]>;
44
+ /**
45
+ * Forks a child, runs one fork unit in it, and replays everything it reports
46
+ * into `sink` in order.
47
+ *
48
+ * The child's own scopes are closed out here if it dies without saying it was
49
+ * done, so a crash can never strand a scope.
50
+ */
51
+ export declare function runForkUnitInChild(start: Omit<StartMessage, "t">, sink: ISink<MeasureData>, onDiagnostic: (diagnostic: ReportDiagnostic) => void, options: ForkOptions): Promise<ChildOutcome>;
52
+ //# sourceMappingURL=fork-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fork-runner.d.ts","sourceRoot":"","sources":["../src/fork-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,mBAAmB,EACnB,WAAW,EAGX,YAAY,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAOhF,mFAAmF;AACnF,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAInD,kEAAkE;AAClE,qBAAa,iBAAkB,SAAQ,KAAK;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM;IAAtC,YAAqB,SAAS,EAAE,MAAM,EAGrC;CACD;AAED,kDAAkD;AAClD,qBAAa,qBAAsB,SAAQ,KAAK;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM;IAAtC,YAAqB,SAAS,EAAE,MAAM,EAGrC;CACD;AAED,wDAAwD;AACxD,qBAAa,gBAAiB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI;IAFvC,YACU,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,EACtC,OAAO,CAAC,EAAE,YAAY,EAStB;CACD;AAED,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,sEAAsE;IACtE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACxD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,wBAAsB,wBAAwB,CAC7C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,GAClB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAwBhC;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACvC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,EAC9B,IAAI,EAAE,KAAK,CAAC,WAAW,CAAC,EACxB,YAAY,EAAE,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EACpD,OAAO,EAAE,WAAW,GAClB,OAAO,CAAC,YAAY,CAAC,CA8FvB"}