@vulcn/plugin-report 0.6.1 → 0.6.3
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/dist/index.cjs +105 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -15
- package/dist/index.d.ts +0 -15
- package/dist/index.js +105 -58
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1601,6 +1601,65 @@ function getFormats(format) {
|
|
|
1601
1601
|
if (format === "all") return ["html", "json", "yaml", "sarif"];
|
|
1602
1602
|
return [format];
|
|
1603
1603
|
}
|
|
1604
|
+
async function writeReports(report, config, logger) {
|
|
1605
|
+
const formats = getFormats(config.format);
|
|
1606
|
+
const outDir = resolve(config.outputDir);
|
|
1607
|
+
await mkdir(outDir, { recursive: true });
|
|
1608
|
+
const basePath = resolve(outDir, config.filename);
|
|
1609
|
+
const writtenFiles = [];
|
|
1610
|
+
for (const fmt of formats) {
|
|
1611
|
+
try {
|
|
1612
|
+
switch (fmt) {
|
|
1613
|
+
case "html": {
|
|
1614
|
+
const html = generateHtml(report);
|
|
1615
|
+
const htmlPath = `${basePath}.html`;
|
|
1616
|
+
await writeFile(htmlPath, html, "utf-8");
|
|
1617
|
+
writtenFiles.push(htmlPath);
|
|
1618
|
+
logger.info(`\u{1F4C4} HTML report: ${htmlPath}`);
|
|
1619
|
+
break;
|
|
1620
|
+
}
|
|
1621
|
+
case "json": {
|
|
1622
|
+
const jsonReport = generateJson(report);
|
|
1623
|
+
const jsonPath = `${basePath}.json`;
|
|
1624
|
+
await writeFile(
|
|
1625
|
+
jsonPath,
|
|
1626
|
+
JSON.stringify(jsonReport, null, 2),
|
|
1627
|
+
"utf-8"
|
|
1628
|
+
);
|
|
1629
|
+
writtenFiles.push(jsonPath);
|
|
1630
|
+
logger.info(`\u{1F4C4} JSON report: ${jsonPath}`);
|
|
1631
|
+
break;
|
|
1632
|
+
}
|
|
1633
|
+
case "yaml": {
|
|
1634
|
+
const yamlContent = generateYaml(report);
|
|
1635
|
+
const yamlPath = `${basePath}.yml`;
|
|
1636
|
+
await writeFile(yamlPath, yamlContent, "utf-8");
|
|
1637
|
+
writtenFiles.push(yamlPath);
|
|
1638
|
+
logger.info(`\u{1F4C4} YAML report: ${yamlPath}`);
|
|
1639
|
+
break;
|
|
1640
|
+
}
|
|
1641
|
+
case "sarif": {
|
|
1642
|
+
const sarifReport = generateSarif(report);
|
|
1643
|
+
const sarifPath = `${basePath}.sarif`;
|
|
1644
|
+
await writeFile(
|
|
1645
|
+
sarifPath,
|
|
1646
|
+
JSON.stringify(sarifReport, null, 2),
|
|
1647
|
+
"utf-8"
|
|
1648
|
+
);
|
|
1649
|
+
writtenFiles.push(sarifPath);
|
|
1650
|
+
logger.info(`\u{1F4C4} SARIF report: ${sarifPath}`);
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
} catch (err) {
|
|
1655
|
+
logger.error(
|
|
1656
|
+
`Failed to generate ${fmt} report: ${err instanceof Error ? err.message : String(err)}`
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return writtenFiles;
|
|
1661
|
+
}
|
|
1662
|
+
var isScanMode = false;
|
|
1604
1663
|
var plugin = {
|
|
1605
1664
|
name: "@vulcn/plugin-report",
|
|
1606
1665
|
version: "0.1.0",
|
|
@@ -1615,76 +1674,64 @@ var plugin = {
|
|
|
1615
1674
|
);
|
|
1616
1675
|
},
|
|
1617
1676
|
/**
|
|
1618
|
-
*
|
|
1619
|
-
*
|
|
1620
|
-
|
|
1621
|
-
|
|
1677
|
+
* Mark that we're in a multi-session scan.
|
|
1678
|
+
* onRunEnd will skip per-session reports — onScanEnd writes the aggregate.
|
|
1679
|
+
*/
|
|
1680
|
+
onScanStart: async (_ctx) => {
|
|
1681
|
+
isScanMode = true;
|
|
1682
|
+
},
|
|
1683
|
+
/**
|
|
1684
|
+
* Generate report after a single-session run.
|
|
1685
|
+
* Skipped when inside a multi-session scan (onScanEnd handles that).
|
|
1622
1686
|
*/
|
|
1623
1687
|
onRunEnd: async (result, ctx) => {
|
|
1688
|
+
if (isScanMode) {
|
|
1689
|
+
return result;
|
|
1690
|
+
}
|
|
1624
1691
|
const config = configSchema.parse(ctx.config);
|
|
1625
|
-
const formats = getFormats(config.format);
|
|
1626
1692
|
const report = buildReport(
|
|
1627
1693
|
ctx.session,
|
|
1628
1694
|
result,
|
|
1629
1695
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
1630
1696
|
ctx.engine.version
|
|
1631
1697
|
);
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
const writtenFiles = [];
|
|
1636
|
-
for (const fmt of formats) {
|
|
1698
|
+
const writtenFiles = await writeReports(report, config, ctx.logger);
|
|
1699
|
+
if (config.open && writtenFiles.some((f) => f.endsWith(".html"))) {
|
|
1700
|
+
const htmlPath = writtenFiles.find((f) => f.endsWith(".html"));
|
|
1637
1701
|
try {
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
await writeFile(htmlPath, html, "utf-8");
|
|
1643
|
-
writtenFiles.push(htmlPath);
|
|
1644
|
-
ctx.logger.info(`\u{1F4C4} HTML report: ${htmlPath}`);
|
|
1645
|
-
break;
|
|
1646
|
-
}
|
|
1647
|
-
case "json": {
|
|
1648
|
-
const jsonReport = generateJson(report);
|
|
1649
|
-
const jsonPath = `${basePath}.json`;
|
|
1650
|
-
await writeFile(
|
|
1651
|
-
jsonPath,
|
|
1652
|
-
JSON.stringify(jsonReport, null, 2),
|
|
1653
|
-
"utf-8"
|
|
1654
|
-
);
|
|
1655
|
-
writtenFiles.push(jsonPath);
|
|
1656
|
-
ctx.logger.info(`\u{1F4C4} JSON report: ${jsonPath}`);
|
|
1657
|
-
break;
|
|
1658
|
-
}
|
|
1659
|
-
case "yaml": {
|
|
1660
|
-
const yamlContent = generateYaml(report);
|
|
1661
|
-
const yamlPath = `${basePath}.yml`;
|
|
1662
|
-
await writeFile(yamlPath, yamlContent, "utf-8");
|
|
1663
|
-
writtenFiles.push(yamlPath);
|
|
1664
|
-
ctx.logger.info(`\u{1F4C4} YAML report: ${yamlPath}`);
|
|
1665
|
-
break;
|
|
1666
|
-
}
|
|
1667
|
-
case "sarif": {
|
|
1668
|
-
const sarifReport = generateSarif(report);
|
|
1669
|
-
const sarifPath = `${basePath}.sarif`;
|
|
1670
|
-
await writeFile(
|
|
1671
|
-
sarifPath,
|
|
1672
|
-
JSON.stringify(sarifReport, null, 2),
|
|
1673
|
-
"utf-8"
|
|
1674
|
-
);
|
|
1675
|
-
writtenFiles.push(sarifPath);
|
|
1676
|
-
ctx.logger.info(`\u{1F4C4} SARIF report: ${sarifPath}`);
|
|
1677
|
-
break;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
} catch (err) {
|
|
1681
|
-
ctx.logger.error(
|
|
1682
|
-
`Failed to generate ${fmt} report: ${err instanceof Error ? err.message : String(err)}`
|
|
1683
|
-
);
|
|
1702
|
+
const { exec } = await import("child_process");
|
|
1703
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1704
|
+
exec(`${openCmd} "${htmlPath}"`);
|
|
1705
|
+
} catch {
|
|
1684
1706
|
}
|
|
1685
1707
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1708
|
+
return result;
|
|
1709
|
+
},
|
|
1710
|
+
/**
|
|
1711
|
+
* Generate aggregate report after all sessions in a scan complete.
|
|
1712
|
+
* This is the single report for vulcn run <session-dir>.
|
|
1713
|
+
*/
|
|
1714
|
+
onScanEnd: async (result, ctx) => {
|
|
1715
|
+
isScanMode = false;
|
|
1716
|
+
const config = configSchema.parse(ctx.config);
|
|
1717
|
+
const syntheticSession = {
|
|
1718
|
+
name: `Scan (${ctx.sessionCount} session${ctx.sessionCount !== 1 ? "s" : ""})`,
|
|
1719
|
+
driver: ctx.sessions[0]?.driver ?? "browser",
|
|
1720
|
+
driverConfig: ctx.sessions[0]?.driverConfig ?? {},
|
|
1721
|
+
steps: [],
|
|
1722
|
+
metadata: {
|
|
1723
|
+
sessionCount: ctx.sessionCount
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
const report = buildReport(
|
|
1727
|
+
syntheticSession,
|
|
1728
|
+
result,
|
|
1729
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
1730
|
+
ctx.engine.version
|
|
1731
|
+
);
|
|
1732
|
+
const writtenFiles = await writeReports(report, config, ctx.logger);
|
|
1733
|
+
if (config.open && writtenFiles.some((f) => f.endsWith(".html"))) {
|
|
1734
|
+
const htmlPath = writtenFiles.find((f) => f.endsWith(".html"));
|
|
1688
1735
|
try {
|
|
1689
1736
|
const { exec } = await import("child_process");
|
|
1690
1737
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|