@automaze/proof 0.20260311.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/LICENSE +202 -0
- package/README.md +221 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +934 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +200 -0
- package/dist/detect.d.ts +2 -0
- package/dist/detect.js +31 -0
- package/dist/detect.test.d.ts +1 -0
- package/dist/detect.test.js +54 -0
- package/dist/duration.d.ts +1 -0
- package/dist/duration.js +23 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +843 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +261 -0
- package/dist/modes/terminal.d.ts +5 -0
- package/dist/modes/terminal.js +287 -0
- package/dist/modes/visual.d.ts +10 -0
- package/dist/modes/visual.js +165 -0
- package/dist/report.d.ts +2 -0
- package/dist/report.js +196 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.js +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { readdir, rm, copyFile, mkdir } from "fs/promises";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import { getVideoDuration } from "../duration";
|
|
6
|
+
const CURSOR_HIGHLIGHT_SCRIPT = `
|
|
7
|
+
(function() {
|
|
8
|
+
function init() {
|
|
9
|
+
if (document.getElementById('__proof_cursor')) return;
|
|
10
|
+
const cursor = document.createElement('div');
|
|
11
|
+
cursor.id = '__proof_cursor';
|
|
12
|
+
cursor.style.cssText = [
|
|
13
|
+
'position: fixed',
|
|
14
|
+
'width: 20px',
|
|
15
|
+
'height: 20px',
|
|
16
|
+
'border-radius: 50%',
|
|
17
|
+
'background: rgba(255, 50, 50, 0.8)',
|
|
18
|
+
'border: 2px solid rgba(255, 255, 255, 0.95)',
|
|
19
|
+
'pointer-events: none',
|
|
20
|
+
'z-index: 2147483647',
|
|
21
|
+
'transform: translate(-50%, -50%)',
|
|
22
|
+
'box-shadow: 0 0 8px rgba(255,50,50,0.4), 0 0 2px rgba(0,0,0,0.3)',
|
|
23
|
+
'display: none',
|
|
24
|
+
'transition: left 0.08s ease-out, top 0.08s ease-out',
|
|
25
|
+
].join(';');
|
|
26
|
+
document.documentElement.appendChild(cursor);
|
|
27
|
+
|
|
28
|
+
function showCursor(x, y) {
|
|
29
|
+
cursor.style.left = x + 'px';
|
|
30
|
+
cursor.style.top = y + 'px';
|
|
31
|
+
cursor.style.display = 'block';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function spawnRipple(x, y) {
|
|
35
|
+
const ripple = document.createElement('div');
|
|
36
|
+
ripple.style.cssText = [
|
|
37
|
+
'position: fixed',
|
|
38
|
+
'pointer-events: none',
|
|
39
|
+
'z-index: 2147483646',
|
|
40
|
+
'border-radius: 50%',
|
|
41
|
+
'border: 3px solid rgba(255, 50, 50, 0.7)',
|
|
42
|
+
'width: 10px',
|
|
43
|
+
'height: 10px',
|
|
44
|
+
'left: ' + x + 'px',
|
|
45
|
+
'top: ' + y + 'px',
|
|
46
|
+
'transform: translate(-50%, -50%) scale(1)',
|
|
47
|
+
'opacity: 1',
|
|
48
|
+
'transition: transform 0.5s ease-out, opacity 0.5s ease-out',
|
|
49
|
+
].join(';');
|
|
50
|
+
document.documentElement.appendChild(ripple);
|
|
51
|
+
requestAnimationFrame(() => {
|
|
52
|
+
ripple.style.transform = 'translate(-50%, -50%) scale(5)';
|
|
53
|
+
ripple.style.opacity = '0';
|
|
54
|
+
});
|
|
55
|
+
setTimeout(() => ripple.remove(), 600);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const evt of ['mousemove', 'pointermove']) {
|
|
59
|
+
document.addEventListener(evt, (e) => showCursor(e.clientX, e.clientY), true);
|
|
60
|
+
window.addEventListener(evt, (e) => showCursor(e.clientX, e.clientY), true);
|
|
61
|
+
}
|
|
62
|
+
for (const evt of ['mousedown', 'pointerdown', 'click']) {
|
|
63
|
+
document.addEventListener(evt, (e) => {
|
|
64
|
+
showCursor(e.clientX, e.clientY);
|
|
65
|
+
spawnRipple(e.clientX, e.clientY);
|
|
66
|
+
}, true);
|
|
67
|
+
window.addEventListener(evt, (e) => {
|
|
68
|
+
showCursor(e.clientX, e.clientY);
|
|
69
|
+
spawnRipple(e.clientX, e.clientY);
|
|
70
|
+
}, true);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (document.readyState === 'loading') {
|
|
75
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
76
|
+
} else {
|
|
77
|
+
init();
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
`;
|
|
81
|
+
export async function captureVisual(options, runDir, filePrefix, config) {
|
|
82
|
+
const label = options.label ?? "recording";
|
|
83
|
+
const testFile = options.testFile;
|
|
84
|
+
const testDir = dirname(testFile);
|
|
85
|
+
const configCandidates = [
|
|
86
|
+
join(testDir, "playwright.config.ts"),
|
|
87
|
+
join(testDir, "playwright.config.js"),
|
|
88
|
+
join(testDir, "playwright.config.mjs"),
|
|
89
|
+
];
|
|
90
|
+
const existingConfig = configCandidates.find((c) => existsSync(c));
|
|
91
|
+
const testResultsDir = join(runDir, "pw-results");
|
|
92
|
+
await mkdir(testResultsDir, { recursive: true });
|
|
93
|
+
const testArgs = [
|
|
94
|
+
"playwright", "test", testFile,
|
|
95
|
+
"--reporter=list",
|
|
96
|
+
"--output", testResultsDir,
|
|
97
|
+
];
|
|
98
|
+
if (options.testName) {
|
|
99
|
+
testArgs.push("-g", options.testName);
|
|
100
|
+
}
|
|
101
|
+
if (existingConfig) {
|
|
102
|
+
testArgs.push("--config", existingConfig);
|
|
103
|
+
}
|
|
104
|
+
const env = {
|
|
105
|
+
...process.env,
|
|
106
|
+
PWDEBUG: "0",
|
|
107
|
+
PLAYWRIGHT_VIDEO: "on",
|
|
108
|
+
};
|
|
109
|
+
let exitCode = null;
|
|
110
|
+
let stderr = "";
|
|
111
|
+
await new Promise((resolve) => {
|
|
112
|
+
const proc = spawn("npx", testArgs, {
|
|
113
|
+
stdio: "pipe",
|
|
114
|
+
env,
|
|
115
|
+
cwd: testDir,
|
|
116
|
+
});
|
|
117
|
+
proc.stderr.on("data", (chunk) => { stderr += chunk.toString(); });
|
|
118
|
+
proc.on("close", (code) => {
|
|
119
|
+
exitCode = code;
|
|
120
|
+
resolve();
|
|
121
|
+
});
|
|
122
|
+
proc.on("error", (err) => {
|
|
123
|
+
throw new Error(`Failed to run Playwright: ${err.message}. Is @playwright/test installed?`);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
const videoFile = await findVideo(testResultsDir);
|
|
127
|
+
if (!videoFile) {
|
|
128
|
+
const hint = stderr.trim() ? `\nPlaywright output:\n${stderr.trim().split("\n").slice(-5).join("\n")}` : "";
|
|
129
|
+
throw new Error(`No video file found after Playwright test run. Check that video: 'on' is set in playwright config.${hint}`);
|
|
130
|
+
}
|
|
131
|
+
const ext = videoFile.endsWith(".webm") ? ".webm" : ".mp4";
|
|
132
|
+
const finalPath = join(runDir, `${filePrefix}${ext}`);
|
|
133
|
+
await copyFile(videoFile, finalPath);
|
|
134
|
+
await rm(testResultsDir, { recursive: true, force: true });
|
|
135
|
+
const duration = await getVideoDuration(finalPath);
|
|
136
|
+
return {
|
|
137
|
+
path: finalPath,
|
|
138
|
+
mode: "browser",
|
|
139
|
+
duration,
|
|
140
|
+
label,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async function findVideo(resultsDir) {
|
|
144
|
+
if (!existsSync(resultsDir))
|
|
145
|
+
return null;
|
|
146
|
+
const entries = await readdir(resultsDir, { withFileTypes: true });
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
if (!entry.isDirectory() && (entry.name.endsWith(".webm") || entry.name.endsWith(".mp4"))) {
|
|
149
|
+
return join(resultsDir, entry.name);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
for (const entry of entries) {
|
|
153
|
+
if (!entry.isDirectory())
|
|
154
|
+
continue;
|
|
155
|
+
const subDir = join(resultsDir, entry.name);
|
|
156
|
+
const files = await readdir(subDir);
|
|
157
|
+
const video = files.find((f) => f.endsWith(".webm") || f.endsWith(".mp4"));
|
|
158
|
+
if (video)
|
|
159
|
+
return join(subDir, video);
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
export function getCursorHighlightScript() {
|
|
164
|
+
return CURSOR_HIGHLIGHT_SCRIPT;
|
|
165
|
+
}
|
package/dist/report.d.ts
ADDED
package/dist/report.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { readFile, writeFile } from "fs/promises";
|
|
2
|
+
import { join, basename } from "path";
|
|
3
|
+
export async function generateReport(manifest, runDir, format) {
|
|
4
|
+
switch (format) {
|
|
5
|
+
case "md":
|
|
6
|
+
return generateMd(manifest, runDir);
|
|
7
|
+
case "html":
|
|
8
|
+
return generateHtml(manifest, runDir, false);
|
|
9
|
+
case "archive":
|
|
10
|
+
return generateHtml(manifest, runDir, true);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function formatTime(iso) {
|
|
14
|
+
return new Date(iso).toLocaleTimeString("en-US", { hour12: false });
|
|
15
|
+
}
|
|
16
|
+
function formatDate(iso) {
|
|
17
|
+
return new Date(iso).toLocaleDateString("en-US", {
|
|
18
|
+
year: "numeric",
|
|
19
|
+
month: "long",
|
|
20
|
+
day: "numeric",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function formatDuration(ms) {
|
|
24
|
+
if (ms < 1000)
|
|
25
|
+
return `${ms}ms`;
|
|
26
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
27
|
+
}
|
|
28
|
+
function modeIcon(mode) {
|
|
29
|
+
return mode === "browser" ? "🌐" : "🖥";
|
|
30
|
+
}
|
|
31
|
+
// --- Markdown ---
|
|
32
|
+
function generateMd(manifest, runDir) {
|
|
33
|
+
const lines = [];
|
|
34
|
+
const totalDuration = manifest.entries.reduce((sum, e) => sum + e.duration, 0);
|
|
35
|
+
lines.push(`# Proof Report`);
|
|
36
|
+
lines.push(``);
|
|
37
|
+
lines.push(`**App:** ${manifest.appName}`);
|
|
38
|
+
lines.push(`**Run:** ${manifest.run}`);
|
|
39
|
+
lines.push(`**Date:** ${formatDate(manifest.createdAt)}`);
|
|
40
|
+
lines.push(`**Entries:** ${manifest.entries.length}`);
|
|
41
|
+
if (manifest.description) {
|
|
42
|
+
lines.push(``);
|
|
43
|
+
lines.push(manifest.description);
|
|
44
|
+
}
|
|
45
|
+
lines.push(``);
|
|
46
|
+
lines.push(`| # | Label | Mode | Duration | Artifact |`);
|
|
47
|
+
lines.push(`|---|-------|------|----------|----------|`);
|
|
48
|
+
manifest.entries.forEach((entry, i) => {
|
|
49
|
+
const label = entry.label ?? entry.mode;
|
|
50
|
+
lines.push(`| ${i + 1} | ${modeIcon(entry.mode)} ${label} | ${entry.mode} | ${formatDuration(entry.duration)} | [${entry.artifact}](./${entry.artifact}) |`);
|
|
51
|
+
});
|
|
52
|
+
lines.push(``);
|
|
53
|
+
lines.push(`---`);
|
|
54
|
+
lines.push(``);
|
|
55
|
+
for (const entry of manifest.entries) {
|
|
56
|
+
lines.push(`### ${modeIcon(entry.mode)} ${entry.label ?? entry.mode}`);
|
|
57
|
+
lines.push(``);
|
|
58
|
+
lines.push(`${entry.description}`);
|
|
59
|
+
lines.push(``);
|
|
60
|
+
lines.push(`- **Time:** ${formatTime(entry.timestamp)}`);
|
|
61
|
+
lines.push(`- **Duration:** ${formatDuration(entry.duration)}`);
|
|
62
|
+
if (entry.command)
|
|
63
|
+
lines.push(`- **Command:** \`${entry.command}\``);
|
|
64
|
+
if (entry.testFile)
|
|
65
|
+
lines.push(`- **Test file:** \`${basename(entry.testFile)}\``);
|
|
66
|
+
if (entry.testName)
|
|
67
|
+
lines.push(`- **Test name:** ${entry.testName}`);
|
|
68
|
+
lines.push(`- **Artifact:** [${entry.artifact}](./${entry.artifact})`);
|
|
69
|
+
lines.push(``);
|
|
70
|
+
}
|
|
71
|
+
lines.push(`---`);
|
|
72
|
+
lines.push(`*Generated by [@automaze/proof](https://www.npmjs.com/package/@automaze/proof)*`);
|
|
73
|
+
const md = lines.join("\n");
|
|
74
|
+
const outPath = join(runDir, "report.md");
|
|
75
|
+
return writeFile(outPath, md, "utf-8").then(() => outPath);
|
|
76
|
+
}
|
|
77
|
+
// --- HTML (shared by html and archive) ---
|
|
78
|
+
async function generateHtml(manifest, runDir, inline) {
|
|
79
|
+
const totalDuration = manifest.entries.reduce((sum, e) => sum + e.duration, 0);
|
|
80
|
+
const terminalCount = manifest.entries.filter(e => e.mode === "terminal").length;
|
|
81
|
+
const browserCount = manifest.entries.filter(e => e.mode === "browser").length;
|
|
82
|
+
const sections = [];
|
|
83
|
+
for (const entry of manifest.entries) {
|
|
84
|
+
const mediaHtml = await buildMediaEmbed(entry, runDir, inline);
|
|
85
|
+
const label = entry.label ?? entry.mode;
|
|
86
|
+
const isTerminal = entry.mode === "terminal";
|
|
87
|
+
const badgeClass = isTerminal
|
|
88
|
+
? "text-green-800 bg-emerald-100/80"
|
|
89
|
+
: "text-blue-800 bg-sky-100";
|
|
90
|
+
const modeLabel = isTerminal ? "Terminal" : "Browser";
|
|
91
|
+
let sourceHtml = "";
|
|
92
|
+
if (entry.command) {
|
|
93
|
+
sourceHtml = `
|
|
94
|
+
<p class="font-medium text-sm mt-4 mb-1">Command:</p>
|
|
95
|
+
<div class="font-mono text-xs border px-1 rounded bg-neutral-50 border-neutral-200 subpixel-antialiased w-fit">${esc(entry.command)}</div>`;
|
|
96
|
+
}
|
|
97
|
+
else if (entry.testFile) {
|
|
98
|
+
sourceHtml = `
|
|
99
|
+
<p class="font-medium text-sm mt-4 mb-1">Test:</p>
|
|
100
|
+
<div class="font-mono text-xs border px-1 rounded bg-neutral-50 border-neutral-200 subpixel-antialiased w-fit">${esc(basename(entry.testFile))}</div>`;
|
|
101
|
+
}
|
|
102
|
+
sections.push(`
|
|
103
|
+
<section class="mt-12 grid-cols-12 gap-x-6 border-t border-neutral-200 pt-12 md:grid">
|
|
104
|
+
<div class="col-span-5">
|
|
105
|
+
<h2 class="text-2xl mb-2">${esc(label)}</h2>
|
|
106
|
+
<div class="flex items-center space-x-4 subpixel-antialiased">
|
|
107
|
+
<span class="rounded ${badgeClass} px-2 py-0.5 text-sm">${modeLabel}</span>
|
|
108
|
+
<time class="font-mono text-[13px]">${formatTime(entry.timestamp)} (${formatDuration(entry.duration)})</time>
|
|
109
|
+
</div>
|
|
110
|
+
<p class="opacity-80 my-4">${esc(entry.description)}</p>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="col-span-7">
|
|
113
|
+
${mediaHtml}
|
|
114
|
+
${sourceHtml}
|
|
115
|
+
</div>
|
|
116
|
+
</section>`);
|
|
117
|
+
}
|
|
118
|
+
const descriptionHtml = manifest.description
|
|
119
|
+
? `<p class="max-w-2xl leading-relaxed opacity-80">${esc(manifest.description)}</p>`
|
|
120
|
+
: "";
|
|
121
|
+
const badgesHtml = [
|
|
122
|
+
terminalCount > 0 ? `<span class="whitespace-nowrap rounded font-medium text-green-800 bg-emerald-100/80 px-2 py-0.5 text-sm">${terminalCount} Terminal</span>` : "",
|
|
123
|
+
browserCount > 0 ? `<span class="whitespace-nowrap rounded font-medium text-blue-800 bg-sky-100 px-2 py-0.5 text-sm">${browserCount} Browser</span>` : "",
|
|
124
|
+
].filter(Boolean).join("\n ");
|
|
125
|
+
const tailwindScript = inline
|
|
126
|
+
? `<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>`
|
|
127
|
+
: `<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>`;
|
|
128
|
+
const html = `<!DOCTYPE html>
|
|
129
|
+
<html lang="en">
|
|
130
|
+
<head>
|
|
131
|
+
<meta charset="utf-8">
|
|
132
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
133
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover">
|
|
134
|
+
<meta http-equiv="cleartype" content="on">
|
|
135
|
+
<title>Proof Report - ${esc(manifest.run)}</title>
|
|
136
|
+
${tailwindScript}
|
|
137
|
+
</head>
|
|
138
|
+
<body>
|
|
139
|
+
|
|
140
|
+
<div class="container mx-auto px-6 my-14 w-full max-w-5xl antialiased">
|
|
141
|
+
<header>
|
|
142
|
+
<div class="flex items-center space-x-4 mb-6">
|
|
143
|
+
<h1 class="text-4xl leading-none font-medium">Proof report</h1>
|
|
144
|
+
<span class="mt-2 rounded bg-neutral-100 px-2 py-1 font-mono text-sm leading-none">${esc(manifest.run)}</span>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
${descriptionHtml}
|
|
148
|
+
|
|
149
|
+
<div class="mt-6 md:flex md:items-center md:space-x-4 leading-none">
|
|
150
|
+
<span class="block mb-2 md:inline md:m-0">${formatDate(manifest.createdAt)} · ${formatTime(manifest.createdAt)} (${formatDuration(totalDuration)})</span>
|
|
151
|
+
${badgesHtml}
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<p class="my-6">Generated by <a href="https://www.npmjs.com/package/@automaze/proof" class="font-medium text-blue-600">@automaze/proof</a></p>
|
|
155
|
+
</header>
|
|
156
|
+
|
|
157
|
+
<main>
|
|
158
|
+
${sections.join("\n")}
|
|
159
|
+
</main>
|
|
160
|
+
|
|
161
|
+
<footer></footer>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
</body>
|
|
165
|
+
</html>`;
|
|
166
|
+
const ext = inline ? "archive.html" : "report.html";
|
|
167
|
+
const outPath = join(runDir, ext);
|
|
168
|
+
await writeFile(outPath, html, "utf-8");
|
|
169
|
+
return outPath;
|
|
170
|
+
}
|
|
171
|
+
async function buildMediaEmbed(entry, runDir, inline) {
|
|
172
|
+
const artifactPath = join(runDir, entry.artifact);
|
|
173
|
+
if (entry.mode === "browser") {
|
|
174
|
+
if (inline) {
|
|
175
|
+
const videoData = await readFile(artifactPath);
|
|
176
|
+
const ext = entry.artifact.endsWith(".mp4") ? "mp4" : "webm";
|
|
177
|
+
const b64 = videoData.toString("base64");
|
|
178
|
+
return `<video class="bg-black w-full shadow-xs rounded-md aspect-3/2" controls muted loop src="data:video/${ext};base64,${b64}"></video>`;
|
|
179
|
+
}
|
|
180
|
+
return `<video class="bg-neutral-200 w-full rounded-md aspect-3/2" controls muted loop src="./${esc(entry.artifact)}"></video>`;
|
|
181
|
+
}
|
|
182
|
+
if (entry.mode === "terminal") {
|
|
183
|
+
if (inline) {
|
|
184
|
+
const playerHtml = await readFile(artifactPath, "utf-8");
|
|
185
|
+
return `<iframe class="w-full aspect-3/2 rounded-lg bg-black" allowtransparency="true" srcdoc="${escAttr(playerHtml)}"></iframe>`;
|
|
186
|
+
}
|
|
187
|
+
return `<iframe class="w-full aspect-3/2 rounded-lg bg-black" allowtransparency="true" src="./${esc(entry.artifact)}"></iframe>`;
|
|
188
|
+
}
|
|
189
|
+
return `<a href="./${esc(entry.artifact)}">${esc(entry.artifact)}</a>`;
|
|
190
|
+
}
|
|
191
|
+
function esc(s) {
|
|
192
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
193
|
+
}
|
|
194
|
+
function escAttr(s) {
|
|
195
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
196
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type RecordingMode = "browser" | "terminal" | "auto";
|
|
2
|
+
export interface ProofConfig {
|
|
3
|
+
appName: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
proofDir?: string;
|
|
6
|
+
run?: string;
|
|
7
|
+
browser?: {
|
|
8
|
+
viewport?: {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
terminal?: {
|
|
14
|
+
cols?: number;
|
|
15
|
+
rows?: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface Recording {
|
|
19
|
+
path: string;
|
|
20
|
+
mode: Exclude<RecordingMode, "auto">;
|
|
21
|
+
duration: number;
|
|
22
|
+
label?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CaptureOptions {
|
|
25
|
+
command?: string;
|
|
26
|
+
testFile?: string;
|
|
27
|
+
testName?: string;
|
|
28
|
+
label?: string;
|
|
29
|
+
mode?: RecordingMode;
|
|
30
|
+
description?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ProofEntry {
|
|
33
|
+
timestamp: string;
|
|
34
|
+
mode: Exclude<RecordingMode, "auto">;
|
|
35
|
+
label?: string;
|
|
36
|
+
command?: string;
|
|
37
|
+
testFile?: string;
|
|
38
|
+
testName?: string;
|
|
39
|
+
duration: number;
|
|
40
|
+
artifact: string;
|
|
41
|
+
description: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ProofManifest {
|
|
44
|
+
version: 1;
|
|
45
|
+
appName: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
run: string;
|
|
48
|
+
createdAt: string;
|
|
49
|
+
entries: ProofEntry[];
|
|
50
|
+
}
|
|
51
|
+
export type ReportFormat = "md" | "html" | "archive";
|
|
52
|
+
export interface ReportOptions {
|
|
53
|
+
format?: ReportFormat | ReportFormat[];
|
|
54
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@automaze/proof",
|
|
3
|
+
"version": "0.20260311.0",
|
|
4
|
+
"description": "Visual proof of work for automated code changes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"proof": "./dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/cli.ts --outdir ./dist --target node",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "tsc --noEmit",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"clean": "rm -rf dist"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"playwright": "^1.50.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@playwright/test": "^1.58.2",
|
|
35
|
+
"@types/bun": "^1.0.0",
|
|
36
|
+
"typescript": "^5.3.0"
|
|
37
|
+
},
|
|
38
|
+
"license": "Apache-2.0"
|
|
39
|
+
}
|