@vulcn/plugin-report 0.6.2 → 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 +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1641,6 +1641,65 @@ function getFormats(format) {
|
|
|
1641
1641
|
if (format === "all") return ["html", "json", "yaml", "sarif"];
|
|
1642
1642
|
return [format];
|
|
1643
1643
|
}
|
|
1644
|
+
async function writeReports(report, config, logger) {
|
|
1645
|
+
const formats = getFormats(config.format);
|
|
1646
|
+
const outDir = (0, import_node_path.resolve)(config.outputDir);
|
|
1647
|
+
await (0, import_promises.mkdir)(outDir, { recursive: true });
|
|
1648
|
+
const basePath = (0, import_node_path.resolve)(outDir, config.filename);
|
|
1649
|
+
const writtenFiles = [];
|
|
1650
|
+
for (const fmt of formats) {
|
|
1651
|
+
try {
|
|
1652
|
+
switch (fmt) {
|
|
1653
|
+
case "html": {
|
|
1654
|
+
const html = generateHtml(report);
|
|
1655
|
+
const htmlPath = `${basePath}.html`;
|
|
1656
|
+
await (0, import_promises.writeFile)(htmlPath, html, "utf-8");
|
|
1657
|
+
writtenFiles.push(htmlPath);
|
|
1658
|
+
logger.info(`\u{1F4C4} HTML report: ${htmlPath}`);
|
|
1659
|
+
break;
|
|
1660
|
+
}
|
|
1661
|
+
case "json": {
|
|
1662
|
+
const jsonReport = generateJson(report);
|
|
1663
|
+
const jsonPath = `${basePath}.json`;
|
|
1664
|
+
await (0, import_promises.writeFile)(
|
|
1665
|
+
jsonPath,
|
|
1666
|
+
JSON.stringify(jsonReport, null, 2),
|
|
1667
|
+
"utf-8"
|
|
1668
|
+
);
|
|
1669
|
+
writtenFiles.push(jsonPath);
|
|
1670
|
+
logger.info(`\u{1F4C4} JSON report: ${jsonPath}`);
|
|
1671
|
+
break;
|
|
1672
|
+
}
|
|
1673
|
+
case "yaml": {
|
|
1674
|
+
const yamlContent = generateYaml(report);
|
|
1675
|
+
const yamlPath = `${basePath}.yml`;
|
|
1676
|
+
await (0, import_promises.writeFile)(yamlPath, yamlContent, "utf-8");
|
|
1677
|
+
writtenFiles.push(yamlPath);
|
|
1678
|
+
logger.info(`\u{1F4C4} YAML report: ${yamlPath}`);
|
|
1679
|
+
break;
|
|
1680
|
+
}
|
|
1681
|
+
case "sarif": {
|
|
1682
|
+
const sarifReport = generateSarif(report);
|
|
1683
|
+
const sarifPath = `${basePath}.sarif`;
|
|
1684
|
+
await (0, import_promises.writeFile)(
|
|
1685
|
+
sarifPath,
|
|
1686
|
+
JSON.stringify(sarifReport, null, 2),
|
|
1687
|
+
"utf-8"
|
|
1688
|
+
);
|
|
1689
|
+
writtenFiles.push(sarifPath);
|
|
1690
|
+
logger.info(`\u{1F4C4} SARIF report: ${sarifPath}`);
|
|
1691
|
+
break;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
} catch (err) {
|
|
1695
|
+
logger.error(
|
|
1696
|
+
`Failed to generate ${fmt} report: ${err instanceof Error ? err.message : String(err)}`
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
return writtenFiles;
|
|
1701
|
+
}
|
|
1702
|
+
var isScanMode = false;
|
|
1644
1703
|
var plugin = {
|
|
1645
1704
|
name: "@vulcn/plugin-report",
|
|
1646
1705
|
version: "0.1.0",
|
|
@@ -1655,76 +1714,64 @@ var plugin = {
|
|
|
1655
1714
|
);
|
|
1656
1715
|
},
|
|
1657
1716
|
/**
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
1660
|
-
|
|
1661
|
-
|
|
1717
|
+
* Mark that we're in a multi-session scan.
|
|
1718
|
+
* onRunEnd will skip per-session reports — onScanEnd writes the aggregate.
|
|
1719
|
+
*/
|
|
1720
|
+
onScanStart: async (_ctx) => {
|
|
1721
|
+
isScanMode = true;
|
|
1722
|
+
},
|
|
1723
|
+
/**
|
|
1724
|
+
* Generate report after a single-session run.
|
|
1725
|
+
* Skipped when inside a multi-session scan (onScanEnd handles that).
|
|
1662
1726
|
*/
|
|
1663
1727
|
onRunEnd: async (result, ctx) => {
|
|
1728
|
+
if (isScanMode) {
|
|
1729
|
+
return result;
|
|
1730
|
+
}
|
|
1664
1731
|
const config = configSchema.parse(ctx.config);
|
|
1665
|
-
const formats = getFormats(config.format);
|
|
1666
1732
|
const report = buildReport(
|
|
1667
1733
|
ctx.session,
|
|
1668
1734
|
result,
|
|
1669
1735
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
1670
1736
|
ctx.engine.version
|
|
1671
1737
|
);
|
|
1672
|
-
const
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
const writtenFiles = [];
|
|
1676
|
-
for (const fmt of formats) {
|
|
1738
|
+
const writtenFiles = await writeReports(report, config, ctx.logger);
|
|
1739
|
+
if (config.open && writtenFiles.some((f) => f.endsWith(".html"))) {
|
|
1740
|
+
const htmlPath = writtenFiles.find((f) => f.endsWith(".html"));
|
|
1677
1741
|
try {
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
await (0, import_promises.writeFile)(htmlPath, html, "utf-8");
|
|
1683
|
-
writtenFiles.push(htmlPath);
|
|
1684
|
-
ctx.logger.info(`\u{1F4C4} HTML report: ${htmlPath}`);
|
|
1685
|
-
break;
|
|
1686
|
-
}
|
|
1687
|
-
case "json": {
|
|
1688
|
-
const jsonReport = generateJson(report);
|
|
1689
|
-
const jsonPath = `${basePath}.json`;
|
|
1690
|
-
await (0, import_promises.writeFile)(
|
|
1691
|
-
jsonPath,
|
|
1692
|
-
JSON.stringify(jsonReport, null, 2),
|
|
1693
|
-
"utf-8"
|
|
1694
|
-
);
|
|
1695
|
-
writtenFiles.push(jsonPath);
|
|
1696
|
-
ctx.logger.info(`\u{1F4C4} JSON report: ${jsonPath}`);
|
|
1697
|
-
break;
|
|
1698
|
-
}
|
|
1699
|
-
case "yaml": {
|
|
1700
|
-
const yamlContent = generateYaml(report);
|
|
1701
|
-
const yamlPath = `${basePath}.yml`;
|
|
1702
|
-
await (0, import_promises.writeFile)(yamlPath, yamlContent, "utf-8");
|
|
1703
|
-
writtenFiles.push(yamlPath);
|
|
1704
|
-
ctx.logger.info(`\u{1F4C4} YAML report: ${yamlPath}`);
|
|
1705
|
-
break;
|
|
1706
|
-
}
|
|
1707
|
-
case "sarif": {
|
|
1708
|
-
const sarifReport = generateSarif(report);
|
|
1709
|
-
const sarifPath = `${basePath}.sarif`;
|
|
1710
|
-
await (0, import_promises.writeFile)(
|
|
1711
|
-
sarifPath,
|
|
1712
|
-
JSON.stringify(sarifReport, null, 2),
|
|
1713
|
-
"utf-8"
|
|
1714
|
-
);
|
|
1715
|
-
writtenFiles.push(sarifPath);
|
|
1716
|
-
ctx.logger.info(`\u{1F4C4} SARIF report: ${sarifPath}`);
|
|
1717
|
-
break;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
} catch (err) {
|
|
1721
|
-
ctx.logger.error(
|
|
1722
|
-
`Failed to generate ${fmt} report: ${err instanceof Error ? err.message : String(err)}`
|
|
1723
|
-
);
|
|
1742
|
+
const { exec } = await import("child_process");
|
|
1743
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1744
|
+
exec(`${openCmd} "${htmlPath}"`);
|
|
1745
|
+
} catch {
|
|
1724
1746
|
}
|
|
1725
1747
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1748
|
+
return result;
|
|
1749
|
+
},
|
|
1750
|
+
/**
|
|
1751
|
+
* Generate aggregate report after all sessions in a scan complete.
|
|
1752
|
+
* This is the single report for vulcn run <session-dir>.
|
|
1753
|
+
*/
|
|
1754
|
+
onScanEnd: async (result, ctx) => {
|
|
1755
|
+
isScanMode = false;
|
|
1756
|
+
const config = configSchema.parse(ctx.config);
|
|
1757
|
+
const syntheticSession = {
|
|
1758
|
+
name: `Scan (${ctx.sessionCount} session${ctx.sessionCount !== 1 ? "s" : ""})`,
|
|
1759
|
+
driver: ctx.sessions[0]?.driver ?? "browser",
|
|
1760
|
+
driverConfig: ctx.sessions[0]?.driverConfig ?? {},
|
|
1761
|
+
steps: [],
|
|
1762
|
+
metadata: {
|
|
1763
|
+
sessionCount: ctx.sessionCount
|
|
1764
|
+
}
|
|
1765
|
+
};
|
|
1766
|
+
const report = buildReport(
|
|
1767
|
+
syntheticSession,
|
|
1768
|
+
result,
|
|
1769
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
1770
|
+
ctx.engine.version
|
|
1771
|
+
);
|
|
1772
|
+
const writtenFiles = await writeReports(report, config, ctx.logger);
|
|
1773
|
+
if (config.open && writtenFiles.some((f) => f.endsWith(".html"))) {
|
|
1774
|
+
const htmlPath = writtenFiles.find((f) => f.endsWith(".html"));
|
|
1728
1775
|
try {
|
|
1729
1776
|
const { exec } = await import("child_process");
|
|
1730
1777
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|