executable-stories-formatters 0.16.0 → 0.17.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/dist/cli.js +473 -247
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +315 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -5
- package/dist/index.d.ts +100 -5
- package/dist/index.js +308 -102
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import "fs";
|
|
3
|
-
import * as
|
|
3
|
+
import * as path12 from "path";
|
|
4
4
|
import * as fsPromises from "fs/promises";
|
|
5
5
|
|
|
6
6
|
// src/converters/acl/status.ts
|
|
@@ -51,7 +51,7 @@ function tokenize(text2) {
|
|
|
51
51
|
}
|
|
52
52
|
function behaviourFingerprint(input) {
|
|
53
53
|
const steps = input.steps.map((step) => `${step.keyword.toLowerCase()}:${normalizeText(step.text)}`).join("\n");
|
|
54
|
-
const covers = [...input.covers ?? []].map((
|
|
54
|
+
const covers = [...input.covers ?? []].map((path13) => path13.trim()).filter(Boolean).sort().join(",");
|
|
55
55
|
if (steps.length === 0 && covers.length === 0) return "";
|
|
56
56
|
return createHash("sha1").update(`${steps}\0${covers}`).digest("hex").slice(0, 16);
|
|
57
57
|
}
|
|
@@ -14274,12 +14274,12 @@ function hasSufficientHistory(entries, min) {
|
|
|
14274
14274
|
}
|
|
14275
14275
|
|
|
14276
14276
|
// src/formatters/html/renderers/scenario.ts
|
|
14277
|
-
function renderTicket(ticket, template,
|
|
14277
|
+
function renderTicket(ticket, template, escapeHtml5) {
|
|
14278
14278
|
const url = ticket.url ?? (template ? template.replace("{ticket}", ticket.id) : void 0);
|
|
14279
14279
|
if (url) {
|
|
14280
|
-
return `<a class="tag ticket-tag" href="${
|
|
14280
|
+
return `<a class="tag ticket-tag" href="${escapeHtml5(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml5(ticket.id)}</a>`;
|
|
14281
14281
|
}
|
|
14282
|
-
return `<span class="tag ticket-tag">${
|
|
14282
|
+
return `<span class="tag ticket-tag">${escapeHtml5(ticket.id)}</span>`;
|
|
14283
14283
|
}
|
|
14284
14284
|
function renderScenario(args, deps) {
|
|
14285
14285
|
const { tc } = args;
|
|
@@ -14479,7 +14479,7 @@ function flattenTree(roots) {
|
|
|
14479
14479
|
}
|
|
14480
14480
|
return result;
|
|
14481
14481
|
}
|
|
14482
|
-
function buildTooltip(span,
|
|
14482
|
+
function buildTooltip(span, escapeHtml5) {
|
|
14483
14483
|
const parts = [];
|
|
14484
14484
|
parts.push(`${span.name} (${formatDuration(span.durationMs)})`);
|
|
14485
14485
|
if (span.statusMessage) {
|
|
@@ -14497,7 +14497,7 @@ function buildTooltip(span, escapeHtml4) {
|
|
|
14497
14497
|
if (text2.length > TOOLTIP_MAX_LENGTH) {
|
|
14498
14498
|
text2 = text2.slice(0, TOOLTIP_MAX_LENGTH - 3) + "...";
|
|
14499
14499
|
}
|
|
14500
|
-
return
|
|
14500
|
+
return escapeHtml5(text2);
|
|
14501
14501
|
}
|
|
14502
14502
|
function renderTraceView(args, deps) {
|
|
14503
14503
|
if (!args.spans || args.spans.length === 0) return "";
|
|
@@ -15792,14 +15792,14 @@ var TraceabilityMatrixFormatter = class {
|
|
|
15792
15792
|
lines.push("");
|
|
15793
15793
|
lines.push(`Status: ${renderRequirementStatus(req.status)}`);
|
|
15794
15794
|
if (req.covers.length > 0) {
|
|
15795
|
-
lines.push(`Covers: ${req.covers.map((
|
|
15795
|
+
lines.push(`Covers: ${req.covers.map((path13) => `\`${path13}\``).join(", ")}`);
|
|
15796
15796
|
}
|
|
15797
15797
|
lines.push("");
|
|
15798
15798
|
lines.push("| Status | Scenario | Source | Covers |");
|
|
15799
15799
|
lines.push("| --- | --- | --- | --- |");
|
|
15800
15800
|
for (const scenario of req.scenarios) {
|
|
15801
15801
|
const source = `${scenario.sourceFile}:${scenario.sourceLine}`;
|
|
15802
|
-
const covers = scenario.covers.length > 0 ? scenario.covers.map((
|
|
15802
|
+
const covers = scenario.covers.length > 0 ? scenario.covers.map((path13) => `\`${path13}\``).join(", ") : "";
|
|
15803
15803
|
lines.push(`| ${scenario.status} | ${escapePipe2(scenario.title)} | \`${source}\` | ${covers} |`);
|
|
15804
15804
|
}
|
|
15805
15805
|
lines.push("");
|
|
@@ -15901,8 +15901,8 @@ function extractFeatureName(testCases, uri) {
|
|
|
15901
15901
|
return tc.titlePath[0];
|
|
15902
15902
|
}
|
|
15903
15903
|
}
|
|
15904
|
-
const
|
|
15905
|
-
return
|
|
15904
|
+
const basename4 = uri.replace(/^.*[\\/]/, "").replace(/\.[^.]+$/, "");
|
|
15905
|
+
return basename4.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
15906
15906
|
}
|
|
15907
15907
|
function synthesizeFeature(uri, testCases) {
|
|
15908
15908
|
const featureName = extractFeatureName(testCases, uri);
|
|
@@ -16514,8 +16514,8 @@ function extractDocAttachments(step) {
|
|
|
16514
16514
|
}
|
|
16515
16515
|
return attachments;
|
|
16516
16516
|
}
|
|
16517
|
-
function guessMediaType(
|
|
16518
|
-
const lower =
|
|
16517
|
+
function guessMediaType(path13) {
|
|
16518
|
+
const lower = path13.toLowerCase();
|
|
16519
16519
|
if (lower.endsWith(".png")) return "image/png";
|
|
16520
16520
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
16521
16521
|
if (lower.endsWith(".gif")) return "image/gif";
|
|
@@ -16656,11 +16656,11 @@ var CucumberHtmlFormatter = class {
|
|
|
16656
16656
|
for (const envelope of envelopes) {
|
|
16657
16657
|
const accepted = htmlStream.write(envelope);
|
|
16658
16658
|
if (!accepted) {
|
|
16659
|
-
await new Promise((
|
|
16659
|
+
await new Promise((resolve10) => htmlStream.once("drain", resolve10));
|
|
16660
16660
|
}
|
|
16661
16661
|
}
|
|
16662
|
-
await new Promise((
|
|
16663
|
-
collector.on("finish",
|
|
16662
|
+
await new Promise((resolve10, reject) => {
|
|
16663
|
+
collector.on("finish", resolve10);
|
|
16664
16664
|
collector.on("error", reject);
|
|
16665
16665
|
htmlStream.end();
|
|
16666
16666
|
});
|
|
@@ -18824,14 +18824,14 @@ ${result.errors.join("\n")}`);
|
|
|
18824
18824
|
}
|
|
18825
18825
|
|
|
18826
18826
|
// src/coverage-index.ts
|
|
18827
|
-
function normalizePath(
|
|
18828
|
-
return
|
|
18827
|
+
function normalizePath(path13) {
|
|
18828
|
+
return path13.replace(/^\.\//, "");
|
|
18829
18829
|
}
|
|
18830
18830
|
function scenariosCoveringPaths(index, paths) {
|
|
18831
18831
|
const queries = paths.map(normalizePath);
|
|
18832
18832
|
return index.scenarios.filter(
|
|
18833
18833
|
(scenario) => scenario.covers.some(
|
|
18834
|
-
(glob) => queries.some((
|
|
18834
|
+
(glob) => queries.some((path13) => matchesPattern(normalizePath(glob), path13))
|
|
18835
18835
|
)
|
|
18836
18836
|
);
|
|
18837
18837
|
}
|
|
@@ -18907,7 +18907,7 @@ function toRun(data, inputType, synthesize) {
|
|
|
18907
18907
|
if (synthesize) raw = synthesizeStories(raw);
|
|
18908
18908
|
return canonicalizeRun(raw);
|
|
18909
18909
|
}
|
|
18910
|
-
async function
|
|
18910
|
+
async function regenerateRun(options, deps = {}) {
|
|
18911
18911
|
const read = deps.readFile ?? ((filePath) => fs6.readFileSync(filePath, "utf8"));
|
|
18912
18912
|
const data = JSON.parse(read(path7.resolve(options.input)));
|
|
18913
18913
|
const run = toRun(data, options.inputType ?? "raw", options.synthesize !== false);
|
|
@@ -18917,7 +18917,10 @@ async function regenerateArtifacts(options, deps = {}) {
|
|
|
18917
18917
|
outputName: options.outputName
|
|
18918
18918
|
});
|
|
18919
18919
|
const result = await generator.generate(run);
|
|
18920
|
-
return [...result.values()].flat();
|
|
18920
|
+
return { files: [...result.values()].flat(), run };
|
|
18921
|
+
}
|
|
18922
|
+
async function regenerateArtifacts(options, deps = {}) {
|
|
18923
|
+
return (await regenerateRun(options, deps)).files;
|
|
18921
18924
|
}
|
|
18922
18925
|
function startWatch(options, deps = {}) {
|
|
18923
18926
|
const log = deps.log ?? ((message) => console.log(message));
|
|
@@ -18960,6 +18963,203 @@ function startWatch(options, deps = {}) {
|
|
|
18960
18963
|
};
|
|
18961
18964
|
}
|
|
18962
18965
|
|
|
18966
|
+
// src/serve.ts
|
|
18967
|
+
import * as fs7 from "fs";
|
|
18968
|
+
import * as http from "http";
|
|
18969
|
+
import * as path8 from "path";
|
|
18970
|
+
function advanceState(prev, run) {
|
|
18971
|
+
if (prev.sessionBaseline === null) {
|
|
18972
|
+
return { sessionBaseline: run, previous: null, current: run, runCount: 1 };
|
|
18973
|
+
}
|
|
18974
|
+
return {
|
|
18975
|
+
sessionBaseline: prev.sessionBaseline,
|
|
18976
|
+
previous: prev.current,
|
|
18977
|
+
current: run,
|
|
18978
|
+
runCount: prev.runCount + 1
|
|
18979
|
+
};
|
|
18980
|
+
}
|
|
18981
|
+
function computeDeltas(state) {
|
|
18982
|
+
if (state.current === null || state.sessionBaseline === null || state.runCount <= 1) {
|
|
18983
|
+
return { session: null, iteration: null };
|
|
18984
|
+
}
|
|
18985
|
+
return {
|
|
18986
|
+
session: diffRuns(state.sessionBaseline, state.current),
|
|
18987
|
+
iteration: state.previous ? diffRuns(state.previous, state.current) : null
|
|
18988
|
+
};
|
|
18989
|
+
}
|
|
18990
|
+
function pluralize(n, word) {
|
|
18991
|
+
return `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
18992
|
+
}
|
|
18993
|
+
function summarizeDiff(diff) {
|
|
18994
|
+
const s = diff.summary;
|
|
18995
|
+
const parts = [];
|
|
18996
|
+
if (s.fixed > 0) parts.push(`+${pluralize(s.fixed, "passing")}`);
|
|
18997
|
+
if (s.regressed > 0) parts.push(`${pluralize(s.regressed, "regressed")}`);
|
|
18998
|
+
if (s.added > 0) parts.push(`${pluralize(s.added, "new behaviour")}`);
|
|
18999
|
+
if (s.removed > 0) parts.push(`${pluralize(s.removed, "removed")}`);
|
|
19000
|
+
const moved = s.renamed + s.moved;
|
|
19001
|
+
if (moved > 0) parts.push(`${pluralize(moved, "renamed")}`);
|
|
19002
|
+
if (s.changed > 0) parts.push(`${pluralize(s.changed, "changed")}`);
|
|
19003
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
19004
|
+
}
|
|
19005
|
+
function escapeHtml3(text2) {
|
|
19006
|
+
return text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
19007
|
+
}
|
|
19008
|
+
function renderDeltaStrip(state) {
|
|
19009
|
+
if (state.current === null) return "";
|
|
19010
|
+
const { session, iteration } = computeDeltas(state);
|
|
19011
|
+
if (session === null) {
|
|
19012
|
+
const label = `Run #${state.runCount} captured \u2014 baseline pinned. Watching for changes\u2026`;
|
|
19013
|
+
return `<div data-es-live="strip"><strong>Live</strong> \xB7 ${escapeHtml3(label)}</div>`;
|
|
19014
|
+
}
|
|
19015
|
+
const sessionLine = summarizeDiff(session) ?? "no change yet";
|
|
19016
|
+
let detail = "";
|
|
19017
|
+
if (iteration) {
|
|
19018
|
+
const iterationLine = summarizeDiff(iteration);
|
|
19019
|
+
if (iterationLine) detail = ` \xB7 <span data-es-live="iteration">this iteration: ${escapeHtml3(iterationLine)}</span>`;
|
|
19020
|
+
}
|
|
19021
|
+
return [
|
|
19022
|
+
`<div data-es-live="strip">`,
|
|
19023
|
+
`<strong>Live</strong> \xB7 run #${state.runCount} \xB7 `,
|
|
19024
|
+
`<span data-es-live="session">since you started: ${escapeHtml3(sessionLine)}</span>`,
|
|
19025
|
+
detail,
|
|
19026
|
+
`</div>`
|
|
19027
|
+
].join("");
|
|
19028
|
+
}
|
|
19029
|
+
var RELOAD_CLIENT = `<script data-es-live="client">
|
|
19030
|
+
(function () {
|
|
19031
|
+
try {
|
|
19032
|
+
var es = new EventSource("/__es_reload");
|
|
19033
|
+
es.onmessage = function (e) { if (e.data === "reload") location.reload(); };
|
|
19034
|
+
} catch (err) { /* SSE unavailable: stay static */ }
|
|
19035
|
+
})();
|
|
19036
|
+
</script>`;
|
|
19037
|
+
var STRIP_STYLE = `<style data-es-live="style">
|
|
19038
|
+
[data-es-live="strip"]{position:sticky;top:0;z-index:9999;font:14px/1.5 system-ui,sans-serif;
|
|
19039
|
+
padding:8px 16px;background:#0b1021;color:#e6e9f5;border-bottom:1px solid #2a3052}
|
|
19040
|
+
[data-es-live="strip"] strong{color:#7dd3fc}
|
|
19041
|
+
</style>`;
|
|
19042
|
+
function injectLiveBits(html, stripHtml) {
|
|
19043
|
+
let out = html;
|
|
19044
|
+
const bodyOpen = out.match(/<body[^>]*>/i);
|
|
19045
|
+
if (bodyOpen) {
|
|
19046
|
+
const at = bodyOpen.index + bodyOpen[0].length;
|
|
19047
|
+
out = out.slice(0, at) + stripHtml + out.slice(at);
|
|
19048
|
+
} else {
|
|
19049
|
+
out = stripHtml + out;
|
|
19050
|
+
}
|
|
19051
|
+
const tail = STRIP_STYLE + RELOAD_CLIENT;
|
|
19052
|
+
if (/<\/body>/i.test(out)) {
|
|
19053
|
+
out = out.replace(/<\/body>/i, tail + "</body>");
|
|
19054
|
+
} else {
|
|
19055
|
+
out += tail;
|
|
19056
|
+
}
|
|
19057
|
+
return out;
|
|
19058
|
+
}
|
|
19059
|
+
var CONTENT_TYPES = {
|
|
19060
|
+
".html": "text/html; charset=utf-8",
|
|
19061
|
+
".css": "text/css; charset=utf-8",
|
|
19062
|
+
".js": "text/javascript; charset=utf-8",
|
|
19063
|
+
".json": "application/json; charset=utf-8",
|
|
19064
|
+
".svg": "image/svg+xml",
|
|
19065
|
+
".png": "image/png"
|
|
19066
|
+
};
|
|
19067
|
+
function watchInputDir(filePath, listener) {
|
|
19068
|
+
const dir = path8.dirname(filePath);
|
|
19069
|
+
const base = path8.basename(filePath);
|
|
19070
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
19071
|
+
const watcher = fs7.watch(dir, (_event, changed) => {
|
|
19072
|
+
if (!changed || changed === base) listener();
|
|
19073
|
+
});
|
|
19074
|
+
return { close: () => watcher.close() };
|
|
19075
|
+
}
|
|
19076
|
+
function plainText(html) {
|
|
19077
|
+
return html.replace(/<[^>]+>/g, "").trim();
|
|
19078
|
+
}
|
|
19079
|
+
function startServe(options, deps = {}) {
|
|
19080
|
+
const log = deps.log ?? ((message) => console.log(message));
|
|
19081
|
+
const read = deps.readFile ?? ((filePath) => fs7.readFileSync(filePath, "utf8"));
|
|
19082
|
+
const port = options.port ?? 4321;
|
|
19083
|
+
const host = options.host ?? "127.0.0.1";
|
|
19084
|
+
let state = { sessionBaseline: null, previous: null, current: null, runCount: 0 };
|
|
19085
|
+
let htmlPath = null;
|
|
19086
|
+
let stripHtml = renderDeltaStrip(state);
|
|
19087
|
+
const clients = /* @__PURE__ */ new Set();
|
|
19088
|
+
const pushReload = () => {
|
|
19089
|
+
for (const res of clients) res.write("data: reload\n\n");
|
|
19090
|
+
};
|
|
19091
|
+
const handler = (req, res) => {
|
|
19092
|
+
const url = (req.url ?? "/").split("?")[0];
|
|
19093
|
+
if (url === "/__es_reload") {
|
|
19094
|
+
res.writeHead(200, {
|
|
19095
|
+
"Content-Type": "text/event-stream",
|
|
19096
|
+
"Cache-Control": "no-cache",
|
|
19097
|
+
Connection: "keep-alive"
|
|
19098
|
+
});
|
|
19099
|
+
res.write("retry: 1000\n\n");
|
|
19100
|
+
clients.add(res);
|
|
19101
|
+
req.on("close", () => clients.delete(res));
|
|
19102
|
+
return;
|
|
19103
|
+
}
|
|
19104
|
+
if (url === "/" || url === "/index.html") {
|
|
19105
|
+
const html = htmlPath ? read(htmlPath) : "<!doctype html><html><body><h1>executable-stories</h1></body></html>";
|
|
19106
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
19107
|
+
res.end(injectLiveBits(html, stripHtml));
|
|
19108
|
+
return;
|
|
19109
|
+
}
|
|
19110
|
+
const safe = path8.normalize(url).replace(/^(\.\.[/\\])+/, "");
|
|
19111
|
+
const filePath = path8.join(path8.resolve(options.outputDir), safe);
|
|
19112
|
+
if (filePath.startsWith(path8.resolve(options.outputDir)) && fs7.existsSync(filePath)) {
|
|
19113
|
+
const ext = path8.extname(filePath).toLowerCase();
|
|
19114
|
+
res.writeHead(200, { "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream" });
|
|
19115
|
+
res.end(read(filePath));
|
|
19116
|
+
return;
|
|
19117
|
+
}
|
|
19118
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
19119
|
+
res.end("Not found");
|
|
19120
|
+
};
|
|
19121
|
+
const server = deps.createServer ? deps.createServer(handler) : http.createServer(handler);
|
|
19122
|
+
const watchOptions = {
|
|
19123
|
+
input: options.input,
|
|
19124
|
+
outputDir: options.outputDir,
|
|
19125
|
+
outputName: options.outputName,
|
|
19126
|
+
formats: options.formats,
|
|
19127
|
+
inputType: options.inputType ?? "raw",
|
|
19128
|
+
synthesize: options.synthesize !== false,
|
|
19129
|
+
debounceMs: options.debounceMs
|
|
19130
|
+
};
|
|
19131
|
+
const watchHandle = startWatch(watchOptions, {
|
|
19132
|
+
readFile: read,
|
|
19133
|
+
watch: deps.watch ?? watchInputDir,
|
|
19134
|
+
log: () => {
|
|
19135
|
+
},
|
|
19136
|
+
// serve emits its own per-run line below
|
|
19137
|
+
regenerate: async (input) => {
|
|
19138
|
+
if (!fs7.existsSync(path8.resolve(input))) return [];
|
|
19139
|
+
const { files, run } = await regenerateRun({ ...watchOptions, input }, { readFile: read });
|
|
19140
|
+
htmlPath = files.find((f) => f.endsWith(".html")) ?? htmlPath;
|
|
19141
|
+
state = advanceState(state, run);
|
|
19142
|
+
stripHtml = renderDeltaStrip(state);
|
|
19143
|
+
log(`Run #${state.runCount}: ${plainText(stripHtml)}`);
|
|
19144
|
+
pushReload();
|
|
19145
|
+
return files;
|
|
19146
|
+
}
|
|
19147
|
+
});
|
|
19148
|
+
server.listen(port, host);
|
|
19149
|
+
const address = server.address();
|
|
19150
|
+
const boundPort = typeof address === "object" && address ? address.port : port;
|
|
19151
|
+
log(`Live docs: http://${host}:${boundPort} (Ctrl+C to stop)`);
|
|
19152
|
+
return {
|
|
19153
|
+
port: boundPort,
|
|
19154
|
+
close: () => {
|
|
19155
|
+
watchHandle.close();
|
|
19156
|
+
for (const res of clients) res.end();
|
|
19157
|
+
clients.clear();
|
|
19158
|
+
server.close();
|
|
19159
|
+
}
|
|
19160
|
+
};
|
|
19161
|
+
}
|
|
19162
|
+
|
|
18963
19163
|
// src/behavior-diff.ts
|
|
18964
19164
|
function classifyStatusChange(baseline, current) {
|
|
18965
19165
|
if (baseline === void 0) return "added";
|
|
@@ -19599,27 +19799,27 @@ function pickleStepArgumentToDocs(ps) {
|
|
|
19599
19799
|
}
|
|
19600
19800
|
|
|
19601
19801
|
// src/utils/git-info.ts
|
|
19602
|
-
import * as
|
|
19603
|
-
import * as
|
|
19802
|
+
import * as fs8 from "fs";
|
|
19803
|
+
import * as path9 from "path";
|
|
19604
19804
|
function readGitSha(cwd = process.cwd()) {
|
|
19605
19805
|
const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;
|
|
19606
19806
|
if (envSha) return envSha;
|
|
19607
19807
|
const gitDir = findGitDir(cwd);
|
|
19608
19808
|
if (!gitDir) return void 0;
|
|
19609
19809
|
try {
|
|
19610
|
-
const headPath =
|
|
19611
|
-
const head =
|
|
19810
|
+
const headPath = path9.join(gitDir, "HEAD");
|
|
19811
|
+
const head = fs8.readFileSync(headPath, "utf8").trim();
|
|
19612
19812
|
if (!head.startsWith("ref:")) {
|
|
19613
19813
|
return head;
|
|
19614
19814
|
}
|
|
19615
19815
|
const refPath = head.replace("ref:", "").trim();
|
|
19616
|
-
const refFile =
|
|
19617
|
-
if (
|
|
19618
|
-
return
|
|
19816
|
+
const refFile = path9.join(gitDir, refPath);
|
|
19817
|
+
if (fs8.existsSync(refFile)) {
|
|
19818
|
+
return fs8.readFileSync(refFile, "utf8").trim();
|
|
19619
19819
|
}
|
|
19620
|
-
const packedRefs =
|
|
19621
|
-
if (
|
|
19622
|
-
const content =
|
|
19820
|
+
const packedRefs = path9.join(gitDir, "packed-refs");
|
|
19821
|
+
if (fs8.existsSync(packedRefs)) {
|
|
19822
|
+
const content = fs8.readFileSync(packedRefs, "utf8");
|
|
19623
19823
|
for (const line of content.split("\n")) {
|
|
19624
19824
|
if (!line || line.startsWith("#") || line.startsWith("^")) continue;
|
|
19625
19825
|
const [sha, ref] = line.split(" ");
|
|
@@ -19634,19 +19834,19 @@ function readGitSha(cwd = process.cwd()) {
|
|
|
19634
19834
|
function findGitDir(start) {
|
|
19635
19835
|
let current = start;
|
|
19636
19836
|
while (true) {
|
|
19637
|
-
const candidate =
|
|
19638
|
-
if (
|
|
19639
|
-
const stat =
|
|
19837
|
+
const candidate = path9.join(current, ".git");
|
|
19838
|
+
if (fs8.existsSync(candidate)) {
|
|
19839
|
+
const stat = fs8.statSync(candidate);
|
|
19640
19840
|
if (stat.isFile()) {
|
|
19641
|
-
const content =
|
|
19841
|
+
const content = fs8.readFileSync(candidate, "utf8").trim();
|
|
19642
19842
|
const match = content.match(/^gitdir: (.+)$/);
|
|
19643
19843
|
if (match) {
|
|
19644
|
-
return
|
|
19844
|
+
return path9.resolve(current, match[1]);
|
|
19645
19845
|
}
|
|
19646
19846
|
}
|
|
19647
19847
|
return candidate;
|
|
19648
19848
|
}
|
|
19649
|
-
const parent =
|
|
19849
|
+
const parent = path9.dirname(current);
|
|
19650
19850
|
if (parent === current) return void 0;
|
|
19651
19851
|
current = parent;
|
|
19652
19852
|
}
|
|
@@ -19657,8 +19857,8 @@ function readBranchName(cwd = process.cwd()) {
|
|
|
19657
19857
|
const gitDir = findGitDir(cwd);
|
|
19658
19858
|
if (!gitDir) return void 0;
|
|
19659
19859
|
try {
|
|
19660
|
-
const headPath =
|
|
19661
|
-
const head =
|
|
19860
|
+
const headPath = path9.join(gitDir, "HEAD");
|
|
19861
|
+
const head = fs8.readFileSync(headPath, "utf8").trim();
|
|
19662
19862
|
if (head.startsWith("ref:")) {
|
|
19663
19863
|
const refPath = head.replace("ref:", "").trim();
|
|
19664
19864
|
const match = refPath.match(/^refs\/heads\/(.+)$/);
|
|
@@ -19695,8 +19895,8 @@ function nanosecondsToMs(ns) {
|
|
|
19695
19895
|
}
|
|
19696
19896
|
|
|
19697
19897
|
// src/utils/metadata.ts
|
|
19698
|
-
import * as
|
|
19699
|
-
import * as
|
|
19898
|
+
import * as fs9 from "fs";
|
|
19899
|
+
import * as path10 from "path";
|
|
19700
19900
|
var versionCache = /* @__PURE__ */ new Map();
|
|
19701
19901
|
function readPackageVersion(root) {
|
|
19702
19902
|
if (versionCache.has(root)) {
|
|
@@ -19707,18 +19907,18 @@ function readPackageVersion(root) {
|
|
|
19707
19907
|
return version;
|
|
19708
19908
|
}
|
|
19709
19909
|
function findPackageVersion(startDir) {
|
|
19710
|
-
let current =
|
|
19910
|
+
let current = path10.resolve(startDir);
|
|
19711
19911
|
while (true) {
|
|
19712
|
-
const pkgPath =
|
|
19912
|
+
const pkgPath = path10.join(current, "package.json");
|
|
19713
19913
|
try {
|
|
19714
|
-
if (
|
|
19715
|
-
const raw =
|
|
19914
|
+
if (fs9.existsSync(pkgPath)) {
|
|
19915
|
+
const raw = fs9.readFileSync(pkgPath, "utf8");
|
|
19716
19916
|
const parsed = JSON.parse(raw);
|
|
19717
19917
|
return parsed.version;
|
|
19718
19918
|
}
|
|
19719
19919
|
} catch {
|
|
19720
19920
|
}
|
|
19721
|
-
const parent =
|
|
19921
|
+
const parent = path10.dirname(current);
|
|
19722
19922
|
if (parent === current) {
|
|
19723
19923
|
return void 0;
|
|
19724
19924
|
}
|
|
@@ -21046,18 +21246,18 @@ function deriveChangeType(tags) {
|
|
|
21046
21246
|
}
|
|
21047
21247
|
return "unknown";
|
|
21048
21248
|
}
|
|
21049
|
-
function extensionOf(
|
|
21050
|
-
const base =
|
|
21249
|
+
function extensionOf(path13) {
|
|
21250
|
+
const base = path13.split("/").pop() ?? path13;
|
|
21051
21251
|
const dot = base.lastIndexOf(".");
|
|
21052
21252
|
return dot === -1 ? "" : base.slice(dot + 1).toLowerCase();
|
|
21053
21253
|
}
|
|
21054
|
-
function isTestFile(
|
|
21055
|
-
return TEST_INFIX.test(
|
|
21254
|
+
function isTestFile(path13) {
|
|
21255
|
+
return TEST_INFIX.test(path13);
|
|
21056
21256
|
}
|
|
21057
|
-
function isReviewableSource(
|
|
21058
|
-
if (isTestFile(
|
|
21059
|
-
if (
|
|
21060
|
-
return CODE_EXTENSIONS.has(extensionOf(
|
|
21257
|
+
function isReviewableSource(path13) {
|
|
21258
|
+
if (isTestFile(path13)) return false;
|
|
21259
|
+
if (path13.endsWith(".d.ts")) return false;
|
|
21260
|
+
return CODE_EXTENSIONS.has(extensionOf(path13));
|
|
21061
21261
|
}
|
|
21062
21262
|
function testBaseKey(testFile) {
|
|
21063
21263
|
return testFile.replace(TEST_INFIX, "");
|
|
@@ -21161,7 +21361,7 @@ function toClaim(testCase, changedSourcePaths) {
|
|
|
21161
21361
|
const { strength, reasons } = gradeEvidence(testCase, audience);
|
|
21162
21362
|
const key = testBaseKey(testCase.sourceFile);
|
|
21163
21363
|
const coversFiles = changedSourcePaths.filter(
|
|
21164
|
-
(
|
|
21364
|
+
(path13) => sourceBaseKey(path13) === key
|
|
21165
21365
|
);
|
|
21166
21366
|
return {
|
|
21167
21367
|
id: testCase.id,
|
|
@@ -21410,7 +21610,7 @@ var ReviewMarkdownFormatter = class {
|
|
|
21410
21610
|
};
|
|
21411
21611
|
|
|
21412
21612
|
// src/formatters/review-html.ts
|
|
21413
|
-
function
|
|
21613
|
+
function escapeHtml4(value) {
|
|
21414
21614
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
21415
21615
|
}
|
|
21416
21616
|
var STRENGTH_LABEL = {
|
|
@@ -21432,22 +21632,22 @@ function statusIcon3(status) {
|
|
|
21432
21632
|
}
|
|
21433
21633
|
}
|
|
21434
21634
|
function formatStep3(step) {
|
|
21435
|
-
return `<li><strong>${
|
|
21635
|
+
return `<li><strong>${escapeHtml4(step.keyword)}</strong> ${escapeHtml4(step.text)}</li>`;
|
|
21436
21636
|
}
|
|
21437
21637
|
function inlineDoc(doc) {
|
|
21438
21638
|
switch (doc.kind) {
|
|
21439
21639
|
case "note":
|
|
21440
|
-
return
|
|
21640
|
+
return escapeHtml4(doc.text);
|
|
21441
21641
|
case "section":
|
|
21442
|
-
return `<strong>${
|
|
21642
|
+
return `<strong>${escapeHtml4(doc.title)}</strong>: ${escapeHtml4(doc.markdown)}`;
|
|
21443
21643
|
case "kv":
|
|
21444
|
-
return `${
|
|
21644
|
+
return `${escapeHtml4(doc.label)}: ${escapeHtml4(String(doc.value))}`;
|
|
21445
21645
|
case "code":
|
|
21446
|
-
return `${
|
|
21646
|
+
return `${escapeHtml4(doc.label)}: <code>${escapeHtml4(doc.content)}</code>`;
|
|
21447
21647
|
case "link":
|
|
21448
|
-
return `${
|
|
21648
|
+
return `${escapeHtml4(doc.label)}: ${escapeHtml4(doc.url)}`;
|
|
21449
21649
|
default:
|
|
21450
|
-
return
|
|
21650
|
+
return escapeHtml4(doc.kind);
|
|
21451
21651
|
}
|
|
21452
21652
|
}
|
|
21453
21653
|
function renderEvidenceArtifacts(testCase) {
|
|
@@ -21455,7 +21655,7 @@ function renderEvidenceArtifacts(testCase) {
|
|
|
21455
21655
|
for (const att of testCase.attachments) {
|
|
21456
21656
|
if (att.mediaType.startsWith("image/") && att.contentEncoding === "BASE64") {
|
|
21457
21657
|
parts.push(
|
|
21458
|
-
`<img class="shot" alt="${
|
|
21658
|
+
`<img class="shot" alt="${escapeHtml4(att.name)}" src="data:${escapeHtml4(att.mediaType)};base64,${att.body}" />`
|
|
21459
21659
|
);
|
|
21460
21660
|
}
|
|
21461
21661
|
}
|
|
@@ -21470,22 +21670,22 @@ function renderTicketPills(claim) {
|
|
|
21470
21670
|
const tickets = claim.testCase.story.tickets ?? [];
|
|
21471
21671
|
if (tickets.length === 0) return "";
|
|
21472
21672
|
return `<div class="ticket-row">${tickets.map((ticket) => {
|
|
21473
|
-
const label =
|
|
21673
|
+
const label = escapeHtml4(ticket.id);
|
|
21474
21674
|
if (ticket.url) {
|
|
21475
|
-
return `<a class="ticket-pill" href="${
|
|
21675
|
+
return `<a class="ticket-pill" href="${escapeHtml4(ticket.url)}" target="_blank" rel="noopener noreferrer">${label}</a>`;
|
|
21476
21676
|
}
|
|
21477
21677
|
return `<span class="ticket-pill">${label}</span>`;
|
|
21478
21678
|
}).join("")}</div>`;
|
|
21479
21679
|
}
|
|
21480
21680
|
function renderClaimCard(claim) {
|
|
21481
21681
|
const ticketSearch = (claim.testCase.story.tickets ?? []).map((ticket) => ticket.id).join(" ");
|
|
21482
|
-
const search =
|
|
21682
|
+
const search = escapeHtml4(
|
|
21483
21683
|
`${claim.scenario} ${claim.sourceFile} ${claim.changeType} ${claim.audience} ${claim.strength} ${ticketSearch}`
|
|
21484
21684
|
).toLowerCase();
|
|
21485
21685
|
const steps = claim.testCase.story.steps.length > 0 ? `<ul class="step-list">${claim.testCase.story.steps.map(formatStep3).join("")}</ul>` : "";
|
|
21486
|
-
const reasons = `<ul class="reasons">${claim.strengthReasons.map((r) => `<li>${
|
|
21487
|
-
const intent = claim.intent !== void 0 ? `<div class="intent"><span class="intent-label">Why</span> ${
|
|
21488
|
-
const covers = claim.coversFiles.length > 0 ? `<p class="covers">Covers ${claim.coversFiles.map((f) => `<code>${
|
|
21686
|
+
const reasons = `<ul class="reasons">${claim.strengthReasons.map((r) => `<li>${escapeHtml4(r)}</li>`).join("")}</ul>`;
|
|
21687
|
+
const intent = claim.intent !== void 0 ? `<div class="intent"><span class="intent-label">Why</span> ${escapeHtml4(claim.intent)}</div>` : "";
|
|
21688
|
+
const covers = claim.coversFiles.length > 0 ? `<p class="covers">Covers ${claim.coversFiles.map((f) => `<code>${escapeHtml4(f)}</code>`).join(", ")}</p>` : "";
|
|
21489
21689
|
const docs = (claim.testCase.story.docs ?? []).filter(
|
|
21490
21690
|
(d) => d.kind === "section" || d.kind === "note"
|
|
21491
21691
|
);
|
|
@@ -21495,9 +21695,9 @@ function renderClaimCard(claim) {
|
|
|
21495
21695
|
<header class="claim-header">
|
|
21496
21696
|
<div>
|
|
21497
21697
|
<span class="strength-badge strength-${claim.strength}">${STRENGTH_LABEL[claim.strength]}</span>
|
|
21498
|
-
${claim.changeType !== "unknown" ? `<span class="change-pill">${
|
|
21499
|
-
<h3>${statusIcon3(claim.status)} ${
|
|
21500
|
-
<p class="source">${
|
|
21698
|
+
${claim.changeType !== "unknown" ? `<span class="change-pill">${escapeHtml4(claim.changeType)}</span>` : ""}
|
|
21699
|
+
<h3>${statusIcon3(claim.status)} ${escapeHtml4(claim.scenario)}</h3>
|
|
21700
|
+
<p class="source">${escapeHtml4(`${claim.sourceFile}:${claim.sourceLine}`)}</p>
|
|
21501
21701
|
${renderTicketPills(claim)}
|
|
21502
21702
|
</div>
|
|
21503
21703
|
</header>
|
|
@@ -21512,18 +21712,18 @@ function renderClaimCard(claim) {
|
|
|
21512
21712
|
</article>`;
|
|
21513
21713
|
}
|
|
21514
21714
|
function renderChangedFileRow(file) {
|
|
21515
|
-
const claims = file.claims.length > 0 ? file.claims.map((c) => `${
|
|
21715
|
+
const claims = file.claims.length > 0 ? file.claims.map((c) => `${escapeHtml4(c.scenario)} <em>(${c.strength})</em>`).join(", ") : "\u2014";
|
|
21516
21716
|
return `<tr data-band="${file.band}">
|
|
21517
21717
|
<td><span class="band-dot band-${file.band}"></span></td>
|
|
21518
|
-
<td><code>${
|
|
21519
|
-
<td>${
|
|
21718
|
+
<td><code>${escapeHtml4(file.path)}</code></td>
|
|
21719
|
+
<td>${escapeHtml4(file.changeKind)}</td>
|
|
21520
21720
|
<td>${claims}</td>
|
|
21521
21721
|
</tr>`;
|
|
21522
21722
|
}
|
|
21523
21723
|
function renderAudienceSection2(title, claims) {
|
|
21524
21724
|
if (claims.length === 0) return "";
|
|
21525
21725
|
return `<section class="audience-section">
|
|
21526
|
-
<h2>${
|
|
21726
|
+
<h2>${escapeHtml4(title)} <span class="count">${claims.length}</span></h2>
|
|
21527
21727
|
<div class="claim-list">${claims.map(renderClaimCard).join("\n")}</div>
|
|
21528
21728
|
</section>`;
|
|
21529
21729
|
}
|
|
@@ -21613,13 +21813,13 @@ var ReviewHtmlFormatter = class {
|
|
|
21613
21813
|
const themeInitJs = this.darkMode ? `${JS_THEME_TOGGLE2}
|
|
21614
21814
|
applyTheme(getEffectiveTheme());` : "";
|
|
21615
21815
|
const themeAttr = this.darkMode ? ' data-theme="light"' : "";
|
|
21616
|
-
const refsLine = context.baseRef || context.headRef ? `<p class="subtle">Comparing ${
|
|
21816
|
+
const refsLine = context.baseRef || context.headRef ? `<p class="subtle">Comparing ${escapeHtml4(context.baseRef ?? "base")} \u2192 ${escapeHtml4(context.headRef ?? "head")}</p>` : "";
|
|
21617
21817
|
return `<!doctype html>
|
|
21618
21818
|
<html lang="en"${themeAttr}>
|
|
21619
21819
|
<head>
|
|
21620
21820
|
<meta charset="utf-8" />
|
|
21621
21821
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
21622
|
-
<title>${
|
|
21822
|
+
<title>${escapeHtml4(this.title)}</title>
|
|
21623
21823
|
<style>
|
|
21624
21824
|
${this.theme.css}
|
|
21625
21825
|
${REVIEW_CSS}
|
|
@@ -21629,7 +21829,7 @@ applyTheme(getEffectiveTheme());` : "";
|
|
|
21629
21829
|
<main>
|
|
21630
21830
|
<div class="hero-card card">
|
|
21631
21831
|
<div class="review-header">
|
|
21632
|
-
<h1>${
|
|
21832
|
+
<h1>${escapeHtml4(this.title)}</h1>
|
|
21633
21833
|
${themeToggleHtml}
|
|
21634
21834
|
</div>
|
|
21635
21835
|
${refsLine}
|
|
@@ -21644,7 +21844,7 @@ applyTheme(getEffectiveTheme());` : "";
|
|
|
21644
21844
|
</section>
|
|
21645
21845
|
<section class="card priority-banner">
|
|
21646
21846
|
<h2>Review priority</h2>
|
|
21647
|
-
<p class="subtle">${
|
|
21847
|
+
<p class="subtle">${escapeHtml4(priority)}</p>
|
|
21648
21848
|
</section>
|
|
21649
21849
|
${changedFilesPanel}
|
|
21650
21850
|
<section class="toolbar">
|
|
@@ -21692,8 +21892,8 @@ applyTheme(getEffectiveTheme());` : "";
|
|
|
21692
21892
|
};
|
|
21693
21893
|
|
|
21694
21894
|
// src/deploy/ledger.ts
|
|
21695
|
-
import * as
|
|
21696
|
-
import * as
|
|
21895
|
+
import * as fs10 from "fs";
|
|
21896
|
+
import * as path11 from "path";
|
|
21697
21897
|
function createEmptyLedger() {
|
|
21698
21898
|
return {
|
|
21699
21899
|
deployments: [],
|
|
@@ -21701,12 +21901,12 @@ function createEmptyLedger() {
|
|
|
21701
21901
|
};
|
|
21702
21902
|
}
|
|
21703
21903
|
function loadLedger(ledgerPath) {
|
|
21704
|
-
const resolved =
|
|
21705
|
-
if (!
|
|
21904
|
+
const resolved = path11.resolve(ledgerPath);
|
|
21905
|
+
if (!fs10.existsSync(resolved)) {
|
|
21706
21906
|
return createEmptyLedger();
|
|
21707
21907
|
}
|
|
21708
21908
|
try {
|
|
21709
|
-
const raw = JSON.parse(
|
|
21909
|
+
const raw = JSON.parse(fs10.readFileSync(resolved, "utf8"));
|
|
21710
21910
|
if (raw.schemaVersion !== 1) {
|
|
21711
21911
|
throw new Error(`Unsupported ledger schemaVersion: ${raw.schemaVersion}`);
|
|
21712
21912
|
}
|
|
@@ -21717,10 +21917,10 @@ function loadLedger(ledgerPath) {
|
|
|
21717
21917
|
}
|
|
21718
21918
|
}
|
|
21719
21919
|
function saveLedger(ledger, ledgerPath) {
|
|
21720
|
-
const resolved =
|
|
21721
|
-
const dir =
|
|
21722
|
-
|
|
21723
|
-
|
|
21920
|
+
const resolved = path11.resolve(ledgerPath);
|
|
21921
|
+
const dir = path11.dirname(resolved);
|
|
21922
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
21923
|
+
fs10.writeFileSync(resolved, JSON.stringify(ledger, null, 2), "utf8");
|
|
21724
21924
|
}
|
|
21725
21925
|
function getLatestDeployment(ledger, environment) {
|
|
21726
21926
|
return [...ledger.deployments].reverse().find((d) => d.environment === environment);
|
|
@@ -21840,11 +22040,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
21840
22040
|
const ext = FORMAT_EXTENSIONS[format];
|
|
21841
22041
|
const effectiveName = outputName + (outputNameSuffix ?? "");
|
|
21842
22042
|
if (mode === "aggregated") {
|
|
21843
|
-
return toPosix(
|
|
22043
|
+
return toPosix(path12.join(baseOutputDir, joinNameAndExt(effectiveName, ext)));
|
|
21844
22044
|
}
|
|
21845
22045
|
const normalizedSource = toPosix(sourceFile);
|
|
21846
|
-
const dirOfSource =
|
|
21847
|
-
let baseName =
|
|
22046
|
+
const dirOfSource = path12.posix.dirname(normalizedSource);
|
|
22047
|
+
let baseName = path12.posix.basename(normalizedSource);
|
|
21848
22048
|
for (const testExt of TEST_EXTENSIONS) {
|
|
21849
22049
|
if (baseName.endsWith(testExt)) {
|
|
21850
22050
|
baseName = baseName.slice(0, -testExt.length);
|
|
@@ -21853,12 +22053,12 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
21853
22053
|
}
|
|
21854
22054
|
const fileName = `${baseName}.${effectiveName}${ext}`;
|
|
21855
22055
|
if (colocatedStyle === "adjacent") {
|
|
21856
|
-
return toPosix(
|
|
22056
|
+
return toPosix(path12.posix.join(dirOfSource, fileName));
|
|
21857
22057
|
}
|
|
21858
22058
|
if (colocatedStyle === "flat") {
|
|
21859
|
-
return toPosix(
|
|
22059
|
+
return toPosix(path12.posix.join(baseOutputDir, `${cleanTestStem(normalizedSource)}${ext}`));
|
|
21860
22060
|
}
|
|
21861
|
-
return toPosix(
|
|
22061
|
+
return toPosix(path12.posix.join(baseOutputDir, dirOfSource, fileName));
|
|
21862
22062
|
}
|
|
21863
22063
|
function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
|
|
21864
22064
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -22071,8 +22271,8 @@ var ReportGenerator = class {
|
|
|
22071
22271
|
if (astroPaths) {
|
|
22072
22272
|
for (const mdPath of astroPaths) {
|
|
22073
22273
|
const content = await fsPromises.readFile(mdPath, "utf8");
|
|
22074
|
-
const mdDir =
|
|
22075
|
-
const assetsDir =
|
|
22274
|
+
const mdDir = path12.dirname(mdPath);
|
|
22275
|
+
const assetsDir = path12.resolve(this.options.astro.assetsDir);
|
|
22076
22276
|
const result = copyMarkdownAssets({
|
|
22077
22277
|
markdown: content,
|
|
22078
22278
|
markdownDir: mdDir,
|
|
@@ -22103,9 +22303,9 @@ var ReportGenerator = class {
|
|
|
22103
22303
|
if (groups.size === 0 && this.options.output.mode === "aggregated") {
|
|
22104
22304
|
const ext = FORMAT_EXTENSIONS[format];
|
|
22105
22305
|
const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
|
|
22106
|
-
const outputPath = toPosix(
|
|
22306
|
+
const outputPath = toPosix(path12.join(this.options.outputDir, joinNameAndExt(effectiveName, ext)));
|
|
22107
22307
|
const content = await this.formatContent(run, format);
|
|
22108
|
-
const dir =
|
|
22308
|
+
const dir = path12.dirname(outputPath);
|
|
22109
22309
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
22110
22310
|
await this.deps.writeFile(outputPath, content);
|
|
22111
22311
|
return [outputPath];
|
|
@@ -22117,7 +22317,7 @@ var ReportGenerator = class {
|
|
|
22117
22317
|
testCases
|
|
22118
22318
|
};
|
|
22119
22319
|
const content = await this.formatContent(groupRun, format);
|
|
22120
|
-
const dir =
|
|
22320
|
+
const dir = path12.dirname(outputPath);
|
|
22121
22321
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
22122
22322
|
await this.deps.writeFile(outputPath, content);
|
|
22123
22323
|
writtenPaths.push(outputPath);
|
|
@@ -22269,7 +22469,7 @@ async function generateRunComparison(args) {
|
|
|
22269
22469
|
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
22270
22470
|
for (const format of args.formats) {
|
|
22271
22471
|
const ext = format === "html" ? ".html" : ".md";
|
|
22272
|
-
const outputPath = toPosix(
|
|
22472
|
+
const outputPath = toPosix(path12.join(outputDir, `${outputName}${ext}`));
|
|
22273
22473
|
const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
|
|
22274
22474
|
await fsPromises.writeFile(outputPath, content, "utf8");
|
|
22275
22475
|
files.push(outputPath);
|
|
@@ -22318,6 +22518,7 @@ export {
|
|
|
22318
22518
|
adaptJestRun,
|
|
22319
22519
|
adaptPlaywrightRun,
|
|
22320
22520
|
adaptVitestRun,
|
|
22521
|
+
advanceState,
|
|
22321
22522
|
assertValidRun,
|
|
22322
22523
|
buildCheck,
|
|
22323
22524
|
buildGoal,
|
|
@@ -22330,6 +22531,7 @@ export {
|
|
|
22330
22531
|
canonicalizeRun,
|
|
22331
22532
|
classifyStatusChange,
|
|
22332
22533
|
clearVersionCache,
|
|
22534
|
+
computeDeltas,
|
|
22333
22535
|
computeTestMetrics,
|
|
22334
22536
|
copyMarkdownAssets,
|
|
22335
22537
|
createPrCommentSummary,
|
|
@@ -22352,6 +22554,7 @@ export {
|
|
|
22352
22554
|
getEnvironmentDrift,
|
|
22353
22555
|
gradeEvidence,
|
|
22354
22556
|
hasSufficientHistory,
|
|
22557
|
+
injectLiveBits,
|
|
22355
22558
|
isReviewableSource,
|
|
22356
22559
|
isTestFile,
|
|
22357
22560
|
joinNameAndExt,
|
|
@@ -22373,7 +22576,9 @@ export {
|
|
|
22373
22576
|
readPackageVersion,
|
|
22374
22577
|
recordDeployment,
|
|
22375
22578
|
regenerateArtifacts,
|
|
22579
|
+
regenerateRun,
|
|
22376
22580
|
renderCheck,
|
|
22581
|
+
renderDeltaStrip,
|
|
22377
22582
|
renderGoal,
|
|
22378
22583
|
renderTriage,
|
|
22379
22584
|
resolveAttachment,
|
|
@@ -22389,6 +22594,7 @@ export {
|
|
|
22389
22594
|
sendWebhookNotification,
|
|
22390
22595
|
signBody,
|
|
22391
22596
|
slugify,
|
|
22597
|
+
startServe,
|
|
22392
22598
|
startWatch,
|
|
22393
22599
|
stripAnsi,
|
|
22394
22600
|
toBehaviorManifest,
|