You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@flakiness/sdk

Package Overview
Dependencies
Maintainers
1
Versions
90
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@flakiness/sdk - npm Package Compare versions

Comparing version
0.151.0
to
0.152.0
+7
types/src/_telemetry.d.ts
export type TelemetryPoint = {
timestamp: number;
value: number;
};
export declare function addTelemetryPoint(collection: TelemetryPoint[], newPoint: TelemetryPoint, precision: number): void;
export declare function toProtocolTelemetry(collection: TelemetryPoint[]): [number, number][];
//# sourceMappingURL=_telemetry.d.ts.map
{"version":3,"file":"_telemetry.d.ts","sourceRoot":"","sources":["../../src/_telemetry.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAY1G;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CASpF"}
import { FlakinessReport as FK } from '@flakiness/flakiness-report';
import { GitWorktree } from './gitWorktree.js';
/**
* Extracts source code snippets referenced by locations in the report.
*
* Scans all locations in the report (tests, steps, errors, annotations) and collects
* the relevant source code chunks with surrounding context (±5 lines). The collected
* sources are stored directly in `report.sources`.
*
* @param worktree - Git worktree for resolving file paths.
* @param report - Report to scan and enrich with source snippets.
*/
export declare function collectSources(worktree: GitWorktree, report: FK.Report): void;
//# sourceMappingURL=collectSources.d.ts.map
{"version":3,"file":"collectSources.d.ts","sourceRoot":"","sources":["../../src/collectSources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,EAAE,EAAE,MAAM,6BAA6B,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAqD/C;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,QAiCtE"}
import { FlakinessReport as FK } from "@flakiness/flakiness-report";
/**
* Tracks CPU utilization over time by recording periodic samples.
*
* Call `sample()` at desired intervals (e.g., test start/end, every second) to record
* CPU usage. The class tracks both average and max utilization across all CPU cores.
* Use `enrich()` to add the collected telemetry to a report.
*/
export declare class CPUUtilization {
private _lastSample;
private _precision;
private _cpuAvg;
private _cpuMax;
/**
* @param options.precision - Minimum change in percentage points to record a new data point. Defaults to 7.
*/
constructor(options?: {
precision?: number;
});
/**
* Records the current CPU utilization since the last sample.
* The first call primes the baseline; subsequent calls record deltas.
*/
sample(): void;
/**
* Adds collected CPU telemetry to the report.
*/
enrich(report: FK.Report): void;
}
//# sourceMappingURL=cpuUtilization.d.ts.map
{"version":3,"file":"cpuUtilization.d.ts","sourceRoot":"","sources":["../../src/cpuUtilization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,EAAE,EAAE,MAAM,6BAA6B,CAAC;AAkBpE;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAgB;IAEnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAAwB;IAEvC;;OAEG;gBACS,OAAO,CAAC,EAAE;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;IAID;;;OAGG;IACH,MAAM;IAwBN;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM;CAKzB"}
import { FlakinessReport as FK } from '@flakiness/flakiness-report';
/**
* Tracks RAM utilization over time by recording periodic samples.
*
* Call `sample()` at desired intervals (e.g., test start/end, every second) to record
* memory usage as a percentage of total system RAM. Use `enrich()` to add the collected
* telemetry to a report.
*/
export declare class RAMUtilization {
private _precision;
private _totalBytes;
private _ram;
/**
* @param options.precision - Minimum change in percentage points to record a new data point. Defaults to 1.
*/
constructor(options?: {
precision?: number;
});
/**
* Records the current RAM utilization.
*/
sample(): void;
/**
* Adds collected RAM telemetry to the report.
*/
enrich(report: FK.Report): void;
}
//# sourceMappingURL=ramUtilization.d.ts.map
{"version":3,"file":"ramUtilization.d.ts","sourceRoot":"","sources":["../../src/ramUtilization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,EAAE,EAAE,MAAM,6BAA6B,CAAC;AA4BpE;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAiB;IAEpC,OAAO,CAAC,IAAI,CAAwB;IAEpC;;OAEG;gBACS,OAAO,CAAC,EAAE;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;IAID;;OAEG;IACH,MAAM;IAQN;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM;CAIzB"}
+52
-5

@@ -32,2 +32,43 @@ var __defProp = Object.defineProperty;

function normalizeReport(report) {
report = deduplicateTestsSuitesEnvironments(report);
function cleanupTestStep(step) {
return {
...step,
duration: step.duration === 0 ? void 0 : step.duration,
steps: step.steps && step.steps.length ? step.steps.map(cleanupTestStep) : void 0
};
}
function cleanupAttempt(attempt) {
return {
...attempt,
status: attempt.status === "passed" ? void 0 : attempt.status,
expectedStatus: attempt.expectedStatus === "passed" ? void 0 : attempt.expectedStatus,
environmentIdx: attempt.environmentIdx === 0 ? void 0 : attempt.environmentIdx,
duration: attempt.duration === 0 ? void 0 : attempt.duration,
stdout: attempt.stdout && attempt.stdout.length ? attempt.stdout : void 0,
stderr: attempt.stderr && attempt.stderr.length ? attempt.stderr : void 0,
attachments: attempt.attachments && attempt.attachments.length ? attempt.attachments : void 0,
steps: attempt.steps && attempt.steps.length ? attempt.steps.map(cleanupTestStep) : void 0
};
}
function cleanupTest(test) {
return {
...test,
attempts: test.attempts.map(cleanupAttempt)
};
}
function cleanupSuite(suite) {
return {
...suite,
tests: suite.tests && suite.tests.length ? suite.tests.map(cleanupTest) : void 0,
suites: suite.suites && suite.suites.length ? suite.suites.map(cleanupSuite) : void 0
};
}
return {
...report,
tests: report.tests && report.tests.length ? report.tests.map(cleanupTest) : void 0,
suites: report.suites && report.suites.length ? report.suites.map(cleanupSuite) : void 0
};
}
function deduplicateTestsSuitesEnvironments(report) {
const gEnvs = /* @__PURE__ */ new Map();

@@ -54,5 +95,11 @@ const gSuites = /* @__PURE__ */ new Map();

for (const attempt of test.attempts) {
const env = report.environments[attempt.environmentIdx];
const env = report.environments[attempt.environmentIdx ?? 0];
const envId = gEnvIds.get(env);
usedEnvIds.add(envId);
if (attempt.annotations && !attempt.annotations.length)
delete attempt.annotations;
if (attempt.stdout && !attempt.stdout.length)
delete attempt.stdout;
if (attempt.stderr && !attempt.stderr.length)
delete attempt.stderr;
}

@@ -82,3 +129,3 @@ }

...attempt,
environmentIdx: envIdToIndex.get(gEnvIds.get(report.environments[attempt.environmentIdx]))
environmentIdx: envIdToIndex.get(gEnvIds.get(report.environments[attempt.environmentIdx ?? 0]))
}))

@@ -102,3 +149,3 @@ };

visitTests2(report.tests ?? [], "suiteless");
for (const suite of report.suites)
for (const suite of report.suites ?? [])
visitSuite(suite);

@@ -110,3 +157,3 @@ const newEnvironments = [...usedEnvIds];

environments: newEnvironments.map((envId) => gEnvs.get(envId)),
suites: transformSuites(report.suites),
suites: transformSuites(report.suites ?? []),
tests: transformTests(report.tests ?? [])

@@ -152,3 +199,3 @@ };

testVisitor(test, []);
for (const suite of report.suites)
for (const suite of report.suites ?? [])
visitSuite(suite, []);

@@ -155,0 +202,0 @@ }

+285
-162

@@ -49,2 +49,82 @@ var __defProp = Object.defineProperty;

// src/cpuUtilization.ts
import os from "os";
// src/_telemetry.ts
function addTelemetryPoint(collection, newPoint, precision) {
const lastPoint = collection.at(-1);
const preLastPoint = collection.at(-2);
if (lastPoint && preLastPoint && Math.abs(lastPoint.value - preLastPoint.value) < precision && Math.abs(lastPoint.value - newPoint.value) < precision) {
lastPoint.timestamp = newPoint.timestamp;
} else {
collection.push(newPoint);
}
}
function toProtocolTelemetry(collection) {
if (!collection.length)
return [];
let lastTimestamp = collection[0].timestamp;
return collection.map((x, idx) => {
const dts = idx === 0 ? x.timestamp : x.timestamp - lastTimestamp;
lastTimestamp = x.timestamp;
return [dts, Math.round(x.value * 100) / 100];
});
}
// src/cpuUtilization.ts
function sampleCpus() {
return os.cpus().map((cpu) => {
const totalTicks = cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq + cpu.times.idle;
const idleTicks = cpu.times.idle;
const busyTicks = totalTicks - idleTicks;
return { totalTicks, busyTicks };
});
}
var CPUUtilization = class {
_lastSample = sampleCpus();
_precision;
_cpuAvg = [];
_cpuMax = [];
/**
* @param options.precision - Minimum change in percentage points to record a new data point. Defaults to 7.
*/
constructor(options) {
this._precision = options?.precision ?? 7;
}
/**
* Records the current CPU utilization since the last sample.
* The first call primes the baseline; subsequent calls record deltas.
*/
sample() {
const newSample = sampleCpus();
if (newSample.length === this._lastSample.length) {
const utilization = newSample.map(
(cpu, idx) => (
// If the CPU did no work since the last sample, then it's
// utilization is effectively 0.
cpu.totalTicks === this._lastSample[idx].totalTicks ? 0 : (cpu.busyTicks - this._lastSample[idx].busyTicks) / (cpu.totalTicks - this._lastSample[idx].totalTicks) * 100
)
);
const timestamp = Date.now();
addTelemetryPoint(this._cpuAvg, {
timestamp,
value: utilization.reduce((acc, x) => acc + x) / utilization.length
}, this._precision);
addTelemetryPoint(this._cpuMax, {
timestamp,
value: Math.max(...utilization)
}, this._precision);
}
this._lastSample = newSample;
}
/**
* Adds collected CPU telemetry to the report.
*/
enrich(report) {
report.cpuCount = os.cpus().length;
report.cpuMax = toProtocolTelemetry(this._cpuMax);
report.cpuAvg = toProtocolTelemetry(this._cpuAvg);
}
};
// src/gitWorktree.ts

@@ -339,9 +419,58 @@ import assert from "assert";

// src/ramUtilization.ts
import { spawnSync as spawnSync2 } from "child_process";
import os2 from "os";
function getAvailableMemMacOS() {
const lines = spawnSync2("vm_stat", { encoding: "utf8" }).stdout.trim().split("\n");
const pageSize = parseInt(lines[0].match(/page size of (\d+) bytes/)[1], 10);
if (isNaN(pageSize)) {
console.warn("[flakiness.io] Error detecting macos page size");
return 0;
}
let totalFree = 0;
for (const line of lines) {
if (/Pages (free|inactive|speculative):/.test(line)) {
const match = line.match(/\d+/);
if (match)
totalFree += parseInt(match[0], 10);
}
}
return totalFree * pageSize;
}
var RAMUtilization = class {
_precision;
_totalBytes = os2.totalmem();
_ram = [];
/**
* @param options.precision - Minimum change in percentage points to record a new data point. Defaults to 1.
*/
constructor(options) {
this._precision = options?.precision ?? 1;
}
/**
* Records the current RAM utilization.
*/
sample() {
const freeBytes = os2.platform() === "darwin" ? getAvailableMemMacOS() : os2.freemem();
addTelemetryPoint(this._ram, {
timestamp: Date.now(),
value: (this._totalBytes - freeBytes) / this._totalBytes * 100
}, this._precision);
}
/**
* Adds collected RAM telemetry to the report.
*/
enrich(report) {
report.ramBytes = this._totalBytes;
report.ram = toProtocolTelemetry(this._ram);
}
};
// src/reportUtils.ts
var reportUtils_exports = {};
__export(reportUtils_exports, {
collectSources: () => collectSources,
createDataAttachment: () => createDataAttachment,
createEnvironment: () => createEnvironment,
createFileAttachment: () => createFileAttachment,
createTestStepSnippetsInplace: () => createTestStepSnippetsInplace,
normalizeReport: () => normalizeReport,

@@ -352,7 +481,89 @@ stripAnsi: () => stripAnsi,

// src/collectSources.ts
import fs2 from "fs";
function collectLocationsFromTestStep(testStep, onLocation) {
onLocation(testStep.location);
for (const step of testStep.steps ?? [])
collectLocationsFromTestStep(step, onLocation);
}
function collectLocationsFromTest(test, onLocation) {
onLocation(test.location);
for (const attempt of test.attempts) {
for (const annotation of attempt.annotations ?? [])
onLocation(annotation.location);
for (const err of attempt.errors ?? [])
onLocation(err.location);
for (const step of attempt.steps ?? [])
collectLocationsFromTestStep(step, onLocation);
}
}
function collectLocationsFromSuite(suite, onLocation) {
onLocation(suite.location);
for (const child of suite.suites ?? [])
collectLocationsFromSuite(child, onLocation);
for (const test of suite.tests ?? [])
collectLocationsFromTest(test, onLocation);
}
function collectLocationsFromReport(report, onLocation) {
for (const e of report.unattributedErrors ?? [])
onLocation(e.location);
for (const test of report.tests ?? [])
collectLocationsFromTest(test, onLocation);
for (const suite of report.suites ?? [])
collectLocationsFromSuite(suite, onLocation);
}
function lineNumbersToChunks(lineNumbers, options) {
const context = options.context;
const result = [];
let current;
for (const ln of Array.from(lineNumbers).sort((a, b) => a - b)) {
const span = [ln - context, ln + context];
if (!current || current[1] + 1 < span[0]) {
result.push(span);
current = span;
} else {
current[1] = span[1];
}
}
return result;
}
function collectSources(worktree, report) {
const filesToLines = /* @__PURE__ */ new Map();
collectLocationsFromReport(report, (location) => {
if (!location)
return;
let lineNumbers = filesToLines.get(location.file);
if (!lineNumbers) {
lineNumbers = /* @__PURE__ */ new Set();
filesToLines.set(location.file, lineNumbers);
}
lineNumbers.add(location.line);
});
const sources = [];
for (const [gitFilePath, lineNumbers] of filesToLines) {
let source;
try {
source = fs2.readFileSync(worktree.absolutePath(gitFilePath), "utf-8");
} catch (e) {
continue;
}
const sourceLines = source.split("\n");
for (const chunk of lineNumbersToChunks(lineNumbers, { context: 5 })) {
const from = Math.max(chunk[0] - 1, 0);
const to = Math.min(chunk[1], sourceLines.length);
sources.push({
filePath: gitFilePath,
lineOffset: from !== 0 ? from + 1 : void 0,
text: sourceLines.slice(from, to).join("\n")
});
}
}
report.sources = sources;
}
// src/createEnvironment.ts
import fs2 from "fs";
import os from "os";
import fs3 from "fs";
import os3 from "os";
function readLinuxOSRelease() {
const osReleaseText = fs2.readFileSync("/etc/os-release", "utf-8");
const osReleaseText = fs3.readFileSync("/etc/os-release", "utf-8");
return new Map(osReleaseText.toLowerCase().split("\n").filter((line) => line.includes("=")).map((line) => {

@@ -382,3 +593,3 @@ line = line.trim();

const arch = process.arch;
const version = os.release();
const version = os3.release();
return { name, arch, version };

@@ -408,72 +619,9 @@ }

},
userSuppliedData: {
metadata: {
...extractEnvConfiguration(),
...options.userSuppliedData ?? {}
},
opaqueData: options.opaqueData
...options.metadata ?? {}
}
};
}
// src/createTestStepSnippets.ts
import { codeFrameColumns } from "@babel/code-frame";
import fs3 from "fs";
// src/visitTests.ts
function visitTests(report, testVisitor) {
function visitSuite(suite, parents) {
parents.push(suite);
for (const test of suite.tests ?? [])
testVisitor(test, parents);
for (const childSuite of suite.suites ?? [])
visitSuite(childSuite, parents);
parents.pop();
}
for (const test of report.tests ?? [])
testVisitor(test, []);
for (const suite of report.suites)
visitSuite(suite, []);
}
// src/createTestStepSnippets.ts
function createTestStepSnippetsInplace(worktree, report) {
const allSteps = /* @__PURE__ */ new Map();
visitTests(report, (test) => {
for (const attempt of test.attempts) {
for (const step of attempt.steps ?? []) {
if (!step.location)
continue;
let fileSteps = allSteps.get(step.location.file);
if (!fileSteps) {
fileSteps = /* @__PURE__ */ new Set();
allSteps.set(step.location.file, fileSteps);
}
fileSteps.add(step);
}
}
});
for (const [gitFilePath, steps] of allSteps) {
let source;
try {
source = fs3.readFileSync(worktree.absolutePath(gitFilePath), "utf-8");
} catch (e) {
continue;
}
const lines = source.split("\n").length;
const highlighted = codeFrameColumns(source, { start: { line: lines, column: 1 } }, { highlightCode: true, linesAbove: lines, linesBelow: 0 });
const highlightedLines = highlighted.split("\n");
const lineWithArrow = highlightedLines[highlightedLines.length - 1];
for (const step of steps) {
if (!step.location)
continue;
if (step.location.line < 2 || step.location.line >= lines)
continue;
const snippetLines = highlightedLines.slice(step.location.line - 2, step.location.line + 1);
const index = lineWithArrow.indexOf("^");
const shiftedArrow = lineWithArrow.slice(0, index) + " ".repeat(step.location.column - 1) + lineWithArrow.slice(index);
snippetLines.splice(2, 0, shiftedArrow);
step.snippet = snippetLines.join("\n");
}
}
}
// src/normalizeReport.ts

@@ -493,2 +641,43 @@ import stableObjectHash from "stable-hash";

function normalizeReport(report) {
report = deduplicateTestsSuitesEnvironments(report);
function cleanupTestStep(step) {
return {
...step,
duration: step.duration === 0 ? void 0 : step.duration,
steps: step.steps && step.steps.length ? step.steps.map(cleanupTestStep) : void 0
};
}
function cleanupAttempt(attempt) {
return {
...attempt,
status: attempt.status === "passed" ? void 0 : attempt.status,
expectedStatus: attempt.expectedStatus === "passed" ? void 0 : attempt.expectedStatus,
environmentIdx: attempt.environmentIdx === 0 ? void 0 : attempt.environmentIdx,
duration: attempt.duration === 0 ? void 0 : attempt.duration,
stdout: attempt.stdout && attempt.stdout.length ? attempt.stdout : void 0,
stderr: attempt.stderr && attempt.stderr.length ? attempt.stderr : void 0,
attachments: attempt.attachments && attempt.attachments.length ? attempt.attachments : void 0,
steps: attempt.steps && attempt.steps.length ? attempt.steps.map(cleanupTestStep) : void 0
};
}
function cleanupTest(test) {
return {
...test,
attempts: test.attempts.map(cleanupAttempt)
};
}
function cleanupSuite(suite) {
return {
...suite,
tests: suite.tests && suite.tests.length ? suite.tests.map(cleanupTest) : void 0,
suites: suite.suites && suite.suites.length ? suite.suites.map(cleanupSuite) : void 0
};
}
return {
...report,
tests: report.tests && report.tests.length ? report.tests.map(cleanupTest) : void 0,
suites: report.suites && report.suites.length ? report.suites.map(cleanupSuite) : void 0
};
}
function deduplicateTestsSuitesEnvironments(report) {
const gEnvs = /* @__PURE__ */ new Map();

@@ -515,5 +704,11 @@ const gSuites = /* @__PURE__ */ new Map();

for (const attempt of test.attempts) {
const env = report.environments[attempt.environmentIdx];
const env = report.environments[attempt.environmentIdx ?? 0];
const envId = gEnvIds.get(env);
usedEnvIds.add(envId);
if (attempt.annotations && !attempt.annotations.length)
delete attempt.annotations;
if (attempt.stdout && !attempt.stdout.length)
delete attempt.stdout;
if (attempt.stderr && !attempt.stderr.length)
delete attempt.stderr;
}

@@ -543,3 +738,3 @@ }

...attempt,
environmentIdx: envIdToIndex.get(gEnvIds.get(report.environments[attempt.environmentIdx]))
environmentIdx: envIdToIndex.get(gEnvIds.get(report.environments[attempt.environmentIdx ?? 0]))
}))

@@ -563,3 +758,3 @@ };

visitTests2(report.tests ?? [], "suiteless");
for (const suite of report.suites)
for (const suite of report.suites ?? [])
visitSuite(suite);

@@ -571,3 +766,3 @@ const newEnvironments = [...usedEnvIds];

environments: newEnvironments.map((envId) => gEnvs.get(envId)),
suites: transformSuites(report.suites),
suites: transformSuites(report.suites ?? []),
tests: transformTests(report.tests ?? [])

@@ -771,90 +966,17 @@ };

// src/systemUtilizationSampler.ts
import { spawnSync as spawnSync2 } from "child_process";
import os2 from "os";
function getAvailableMemMacOS() {
const lines = spawnSync2("vm_stat", { encoding: "utf8" }).stdout.trim().split("\n");
const pageSize = parseInt(lines[0].match(/page size of (\d+) bytes/)[1], 10);
if (isNaN(pageSize)) {
console.warn("[flakiness.io] Error detecting macos page size");
return 0;
// src/visitTests.ts
function visitTests(report, testVisitor) {
function visitSuite(suite, parents) {
parents.push(suite);
for (const test of suite.tests ?? [])
testVisitor(test, parents);
for (const childSuite of suite.suites ?? [])
visitSuite(childSuite, parents);
parents.pop();
}
let totalFree = 0;
for (const line of lines) {
if (/Pages (free|inactive|speculative):/.test(line)) {
const match = line.match(/\d+/);
if (match)
totalFree += parseInt(match[0], 10);
}
}
return totalFree * pageSize;
for (const test of report.tests ?? [])
testVisitor(test, []);
for (const suite of report.suites ?? [])
visitSuite(suite, []);
}
function getSystemUtilization() {
let idleTicks = 0;
let totalTicks = 0;
for (const cpu of os2.cpus()) {
totalTicks += cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq + cpu.times.idle;
idleTicks += cpu.times.idle;
}
return {
idleTicks,
totalTicks,
timestamp: Date.now(),
freeBytes: os2.platform() === "darwin" ? getAvailableMemMacOS() : os2.freemem()
};
}
function toFKUtilization(sample, previous) {
const idleTicks = sample.idleTicks - previous.idleTicks;
const totalTicks = sample.totalTicks - previous.totalTicks;
const cpuUtilization = Math.floor((1 - idleTicks / totalTicks) * 1e4) / 100;
const memoryUtilization = Math.floor((1 - sample.freeBytes / os2.totalmem()) * 1e4) / 100;
return {
cpuUtilization,
memoryUtilization,
dts: sample.timestamp - previous.timestamp
};
}
var SystemUtilizationSampler = class {
/**
* The accumulated system utilization data.
*
* This object is populated as samples are collected and can be directly included in
* Flakiness reports. It contains:
* - `samples` - Array of utilization samples with CPU/memory percentages and durations
* - `startTimestamp` - Timestamp when sampling began
* - `totalMemoryBytes` - Total system memory in bytes
*/
result;
_lastSample = getSystemUtilization();
_timer;
/**
* Creates a new SystemUtilizationSampler and starts sampling immediately.
*
* The first sample is collected after 50ms, and subsequent samples are collected
* every 1000ms. Call `dispose()` to stop sampling and clean up resources.
*/
constructor() {
this.result = {
samples: [],
startTimestamp: this._lastSample.timestamp,
totalMemoryBytes: os2.totalmem()
};
this._timer = setTimeout(this._addSample.bind(this), 50);
}
_addSample() {
const sample = getSystemUtilization();
this.result.samples.push(toFKUtilization(sample, this._lastSample));
this._lastSample = sample;
this._timer = setTimeout(this._addSample.bind(this), 1e3);
}
/**
* Stops sampling and cleans up resources.
*
* Call this method when you're done collecting utilization data to stop the sampling
* timer and prevent memory leaks. The `result` object remains accessible after disposal.
*/
dispose() {
clearTimeout(this._timer);
}
};

@@ -1201,7 +1323,8 @@ // src/showReport.ts

CIUtils,
CPUUtilization,
FlakinessProjectConfig,
FlakinessReport,
GitWorktree,
RAMUtilization,
reportUtils_exports as ReportUtils,
SystemUtilizationSampler,
showReport,

@@ -1208,0 +1331,0 @@ uploadReport,

{
"name": "@flakiness/sdk",
"version": "0.151.0",
"version": "0.152.0",
"private": false,

@@ -22,3 +22,3 @@ "repository": {

"type": "module",
"description": "",
"description": "Comprehensive SDK for creating and managing Flakiness JSON Reports in Node.js",
"types": "./types/index.d.ts",

@@ -33,3 +33,2 @@ "scripts": {

"devDependencies": {
"@types/babel__code-frame": "^7.0.6",
"@types/debug": "^4.1.12",

@@ -43,4 +42,3 @@ "@types/node": "^25.0.3",

"dependencies": {
"@babel/code-frame": "^7.26.2",
"@flakiness/flakiness-report": "^0.16.0",
"@flakiness/flakiness-report": "^0.18.0",
"chalk": "^5.6.2",

@@ -47,0 +45,0 @@ "debug": "^4.4.3",

@@ -12,6 +12,4 @@ import { FlakinessReport } from '@flakiness/flakiness-report';

* @param {string} options.name - Human-readable name for the environment (e.g., 'CI', 'Local Dev', 'Staging').
* @param {Record<string, string>} [options.userSuppliedData] - Additional key-value pairs to include
* @param {Record<string, string>} [options.metadata] - Additional key-value pairs to include
* in the environment data. These are merged with `FK_ENV_*` environment variables.
* @param {any} [options.opaqueData] - Optional opaque data object that will be stored with the
* environment but not used for environment deduplication.
*

@@ -21,4 +19,3 @@ * @returns {FlakinessReport.Environment} Environment object containing:

* - `systemData` - Automatically detected OS information (arch, name, version)
* - `userSuppliedData` - Merged data from `FK_ENV_*` variables and `userSuppliedData` option
* - `opaqueData` - The provided opaque data, if any
* - `metadata` - Merged data from `FK_ENV_*` variables and `userSuppliedData` option
*

@@ -33,3 +30,3 @@ * @example

* name: 'Staging',
* userSuppliedData: { region: 'us-east-1', instance: 'large' }
* metadata: { region: 'us-east-1', instance: 'large' }
* });

@@ -40,5 +37,4 @@ * ```

name: string;
userSuppliedData?: Record<string, string>;
opaqueData?: any;
metadata?: Record<string, string>;
}): FlakinessReport.Environment;
//# sourceMappingURL=createEnvironment.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"createEnvironment.d.ts","sourceRoot":"","sources":["../../src/createEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAwD9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,GAAG,CAAC;CAClB,GAAG,eAAe,CAAC,WAAW,CAe9B"}
{"version":3,"file":"createEnvironment.d.ts","sourceRoot":"","sources":["../../src/createEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAwD9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,GAAG,eAAe,CAAC,WAAW,CAc9B"}
export { FlakinessReport } from '@flakiness/flakiness-report';
export { CIUtils } from './ciUtils.js';
export { CPUUtilization } from './cpuUtilization.js';
export { GitWorktree } from './gitWorktree.js';
export { RAMUtilization } from './ramUtilization.js';
export * as ReportUtils from './reportUtils.js';
export { SystemUtilizationSampler } from './systemUtilizationSampler.js';
export { showReport } from './showReport.js';

@@ -7,0 +8,0 @@ export { uploadReport } from './uploadReport.js';

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAG9D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAG9D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
import { FlakinessReport } from "@flakiness/flakiness-report";
/**
* Normalizes a Flakiness report by deduplicating environments, suites, and tests.
* It also drops the fields from JSON that are equal to their default values.
*

@@ -5,0 +6,0 @@ * This function processes a report to:

@@ -1,1 +0,1 @@

{"version":3,"file":"normalizeReport.d.ts","sourceRoot":"","sources":["../../src/normalizeReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAyB9D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,CA2FtF"}
{"version":3,"file":"normalizeReport.d.ts","sourceRoot":"","sources":["../../src/normalizeReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAyB9D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,CA+CtF"}

@@ -0,3 +1,3 @@

export { collectSources } from './collectSources.js';
export { createEnvironment } from './createEnvironment.js';
export { createTestStepSnippetsInplace } from './createTestStepSnippets.js';
export { normalizeReport } from './normalizeReport.js';

@@ -4,0 +4,0 @@ export { stripAnsi } from './stripAnsi.js';

@@ -1,1 +0,1 @@

{"version":3,"file":"reportUtils.d.ts","sourceRoot":"","sources":["../../src/reportUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,UAAU,EAAE,cAAc,EAC1B,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
{"version":3,"file":"reportUtils.d.ts","sourceRoot":"","sources":["../../src/reportUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,UAAU,EAAE,cAAc,EAC1B,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
import { FlakinessReport } from '@flakiness/flakiness-report';
import { GitWorktree } from './gitWorktree.js';
/**
* Generates code snippets for test steps and attaches them to the report in-place.
*
* This function reads source files from the git worktree and creates highlighted code snippets
* for each test step that has a location. The snippets include 3 lines of context (1 before,
* the line itself, 1 after) with syntax highlighting and a visual indicator pointing to the
* exact column position.
*
* The snippets are attached directly to the `step.snippet` property of each test step in the
* report object. Steps without locations or with invalid file paths are silently skipped.
*
* @param {GitWorktree} worktree - Git worktree instance used to resolve file paths from
* git-relative paths to absolute paths for reading source files.
*
* @param {FlakinessReport.Report} report - Flakiness report to process. The report is modified
* in-place by adding `snippet` properties to test steps.
*
* @returns {void} This function modifies the report in-place and does not return a value.
*
* @example
* ```typescript
* const worktree = GitWorktree.create(process.cwd());
* createTestStepSnippetsInplace(worktree, report);
* // Report steps now have .snippet properties with highlighted code
* ```
*/
export declare function createTestStepSnippetsInplace(worktree: GitWorktree, report: FlakinessReport.Report): void;
//# sourceMappingURL=createTestStepSnippets.d.ts.map
{"version":3,"file":"createTestStepSnippets.d.ts","sourceRoot":"","sources":["../../src/createTestStepSnippets.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,QA4ClG"}
import { FlakinessReport } from "@flakiness/flakiness-report";
/**
* Samples and records system CPU and memory utilization over time.
*
* This class continuously monitors system resource usage at regular intervals and stores
* the samples in a format suitable for inclusion in Flakiness reports. Sampling starts
* immediately upon construction and continues until `dispose()` is called.
*
* The first sample is collected after 50ms, and subsequent samples are collected every
* 1000ms (1 second). CPU utilization is calculated as a percentage based on CPU tick
* differences between samples. Memory utilization uses platform-specific methods for
* accurate measurement (especially on macOS).
*/
export declare class SystemUtilizationSampler {
/**
* The accumulated system utilization data.
*
* This object is populated as samples are collected and can be directly included in
* Flakiness reports. It contains:
* - `samples` - Array of utilization samples with CPU/memory percentages and durations
* - `startTimestamp` - Timestamp when sampling began
* - `totalMemoryBytes` - Total system memory in bytes
*/
readonly result: FlakinessReport.SystemUtilization;
private _lastSample;
private _timer;
/**
* Creates a new SystemUtilizationSampler and starts sampling immediately.
*
* The first sample is collected after 50ms, and subsequent samples are collected
* every 1000ms. Call `dispose()` to stop sampling and clean up resources.
*/
constructor();
private _addSample;
/**
* Stops sampling and cleans up resources.
*
* Call this method when you're done collecting utilization data to stop the sampling
* timer and prevent memory leaks. The `result` object remains accessible after disposal.
*/
dispose(): void;
}
//# sourceMappingURL=systemUtilizationSampler.d.ts.map
{"version":3,"file":"systemUtilizationSampler.d.ts","sourceRoot":"","sources":["../../src/systemUtilizationSampler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA6D9D;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAwB;IACnC;;;;;;;;OAQG;IACH,SAAgB,MAAM,EAAE,eAAe,CAAC,iBAAiB,CAAC;IAE1D,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,MAAM,CAAiB;IAE/B;;;;;OAKG;;IAWH,OAAO,CAAC,UAAU;IAOlB;;;;;OAKG;IACH,OAAO;CAGR"}