@wasao/kagemusha 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +128 -0
  3. package/dist/commands/capture.d.ts +7 -0
  4. package/dist/commands/capture.d.ts.map +1 -0
  5. package/dist/commands/capture.js +27 -0
  6. package/dist/commands/capture.js.map +1 -0
  7. package/dist/commands/init.d.ts +2 -0
  8. package/dist/commands/init.d.ts.map +1 -0
  9. package/dist/commands/init.js +269 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/preview.d.ts +6 -0
  12. package/dist/commands/preview.d.ts.map +1 -0
  13. package/dist/commands/preview.js +33 -0
  14. package/dist/commands/preview.js.map +1 -0
  15. package/dist/commands/run.d.ts +6 -0
  16. package/dist/commands/run.d.ts.map +1 -0
  17. package/dist/commands/run.js +39 -0
  18. package/dist/commands/run.js.map +1 -0
  19. package/dist/commands/validate.d.ts +2 -0
  20. package/dist/commands/validate.d.ts.map +1 -0
  21. package/dist/commands/validate.js +53 -0
  22. package/dist/commands/validate.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +63 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lib/annotate.d.ts +3 -0
  28. package/dist/lib/annotate.d.ts.map +1 -0
  29. package/dist/lib/annotate.js +102 -0
  30. package/dist/lib/annotate.js.map +1 -0
  31. package/dist/lib/config.d.ts +9 -0
  32. package/dist/lib/config.d.ts.map +1 -0
  33. package/dist/lib/config.js +92 -0
  34. package/dist/lib/config.js.map +1 -0
  35. package/dist/lib/crawl.d.ts +6 -0
  36. package/dist/lib/crawl.d.ts.map +1 -0
  37. package/dist/lib/crawl.js +95 -0
  38. package/dist/lib/crawl.js.map +1 -0
  39. package/dist/lib/screenshot.d.ts +3 -0
  40. package/dist/lib/screenshot.d.ts.map +1 -0
  41. package/dist/lib/screenshot.js +166 -0
  42. package/dist/lib/screenshot.js.map +1 -0
  43. package/dist/lib/upload.d.ts +9 -0
  44. package/dist/lib/upload.d.ts.map +1 -0
  45. package/dist/lib/upload.js +43 -0
  46. package/dist/lib/upload.js.map +1 -0
  47. package/dist/types.d.ts +168 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +63 -0
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import chalk from "chalk";
3
+ import { Command } from "commander";
4
+ import { captureCommand } from "./commands/capture.js";
5
+ import { initCommand } from "./commands/init.js";
6
+ import { previewCommand } from "./commands/preview.js";
7
+ import { runCommand } from "./commands/run.js";
8
+ import { validateCommand } from "./commands/validate.js";
9
+ const BANNER = `
10
+ ${chalk.bold("kagemusha")} ${chalk.gray("v0.1.0")}
11
+ ${chalk.dim("The shadow warrior for your documentation.")}
12
+
13
+ ${chalk.white("Auto-update help center screenshots")}
14
+ ${chalk.white("when your code changes.")}
15
+ `;
16
+ const program = new Command();
17
+ program
18
+ .name("kagemusha")
19
+ .version("0.1.0")
20
+ .addHelpText("beforeAll", BANNER)
21
+ .configureHelp({
22
+ sortSubcommands: false,
23
+ });
24
+ program
25
+ .command("init")
26
+ .description("Set up config, screenshot definitions, and GitHub Actions workflow")
27
+ .action(initCommand);
28
+ program
29
+ .command("run")
30
+ .description("Run full pipeline: capture → upload to S3")
31
+ .option("--ids <ids>", "Comma-separated screenshot definition IDs")
32
+ .action(runCommand);
33
+ program
34
+ .command("capture")
35
+ .description("Capture screenshots only")
36
+ .option("--ids <ids>", "Comma-separated screenshot definition IDs")
37
+ .option("--all", "Capture all definitions")
38
+ .action(captureCommand);
39
+ program
40
+ .command("preview")
41
+ .description("Preview screenshots locally (opens browser)")
42
+ .option("--id <id>", "Preview a specific definition")
43
+ .action(previewCommand);
44
+ program
45
+ .command("validate")
46
+ .description("Validate config and definition files")
47
+ .action(validateCommand);
48
+ // Phase 2 commands
49
+ program
50
+ .command("compare")
51
+ .description(`Compare screenshots with baselines (VRT) ${chalk.yellow("[coming soon]")}`)
52
+ .action(() => {
53
+ console.log(chalk.yellow("\n🚧 compare is coming in Phase 2.\n"));
54
+ });
55
+ program
56
+ .command("publish")
57
+ .description("Publish screenshots to Intercom / external services " +
58
+ chalk.yellow("[coming soon]"))
59
+ .action(() => {
60
+ console.log(chalk.yellow("\n🚧 publish is coming in Phase 2.\n"));
61
+ });
62
+ program.parse();
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,MAAM,GAAG;IACX,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;IAC/C,KAAK,CAAC,GAAG,CAAC,4CAA4C,CAAC;;IAEvD,KAAK,CAAC,KAAK,CAAC,qCAAqC,CAAC;IAClD,KAAK,CAAC,KAAK,CAAC,yBAAyB,CAAC;CACzC,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACL,IAAI,CAAC,WAAW,CAAC;KACjB,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC;KAChC,aAAa,CAAC;IACd,eAAe,EAAE,KAAK;CACtB,CAAC,CAAC;AAEJ,OAAO;KACL,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CACX,oEAAoE,CACpE;KACA,MAAM,CAAC,WAAW,CAAC,CAAC;AAEtB,OAAO;KACL,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,2CAA2C,CAAC;KACxD,MAAM,CAAC,aAAa,EAAE,2CAA2C,CAAC;KAClE,MAAM,CAAC,UAAU,CAAC,CAAC;AAErB,OAAO;KACL,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,aAAa,EAAE,2CAA2C,CAAC;KAClE,MAAM,CAAC,OAAO,EAAE,yBAAyB,CAAC;KAC1C,MAAM,CAAC,cAAc,CAAC,CAAC;AAEzB,OAAO;KACL,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,WAAW,EAAE,+BAA+B,CAAC;KACpD,MAAM,CAAC,cAAc,CAAC,CAAC;AAEzB,OAAO;KACL,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,eAAe,CAAC,CAAC;AAE1B,mBAAmB;AACnB,OAAO;KACL,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CACX,4CAA4C,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAC3E;KACA,MAAM,CAAC,GAAG,EAAE;IACZ,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sCAAsC,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEJ,OAAO;KACL,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CACX,sDAAsD;IACrD,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAC9B;KACA,MAAM,CAAC,GAAG,EAAE;IACZ,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sCAAsC,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEJ,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CaptureResult, ScreenshotDefinition } from "../types.js";
2
+ export declare function annotateScreenshots(definitions: ScreenshotDefinition[], results: CaptureResult[], _projectRoot: string): Promise<CaptureResult[]>;
3
+ //# sourceMappingURL=annotate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotate.d.ts","sourceRoot":"","sources":["../../src/lib/annotate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,aAAa,EAEb,oBAAoB,EACpB,MAAM,aAAa,CAAC;AAErB,wBAAsB,mBAAmB,CACxC,WAAW,EAAE,oBAAoB,EAAE,EACnC,OAAO,EAAE,aAAa,EAAE,EACxB,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,EAAE,CAAC,CAoB1B"}
@@ -0,0 +1,102 @@
1
+ import fs from "node:fs";
2
+ import sharp from "sharp";
3
+ export async function annotateScreenshots(definitions, results, _projectRoot) {
4
+ const annotatedResults = [];
5
+ for (const result of results) {
6
+ const def = definitions.find((d) => d.id === result.id);
7
+ if (!def?.decorations?.length) {
8
+ // No decorations — copy raw as annotated
9
+ const annotatedPath = result.rawPath.replace(".raw.png", ".png");
10
+ fs.copyFileSync(result.rawPath, annotatedPath);
11
+ annotatedResults.push({ ...result, annotatedPath });
12
+ continue;
13
+ }
14
+ const annotatedPath = result.rawPath.replace(".raw.png", ".png");
15
+ await drawAnnotations(result.rawPath, annotatedPath, def.decorations);
16
+ annotatedResults.push({ ...result, annotatedPath });
17
+ }
18
+ return annotatedResults;
19
+ }
20
+ async function drawAnnotations(inputPath, outputPath, decorations) {
21
+ const image = sharp(inputPath);
22
+ const metadata = await image.metadata();
23
+ const width = metadata.width ?? 1280;
24
+ const height = metadata.height ?? 720;
25
+ const svgParts = [];
26
+ for (const dec of decorations) {
27
+ switch (dec.type) {
28
+ case "rect":
29
+ svgParts.push(renderRect(dec));
30
+ break;
31
+ case "arrow":
32
+ svgParts.push(renderArrow(dec));
33
+ break;
34
+ case "label":
35
+ svgParts.push(renderLabel(dec));
36
+ break;
37
+ }
38
+ }
39
+ const svgOverlay = `
40
+ <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
41
+ <defs>
42
+ <marker id="arrowhead" markerWidth="10" markerHeight="7"
43
+ refX="10" refY="3.5" orient="auto" fill="red">
44
+ <polygon points="0 0, 10 3.5, 0 7" />
45
+ </marker>
46
+ </defs>
47
+ ${svgParts.join("\n")}
48
+ </svg>
49
+ `;
50
+ await image
51
+ .composite([{ input: Buffer.from(svgOverlay), top: 0, left: 0 }])
52
+ .toFile(outputPath);
53
+ }
54
+ function renderRect(dec) {
55
+ const color = dec.style?.color ?? "#FF0000";
56
+ const strokeWidth = dec.style?.strokeWidth ?? 2;
57
+ const borderRadius = dec.style?.borderRadius ?? 0;
58
+ if ("selector" in dec.target) {
59
+ // Selector-based rects need runtime resolution — use placeholder
60
+ // In real usage, the bounding box is resolved during capture
61
+ return `<!-- rect: selector "${dec.target.selector}" needs runtime resolution -->`;
62
+ }
63
+ const { x, y, width, height } = dec.target;
64
+ return `<rect x="${x}" y="${y}" width="${width}" height="${height}"
65
+ rx="${borderRadius}" ry="${borderRadius}"
66
+ fill="none" stroke="${color}" stroke-width="${strokeWidth}" />`;
67
+ }
68
+ function renderArrow(dec) {
69
+ const color = dec.style?.color ?? "#FF0000";
70
+ const strokeWidth = dec.style?.strokeWidth ?? 2;
71
+ const from = "x" in dec.from ? dec.from : { x: 0, y: 0 };
72
+ const to = "x" in dec.to ? dec.to : { x: 100, y: 100 };
73
+ return `<line x1="${from.x}" y1="${from.y}" x2="${to.x}" y2="${to.y}"
74
+ stroke="${color}" stroke-width="${strokeWidth}"
75
+ marker-end="url(#arrowhead)" />`;
76
+ }
77
+ function renderLabel(dec) {
78
+ const fontSize = dec.style?.fontSize ?? 14;
79
+ const color = dec.style?.color ?? "#FF0000";
80
+ const bg = dec.style?.background ?? "#FFFFFF";
81
+ const pos = "x" in dec.position ? dec.position : { x: 0, y: 0 };
82
+ const paddingX = 6;
83
+ const paddingY = 4;
84
+ const textWidth = dec.text.length * fontSize * 0.6;
85
+ const textHeight = fontSize;
86
+ return `
87
+ <rect x="${pos.x - paddingX}" y="${pos.y - textHeight - paddingY}"
88
+ width="${textWidth + paddingX * 2}" height="${textHeight + paddingY * 2}"
89
+ rx="4" ry="4" fill="${bg}" stroke="${color}" stroke-width="1" />
90
+ <text x="${pos.x}" y="${pos.y}" font-size="${fontSize}"
91
+ fill="${color}" font-family="sans-serif">${escapeXml(dec.text)}</text>
92
+ `;
93
+ }
94
+ function escapeXml(str) {
95
+ return str
96
+ .replace(/&/g, "&amp;")
97
+ .replace(/</g, "&lt;")
98
+ .replace(/>/g, "&gt;")
99
+ .replace(/"/g, "&quot;")
100
+ .replace(/'/g, "&apos;");
101
+ }
102
+ //# sourceMappingURL=annotate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotate.js","sourceRoot":"","sources":["../../src/lib/annotate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { KagemushaConfig, Route, ScreenshotDefinition } from "../types.js";
2
+ export declare function findProjectRoot(startDir?: string): string;
3
+ export declare function loadConfig(projectRoot?: string): KagemushaConfig;
4
+ export declare function loadDefinitions(projectRoot?: string): ScreenshotDefinition[];
5
+ export declare function loadRouting(projectRoot?: string): Route[];
6
+ export declare function loadDefinitionById(id: string, projectRoot?: string): ScreenshotDefinition | undefined;
7
+ export declare function validateConfig(config: KagemushaConfig): string[];
8
+ export declare function validateDefinition(def: ScreenshotDefinition): string[];
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMhF,wBAAgB,eAAe,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAWxE;AAED,wBAAgB,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,eAAe,CAYhE;AAED,wBAAgB,eAAe,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAa5E;AAED,wBAAgB,WAAW,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,CAWzD;AAED,wBAAgB,kBAAkB,CACjC,EAAE,EAAE,MAAM,EACV,WAAW,CAAC,EAAE,MAAM,GAClB,oBAAoB,GAAG,SAAS,CAGlC;AAkBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,EAAE,CAWhE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,oBAAoB,GAAG,MAAM,EAAE,CActE"}
@@ -0,0 +1,92 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ const CONFIG_FILENAME = "kagemusha.config.yaml";
5
+ const DEFINITIONS_DIR = ".kagemusha/definitions";
6
+ const ROUTING_FILENAME = ".kagemusha/routing.yaml";
7
+ export function findProjectRoot(startDir = process.cwd()) {
8
+ let dir = startDir;
9
+ while (dir !== path.dirname(dir)) {
10
+ if (fs.existsSync(path.join(dir, CONFIG_FILENAME))) {
11
+ return dir;
12
+ }
13
+ dir = path.dirname(dir);
14
+ }
15
+ throw new Error(`${CONFIG_FILENAME} not found. Run "kagemusha init" to set up your project.`);
16
+ }
17
+ export function loadConfig(projectRoot) {
18
+ const root = projectRoot ?? findProjectRoot();
19
+ const configPath = path.join(root, CONFIG_FILENAME);
20
+ if (!fs.existsSync(configPath)) {
21
+ throw new Error(`Config file not found: ${configPath}`);
22
+ }
23
+ const raw = fs.readFileSync(configPath, "utf-8");
24
+ const config = parseYaml(raw);
25
+ resolveEnvVars(config);
26
+ return config;
27
+ }
28
+ export function loadDefinitions(projectRoot) {
29
+ const root = projectRoot ?? findProjectRoot();
30
+ const defsDir = path.join(root, DEFINITIONS_DIR);
31
+ if (!fs.existsSync(defsDir)) {
32
+ return [];
33
+ }
34
+ const files = fs.readdirSync(defsDir).filter((f) => f.endsWith(".json"));
35
+ return files.map((file) => {
36
+ const content = fs.readFileSync(path.join(defsDir, file), "utf-8");
37
+ return JSON.parse(content);
38
+ });
39
+ }
40
+ export function loadRouting(projectRoot) {
41
+ const root = projectRoot ?? findProjectRoot();
42
+ const routingPath = path.join(root, ROUTING_FILENAME);
43
+ if (!fs.existsSync(routingPath)) {
44
+ return [];
45
+ }
46
+ const raw = fs.readFileSync(routingPath, "utf-8");
47
+ const parsed = parseYaml(raw);
48
+ return parsed.routes ?? [];
49
+ }
50
+ export function loadDefinitionById(id, projectRoot) {
51
+ const defs = loadDefinitions(projectRoot);
52
+ return defs.find((d) => d.id === id);
53
+ }
54
+ function resolveEnvVars(obj) {
55
+ if (obj === null || obj === undefined)
56
+ return;
57
+ if (typeof obj === "object") {
58
+ for (const [key, value] of Object.entries(obj)) {
59
+ if (typeof value === "string") {
60
+ obj[key] = value.replace(/\$\{(\w+)\}/g, (_, envVar) => process.env[envVar] ?? "");
61
+ }
62
+ else if (typeof value === "object") {
63
+ resolveEnvVars(value);
64
+ }
65
+ }
66
+ }
67
+ }
68
+ export function validateConfig(config) {
69
+ const errors = [];
70
+ if (!config.app?.baseUrl) {
71
+ errors.push("app.baseUrl is required");
72
+ }
73
+ if (!config.screenshot?.defaultViewport) {
74
+ errors.push("screenshot.defaultViewport is required");
75
+ }
76
+ return errors;
77
+ }
78
+ export function validateDefinition(def) {
79
+ const errors = [];
80
+ if (!def.id)
81
+ errors.push("id is required");
82
+ if (!def.url)
83
+ errors.push("url is required");
84
+ if (!def.capture)
85
+ errors.push("capture is required");
86
+ if (def.capture &&
87
+ !["fullPage", "selector", "crop"].includes(def.capture.mode)) {
88
+ errors.push(`capture.mode must be "fullPage", "selector", or "crop"`);
89
+ }
90
+ return errors;
91
+ }
92
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface DiscoveredPage {
2
+ path: string;
3
+ title: string;
4
+ }
5
+ export declare function discoverPages(baseUrl: string, maxDepth?: number, maxPages?: number): Promise<DiscoveredPage[]>;
6
+ //# sourceMappingURL=crawl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawl.d.ts","sourceRoot":"","sources":["../../src/lib/crawl.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,aAAa,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAU,EACpB,QAAQ,GAAE,MAAW,GACnB,OAAO,CAAC,cAAc,EAAE,CAAC,CAqD3B"}
@@ -0,0 +1,95 @@
1
+ export async function discoverPages(baseUrl, maxDepth = 2, maxPages = 50) {
2
+ const { chromium } = await import("playwright-chromium");
3
+ const browser = await chromium.launch({ headless: true });
4
+ const context = await browser.newContext();
5
+ const origin = new URL(baseUrl).origin;
6
+ const visited = new Set();
7
+ const results = [];
8
+ const queue = [{ url: baseUrl, depth: 0 }];
9
+ try {
10
+ while (queue.length > 0 && results.length < maxPages) {
11
+ const item = queue.shift();
12
+ if (!item)
13
+ break;
14
+ const normalized = normalizeUrl(item.url, origin);
15
+ if (!normalized || visited.has(normalized))
16
+ continue;
17
+ visited.add(normalized);
18
+ try {
19
+ const page = await context.newPage();
20
+ await page.goto(normalized, {
21
+ waitUntil: "domcontentloaded",
22
+ timeout: 10000,
23
+ });
24
+ const title = await page.title();
25
+ const pagePath = new URL(page.url()).pathname;
26
+ results.push({
27
+ path: pagePath,
28
+ title: title || pagePath,
29
+ });
30
+ if (item.depth < maxDepth) {
31
+ const links = await collectLinks(page, origin);
32
+ for (const link of links) {
33
+ if (!visited.has(link)) {
34
+ queue.push({ url: link, depth: item.depth + 1 });
35
+ }
36
+ }
37
+ }
38
+ await page.close();
39
+ }
40
+ catch {
41
+ // Skip pages that fail to load
42
+ }
43
+ }
44
+ }
45
+ finally {
46
+ await browser.close();
47
+ }
48
+ return results;
49
+ }
50
+ async function collectLinks(page, origin) {
51
+ const hrefs = await page.evaluate(() => Array.from(document.querySelectorAll("a[href]")).map((a) => a.href));
52
+ return hrefs
53
+ .map((href) => normalizeUrl(href, origin))
54
+ .filter((url) => url !== null);
55
+ }
56
+ function normalizeUrl(url, origin) {
57
+ try {
58
+ const parsed = new URL(url, origin);
59
+ // Same origin only
60
+ if (parsed.origin !== origin)
61
+ return null;
62
+ // Skip non-page resources
63
+ const skip = [
64
+ ".js",
65
+ ".css",
66
+ ".png",
67
+ ".jpg",
68
+ ".jpeg",
69
+ ".gif",
70
+ ".svg",
71
+ ".ico",
72
+ ".woff",
73
+ ".woff2",
74
+ ".ttf",
75
+ ".eot",
76
+ ".pdf",
77
+ ".zip",
78
+ ".json",
79
+ ".xml",
80
+ ];
81
+ if (skip.some((ext) => parsed.pathname.endsWith(ext)))
82
+ return null;
83
+ // Skip anchors and mailto
84
+ if (url.startsWith("mailto:") || url.startsWith("tel:"))
85
+ return null;
86
+ // Remove hash and search params for deduplication
87
+ parsed.hash = "";
88
+ parsed.search = "";
89
+ return parsed.toString();
90
+ }
91
+ catch {
92
+ return null;
93
+ }
94
+ }
95
+ //# sourceMappingURL=crawl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawl.js","sourceRoot":"","sources":["../../src/lib/crawl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { CaptureResult, KagemushaConfig, ScreenshotDefinition } from "../types.js";
2
+ export declare function captureScreenshots(config: KagemushaConfig, definitions: ScreenshotDefinition[], projectRoot: string): Promise<CaptureResult[]>;
3
+ //# sourceMappingURL=screenshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEX,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,MAAM,aAAa,CAAC;AAmBrB,wBAAsB,kBAAkB,CACvC,MAAM,EAAE,eAAe,EACvB,WAAW,EAAE,oBAAoB,EAAE,EACnC,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,EAAE,CAAC,CAsB1B"}
@@ -0,0 +1,166 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ async function loadPlaywright() {
4
+ try {
5
+ return await import("playwright-chromium");
6
+ }
7
+ catch {
8
+ throw new Error("Playwright is required for screenshot capture.\n" +
9
+ "Install it with: npm install -D playwright && npx playwright install chromium");
10
+ }
11
+ }
12
+ const SCREENSHOTS_DIR = "screenshots";
13
+ export async function captureScreenshots(config, definitions, projectRoot) {
14
+ const outputDir = path.join(projectRoot, SCREENSHOTS_DIR);
15
+ fs.mkdirSync(outputDir, { recursive: true });
16
+ const { chromium } = await loadPlaywright();
17
+ const browser = await chromium.launch({ headless: true });
18
+ const results = [];
19
+ try {
20
+ const context = await createAuthenticatedContext(browser, config);
21
+ for (const def of definitions) {
22
+ const result = await captureOne(context, config, def, outputDir);
23
+ results.push(result);
24
+ }
25
+ await context.close();
26
+ }
27
+ finally {
28
+ await browser.close();
29
+ }
30
+ return results;
31
+ }
32
+ async function createAuthenticatedContext(browser, config) {
33
+ const viewport = config.screenshot.defaultViewport;
34
+ const context = await browser.newContext({
35
+ viewport: { width: viewport.width, height: viewport.height },
36
+ deviceScaleFactor: viewport.deviceScaleFactor ?? 2,
37
+ });
38
+ if (config.auth) {
39
+ const page = await context.newPage();
40
+ const loginUrl = new URL(config.auth.loginUrl, config.app.baseUrl).toString();
41
+ await page.goto(loginUrl);
42
+ await executeActions(page, config.auth.steps);
43
+ await page.close();
44
+ }
45
+ return context;
46
+ }
47
+ async function captureOne(context, config, def, outputDir) {
48
+ const page = await context.newPage();
49
+ if (def.viewport) {
50
+ await page.setViewportSize({
51
+ width: def.viewport.width,
52
+ height: def.viewport.height,
53
+ });
54
+ }
55
+ const url = resolveUrl(config.app.baseUrl, def.url, def.urlParams);
56
+ await page.goto(url, { waitUntil: "networkidle" });
57
+ if (def.hideElements?.length) {
58
+ await hideElements(page, def.hideElements);
59
+ }
60
+ if (def.beforeCapture?.length) {
61
+ await executeActions(page, def.beforeCapture);
62
+ }
63
+ const rawPath = path.join(outputDir, `${def.id}.raw.png`);
64
+ await takeScreenshot(page, def, rawPath);
65
+ await page.close();
66
+ const timestamp = new Date().toISOString();
67
+ return {
68
+ id: def.id,
69
+ rawPath,
70
+ annotatedPath: rawPath, // annotate step will create the final version
71
+ timestamp,
72
+ };
73
+ }
74
+ async function takeScreenshot(page, def, outputPath) {
75
+ switch (def.capture.mode) {
76
+ case "fullPage":
77
+ await page.screenshot({ path: outputPath, fullPage: true });
78
+ break;
79
+ case "selector": {
80
+ const element = await page.waitForSelector(def.capture.selector, {
81
+ timeout: 10000,
82
+ });
83
+ if (!element) {
84
+ throw new Error(`Element not found: ${def.capture.selector} (definition: ${def.id})`);
85
+ }
86
+ await element.screenshot({ path: outputPath });
87
+ break;
88
+ }
89
+ case "crop": {
90
+ const { start, end } = def.capture.crop;
91
+ await page.screenshot({
92
+ path: outputPath,
93
+ clip: {
94
+ x: start.x,
95
+ y: start.y,
96
+ width: end.x - start.x,
97
+ height: end.y - start.y,
98
+ },
99
+ });
100
+ break;
101
+ }
102
+ }
103
+ }
104
+ async function executeActions(page, actions) {
105
+ for (const action of actions) {
106
+ switch (action.action) {
107
+ case "click":
108
+ await page.click(action.selector);
109
+ break;
110
+ case "type":
111
+ await page.fill(action.selector, action.text);
112
+ break;
113
+ case "select":
114
+ await page.selectOption(action.selector, action.value);
115
+ break;
116
+ case "hover":
117
+ await page.hover(action.selector);
118
+ break;
119
+ case "scroll":
120
+ if (action.selector) {
121
+ await page
122
+ .locator(action.selector)
123
+ .evaluate((el, y) => el.scrollTo(0, y), action.y);
124
+ }
125
+ else {
126
+ await page.evaluate((y) => window.scrollTo(0, y), action.y);
127
+ }
128
+ break;
129
+ case "wait":
130
+ await page.waitForTimeout(action.ms);
131
+ break;
132
+ case "waitForSelector":
133
+ await page.waitForSelector(action.selector, {
134
+ timeout: action.timeout ?? 10000,
135
+ });
136
+ break;
137
+ case "waitForNavigation":
138
+ await page.waitForLoadState("networkidle", {
139
+ timeout: action.timeout ?? 30000,
140
+ });
141
+ break;
142
+ case "evaluate":
143
+ await page.evaluate(action.script);
144
+ break;
145
+ }
146
+ }
147
+ }
148
+ async function hideElements(page, selectors) {
149
+ for (const selector of selectors) {
150
+ await page.evaluate((sel) => {
151
+ document.querySelectorAll(sel).forEach((el) => {
152
+ el.style.display = "none";
153
+ });
154
+ }, selector);
155
+ }
156
+ }
157
+ function resolveUrl(baseUrl, urlPath, params) {
158
+ let resolved = urlPath;
159
+ if (params) {
160
+ for (const [key, value] of Object.entries(params)) {
161
+ resolved = resolved.replace(`{${key}}`, value);
162
+ }
163
+ }
164
+ return new URL(resolved, baseUrl).toString();
165
+ }
166
+ //# sourceMappingURL=screenshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAY7B,KAAK,UAAU,cAAc;IAC5B,IAAI,CAAC;QACJ,OAAO,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CACd,kDAAkD;YACjD,+EAA+E,CAChF,CAAC;IACH,CAAC;AACF,CAAC;AAED,MAAM,eAAe,GAAG,aAAa,CAAC;AAEtC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,MAAuB,EACvB,WAAmC,EACnC,WAAmB;IAEnB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAC1D,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAoB,EAAE,CAAC;IAEpC,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,0BAA0B,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAElE,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QAED,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,0BAA0B,CACxC,OAAgB,EAChB,MAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC;IACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;QACxC,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE;QAC5D,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,IAAI,CAAC;KAClD,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,IAAI,GAAG,CACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,EACpB,MAAM,CAAC,GAAG,CAAC,OAAO,CAClB,CAAC,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1B,MAAM,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,UAAU,CACxB,OAAuB,EACvB,MAAuB,EACvB,GAAyB,EACzB,SAAiB;IAEjB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAErC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,IAAI,CAAC,eAAe,CAAC;YAC1B,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK;YACzB,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM;SAC3B,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;IACnE,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IAEnD,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAC9B,MAAM,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;QAC/B,MAAM,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAEzC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IAEnB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE3C,OAAO;QACN,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,OAAO;QACP,aAAa,EAAE,OAAO,EAAE,8CAA8C;QACtE,SAAS;KACT,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAC5B,IAAU,EACV,GAAyB,EACzB,UAAkB;IAElB,QAAQ,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC1B,KAAK,UAAU;YACd,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,MAAM;QAEP,KAAK,UAAU,CAAC,CAAC,CAAC;YACjB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE;gBAChE,OAAO,EAAE,KAAK;aACd,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CACd,sBAAsB,GAAG,CAAC,OAAO,CAAC,QAAQ,iBAAiB,GAAG,CAAC,EAAE,GAAG,CACpE,CAAC;YACH,CAAC;YACD,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YAC/C,MAAM;QACP,CAAC;QAED,KAAK,MAAM,CAAC,CAAC,CAAC;YACb,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;YACxC,MAAM,IAAI,CAAC,UAAU,CAAC;gBACrB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE;oBACL,CAAC,EAAE,KAAK,CAAC,CAAC;oBACV,CAAC,EAAE,KAAK,CAAC,CAAC;oBACV,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;oBACtB,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;iBACvB;aACD,CAAC,CAAC;YACH,MAAM;QACP,CAAC;IACF,CAAC;AACF,CAAC;AAED,KAAK,UAAU,cAAc,CAC5B,IAAU,EACV,OAAwB;IAExB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;YACvB,KAAK,OAAO;gBACX,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAClC,MAAM;YACP,KAAK,MAAM;gBACV,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC9C,MAAM;YACP,KAAK,QAAQ;gBACZ,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvD,MAAM;YACP,KAAK,OAAO;gBACX,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAClC,MAAM;YACP,KAAK,QAAQ;gBACZ,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACrB,MAAM,IAAI;yBACR,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;yBACxB,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;gBACpD,CAAC;qBAAM,CAAC;oBACP,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7D,CAAC;gBACD,MAAM;YACP,KAAK,MAAM;gBACV,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrC,MAAM;YACP,KAAK,iBAAiB;gBACrB,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,EAAE;oBAC3C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;iBAChC,CAAC,CAAC;gBACH,MAAM;YACP,KAAK,mBAAmB;gBACvB,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE;oBAC1C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;iBAChC,CAAC,CAAC;gBACH,MAAM;YACP,KAAK,UAAU;gBACd,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACnC,MAAM;QACR,CAAC;IACF,CAAC;AACF,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAU,EAAE,SAAmB;IAC1D,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,EAAE;YAC3B,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;gBAC5C,EAAkB,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;YAC5C,CAAC,CAAC,CAAC;QACJ,CAAC,EAAE,QAAQ,CAAC,CAAC;IACd,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAClB,OAAe,EACf,OAAe,EACf,MAA+B;IAE/B,IAAI,QAAQ,GAAG,OAAO,CAAC;IACvB,IAAI,MAAM,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;QAChD,CAAC;IACF,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { CaptureResult, KagemushaConfig } from "../types.js";
2
+ export interface UploadResult {
3
+ id: string;
4
+ url: string;
5
+ bucket: string;
6
+ key: string;
7
+ }
8
+ export declare function uploadToS3(config: KagemushaConfig, results: CaptureResult[], _projectRoot: string): Promise<UploadResult[]>;
9
+ //# sourceMappingURL=upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/lib/upload.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,wBAAsB,UAAU,CAC/B,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,aAAa,EAAE,EACxB,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,EAAE,CAAC,CAmDzB"}