@cleaver/breakcheck-core 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.
- package/README.md +152 -0
- package/dist/__tests__/compare/index.test.js +322 -0
- package/dist/__tests__/compare/index.test.js.map +1 -0
- package/dist/__tests__/rules/RulesDsl.test.js +190 -0
- package/dist/__tests__/rules/RulesDsl.test.js.map +1 -0
- package/dist/__tests__/rules/RulesEngine.test.js +142 -0
- package/dist/__tests__/rules/RulesEngine.test.js.map +1 -0
- package/dist/__tests__/snapshot/index.test.js +238 -0
- package/dist/__tests__/snapshot/index.test.js.map +1 -0
- package/dist/__tests__/view/index.test.js +102 -0
- package/dist/__tests__/view/index.test.js.map +1 -0
- package/dist/api/index.js +69 -0
- package/dist/api/index.js.map +1 -0
- package/dist/core/compare/classes/ComparisonRepository.js +77 -0
- package/dist/core/compare/classes/ComparisonRepository.js.map +1 -0
- package/dist/core/compare/index.js +81 -0
- package/dist/core/compare/index.js.map +1 -0
- package/dist/core/crawler/implementations/cheerio.js +46 -0
- package/dist/core/crawler/implementations/cheerio.js.map +1 -0
- package/dist/core/crawler/implementations/playwright.js +44 -0
- package/dist/core/crawler/implementations/playwright.js.map +1 -0
- package/dist/core/crawler/index.js +61 -0
- package/dist/core/crawler/index.js.map +1 -0
- package/dist/core/rules/RulesDsl.js +251 -0
- package/dist/core/rules/RulesDsl.js.map +1 -0
- package/dist/core/rules/RulesEngine.js +91 -0
- package/dist/core/rules/RulesEngine.js.map +1 -0
- package/dist/core/snapshot/classes/LoadedSnapshot.js +37 -0
- package/dist/core/snapshot/classes/LoadedSnapshot.js.map +1 -0
- package/dist/core/snapshot/classes/SnapshotRepository.js +192 -0
- package/dist/core/snapshot/classes/SnapshotRepository.js.map +1 -0
- package/dist/core/snapshot/index.js +67 -0
- package/dist/core/snapshot/index.js.map +1 -0
- package/dist/core/view/index.handlers.js +70 -0
- package/dist/core/view/index.handlers.js.map +1 -0
- package/dist/core/view/index.js +50 -0
- package/dist/core/view/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/logger.js +26 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/root.js +25 -0
- package/dist/lib/root.js.map +1 -0
- package/dist/public/css/diff2html.min.css +1 -0
- package/dist/public/js/diff2html.min.js +1 -0
- package/dist/types/api.js +3 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/compare.js +3 -0
- package/dist/types/compare.js.map +1 -0
- package/dist/types/crawler.js +2 -0
- package/dist/types/crawler.js.map +1 -0
- package/dist/types/rules.js +6 -0
- package/dist/types/rules.js.map +1 -0
- package/dist/types/snapshot.js +3 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/views/diff.ejs +59 -0
- package/dist/views/index.ejs +83 -0
- package/package.json +41 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as cheerio from "cheerio";
|
|
2
|
+
import { logger } from "../../lib/logger";
|
|
3
|
+
import { processRulesDsl } from "./RulesDsl";
|
|
4
|
+
export class RulesEngine {
|
|
5
|
+
ruleset;
|
|
6
|
+
constructor(ruleset) {
|
|
7
|
+
this.ruleset = ruleset;
|
|
8
|
+
}
|
|
9
|
+
static async create(rulesetOrName) {
|
|
10
|
+
let ruleset;
|
|
11
|
+
if (typeof rulesetOrName === "string") {
|
|
12
|
+
try {
|
|
13
|
+
ruleset = await processRulesDsl(rulesetOrName);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof Error) {
|
|
17
|
+
logger.error({ error, rulesetName: rulesetOrName }, "Failed to process ruleset DSL");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
ruleset = rulesetOrName;
|
|
25
|
+
}
|
|
26
|
+
return new RulesEngine(ruleset);
|
|
27
|
+
}
|
|
28
|
+
process(html) {
|
|
29
|
+
const $ = cheerio.load(html);
|
|
30
|
+
// Apply each rule in sequence
|
|
31
|
+
for (const rule of this.ruleset.rules) {
|
|
32
|
+
const elements = $(rule.selector);
|
|
33
|
+
elements.each((_, element) => {
|
|
34
|
+
if (element.type === "tag") {
|
|
35
|
+
for (const action of rule.actions) {
|
|
36
|
+
this.applyAction($, element, action);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return $.html();
|
|
42
|
+
}
|
|
43
|
+
applyAction($, element, action) {
|
|
44
|
+
const $element = $(element);
|
|
45
|
+
// Check content regex if specified
|
|
46
|
+
if (action.modifiers?.content_regex) {
|
|
47
|
+
const content = $element.text();
|
|
48
|
+
const regex = new RegExp(action.modifiers.content_regex);
|
|
49
|
+
if (!regex.test(content)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
switch (action.action) {
|
|
54
|
+
case "exclude":
|
|
55
|
+
$element.remove();
|
|
56
|
+
break;
|
|
57
|
+
case "include":
|
|
58
|
+
// No action needed for include
|
|
59
|
+
break;
|
|
60
|
+
case "remove_attr":
|
|
61
|
+
if (action.modifiers && "attr" in action.modifiers) {
|
|
62
|
+
$element.removeAttr(action.modifiers.attr);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
case "rewrite_attr":
|
|
66
|
+
if (action.modifiers &&
|
|
67
|
+
"attr" in action.modifiers &&
|
|
68
|
+
"regex" in action.modifiers &&
|
|
69
|
+
"replace" in action.modifiers) {
|
|
70
|
+
const currentValue = $element.attr(action.modifiers.attr);
|
|
71
|
+
if (currentValue) {
|
|
72
|
+
const regex = new RegExp(action.modifiers.regex);
|
|
73
|
+
const newValue = currentValue.replace(regex, action.modifiers.replace);
|
|
74
|
+
$element.attr(action.modifiers.attr, newValue);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
case "rewrite_content":
|
|
79
|
+
if (action.modifiers &&
|
|
80
|
+
"regex" in action.modifiers &&
|
|
81
|
+
"replace" in action.modifiers) {
|
|
82
|
+
const content = $element.text();
|
|
83
|
+
const regex = new RegExp(action.modifiers.regex);
|
|
84
|
+
const newContent = content.replace(regex, action.modifiers.replace);
|
|
85
|
+
$element.text(newContent);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=RulesEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RulesEngine.js","sourceRoot":"","sources":["../../../src/core/rules/RulesEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import * as zlib from "zlib";
|
|
5
|
+
import { logger } from "../../../lib/logger";
|
|
6
|
+
const gunzip = promisify(zlib.gunzip);
|
|
7
|
+
export class LoadedSnapshot {
|
|
8
|
+
metadata;
|
|
9
|
+
index;
|
|
10
|
+
pagesDir;
|
|
11
|
+
constructor(metadata, index, pagesDir) {
|
|
12
|
+
this.metadata = metadata;
|
|
13
|
+
this.index = index;
|
|
14
|
+
this.pagesDir = pagesDir;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Loads a single page snapshot on demand by URL
|
|
18
|
+
*/
|
|
19
|
+
async getPage(url) {
|
|
20
|
+
const info = this.index.urls[url];
|
|
21
|
+
if (!info)
|
|
22
|
+
return null;
|
|
23
|
+
const filePath = path.join(this.pagesDir, info.filename);
|
|
24
|
+
try {
|
|
25
|
+
const compressed = await fs.readFile(filePath);
|
|
26
|
+
const decompressed = await gunzip(compressed);
|
|
27
|
+
const page = JSON.parse(decompressed.toString());
|
|
28
|
+
return page;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
// Could be file not found, decompression, or parse error
|
|
32
|
+
logger.error({ err, url, filePath }, "Failed to load page snapshot");
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=LoadedSnapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadedSnapshot.js","sourceRoot":"","sources":["../../../../src/core/snapshot/classes/LoadedSnapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAI7C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAEtC,MAAM,OAAO,cAAc;IACT,QAAQ,CAAmB;IAC3B,KAAK,CAAgB;IACpB,QAAQ,CAAS;IAElC,YACE,QAA0B,EAC1B,KAAoB,EACpB,QAAgB;QAEhB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC/C,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,yDAAyD;YACzD,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,8BAA8B,CAAC,CAAC;YACrE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import * as zlib from "zlib";
|
|
5
|
+
import { findRootDir } from "../../../lib/root";
|
|
6
|
+
import { LoadedSnapshot } from "./LoadedSnapshot";
|
|
7
|
+
const gzip = promisify(zlib.gzip);
|
|
8
|
+
export class SnapshotRepository {
|
|
9
|
+
snapshotsDir;
|
|
10
|
+
constructor(snapshotsDir) {
|
|
11
|
+
this.snapshotsDir = snapshotsDir;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new SnapshotRepository instance using the project root directory
|
|
15
|
+
* @returns Promise<SnapshotRepository>
|
|
16
|
+
*/
|
|
17
|
+
static async create() {
|
|
18
|
+
const rootDir = await findRootDir();
|
|
19
|
+
const snapshotsDir = path.join(rootDir, "snapshots");
|
|
20
|
+
return new SnapshotRepository(snapshotsDir);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new SnapshotRepository instance with a custom snapshots directory
|
|
24
|
+
* @param snapshotsDir Custom directory path for snapshots
|
|
25
|
+
* @returns Promise<SnapshotRepository>
|
|
26
|
+
*/
|
|
27
|
+
static async createWithCustomDir(snapshotsDir) {
|
|
28
|
+
return new SnapshotRepository(snapshotsDir);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Normalizes a URL by removing scheme, hostname, and port
|
|
32
|
+
* @param url The URL to normalize
|
|
33
|
+
* @returns The normalized URL path starting with "/"
|
|
34
|
+
*/
|
|
35
|
+
normalizeUrl(url) {
|
|
36
|
+
try {
|
|
37
|
+
const urlObj = new URL(url);
|
|
38
|
+
return urlObj.pathname + urlObj.search + urlObj.hash;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// If URL parsing fails, return the original URL
|
|
42
|
+
return url;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Save a snapshot to disk (streaming/iterative)
|
|
47
|
+
* Returns the number of non-error pages saved
|
|
48
|
+
*/
|
|
49
|
+
async saveSnapshot(name, data) {
|
|
50
|
+
await fs.mkdir(this.snapshotsDir, { recursive: true });
|
|
51
|
+
const snapshotDir = path.join(this.snapshotsDir, name);
|
|
52
|
+
try {
|
|
53
|
+
await fs.rm(snapshotDir, { recursive: true, force: true });
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// Ignore errors if directory doesn't exist
|
|
57
|
+
}
|
|
58
|
+
await fs.mkdir(snapshotDir, { recursive: true });
|
|
59
|
+
const metadataPath = path.join(snapshotDir, "metadata.json");
|
|
60
|
+
await fs.writeFile(metadataPath, JSON.stringify(data.metadata, null, 2));
|
|
61
|
+
const pagesDir = path.join(snapshotDir, "pages");
|
|
62
|
+
await fs.mkdir(pagesDir, { recursive: true });
|
|
63
|
+
// Create index
|
|
64
|
+
const index = {
|
|
65
|
+
urls: {},
|
|
66
|
+
metadata: {
|
|
67
|
+
baseUrl: data.metadata.baseUrl,
|
|
68
|
+
timestamp: data.metadata.timestamp,
|
|
69
|
+
totalPages: 0,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
let pageCount = 0;
|
|
73
|
+
await data.dataset.forEach(async (item) => {
|
|
74
|
+
if (item.type === "error")
|
|
75
|
+
return;
|
|
76
|
+
// Save each page as a compressed JSON file
|
|
77
|
+
const page = item;
|
|
78
|
+
const normalizedUrl = this.normalizeUrl(page.url);
|
|
79
|
+
// Create a copy of the page with normalized URL
|
|
80
|
+
const normalizedPage = {
|
|
81
|
+
...page,
|
|
82
|
+
url: normalizedUrl,
|
|
83
|
+
};
|
|
84
|
+
const pageData = JSON.stringify(normalizedPage);
|
|
85
|
+
const compressed = await gzip(pageData);
|
|
86
|
+
const filename = Buffer.from(normalizedUrl).toString("base64url") + ".json.gz";
|
|
87
|
+
await fs.writeFile(path.join(pagesDir, filename), compressed);
|
|
88
|
+
// Add to index
|
|
89
|
+
index.urls[normalizedUrl] = {
|
|
90
|
+
filename,
|
|
91
|
+
statusCode: page.statusCode,
|
|
92
|
+
finalUrl: page.finalUrl !== page.url ? page.finalUrl : undefined,
|
|
93
|
+
pageTitle: page.title,
|
|
94
|
+
};
|
|
95
|
+
pageCount++;
|
|
96
|
+
});
|
|
97
|
+
// Save index
|
|
98
|
+
index.metadata.totalPages = pageCount;
|
|
99
|
+
const indexPath = path.join(snapshotDir, "index.json");
|
|
100
|
+
await fs.writeFile(indexPath, JSON.stringify(index, null, 2));
|
|
101
|
+
return pageCount;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Load a snapshot from disk
|
|
105
|
+
*/
|
|
106
|
+
async loadSnapshot(name) {
|
|
107
|
+
const snapshotDir = path.join(this.snapshotsDir, name);
|
|
108
|
+
// Load metadata
|
|
109
|
+
const metadataPath = path.join(snapshotDir, "metadata.json");
|
|
110
|
+
const metadataContent = await fs.readFile(metadataPath, "utf-8");
|
|
111
|
+
const metadata = JSON.parse(metadataContent);
|
|
112
|
+
// Load index
|
|
113
|
+
const indexPath = path.join(snapshotDir, "index.json");
|
|
114
|
+
const indexContent = await fs.readFile(indexPath, "utf-8");
|
|
115
|
+
const index = JSON.parse(indexContent);
|
|
116
|
+
// Prepare pages directory path
|
|
117
|
+
const pagesDir = path.join(snapshotDir, "pages");
|
|
118
|
+
// Return a LoadedSnapshot instance for on-demand page loading
|
|
119
|
+
return new LoadedSnapshot(metadata, index, pagesDir);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Generate a URL list file from a snapshot
|
|
123
|
+
* @param name Snapshot name
|
|
124
|
+
* @param outputPath Optional path to save the URL list. If not provided, saves in the snapshot directory
|
|
125
|
+
* @param filter Optional function to filter URLs
|
|
126
|
+
*/
|
|
127
|
+
async generateUrlList(name, outputPath, filter) {
|
|
128
|
+
const snapshotDir = path.join(this.snapshotsDir, name);
|
|
129
|
+
// Load index
|
|
130
|
+
const indexPath = path.join(snapshotDir, "index.json");
|
|
131
|
+
const indexContent = await fs.readFile(indexPath, "utf-8");
|
|
132
|
+
const index = JSON.parse(indexContent);
|
|
133
|
+
// Filter and collect URLs
|
|
134
|
+
const urls = Object.entries(index.urls)
|
|
135
|
+
.filter(([url, info]) => !filter || filter(url, info.statusCode))
|
|
136
|
+
.map(([url]) => url);
|
|
137
|
+
// Generate URL list content
|
|
138
|
+
const content = urls.join("\n");
|
|
139
|
+
// Determine output path
|
|
140
|
+
const finalOutputPath = outputPath || path.join(snapshotDir, "urls.txt");
|
|
141
|
+
// Save URL list
|
|
142
|
+
await fs.writeFile(finalOutputPath, content);
|
|
143
|
+
return finalOutputPath;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* List all available snapshots with their details
|
|
147
|
+
*/
|
|
148
|
+
async listSnapshots() {
|
|
149
|
+
try {
|
|
150
|
+
const entries = await fs.readdir(this.snapshotsDir, {
|
|
151
|
+
withFileTypes: true,
|
|
152
|
+
});
|
|
153
|
+
const snapshots = entries.filter((entry) => entry.isDirectory());
|
|
154
|
+
const summaries = [];
|
|
155
|
+
for (const snapshot of snapshots) {
|
|
156
|
+
try {
|
|
157
|
+
// Load metadata
|
|
158
|
+
const metadataPath = path.join(this.snapshotsDir, snapshot.name, "metadata.json");
|
|
159
|
+
const metadataContent = await fs.readFile(metadataPath, "utf-8");
|
|
160
|
+
const metadata = JSON.parse(metadataContent);
|
|
161
|
+
// Load index for page count
|
|
162
|
+
const indexPath = path.join(this.snapshotsDir, snapshot.name, "index.json");
|
|
163
|
+
const indexContent = await fs.readFile(indexPath, "utf-8");
|
|
164
|
+
const index = JSON.parse(indexContent);
|
|
165
|
+
summaries.push({
|
|
166
|
+
name: snapshot.name,
|
|
167
|
+
date: metadata.timestamp,
|
|
168
|
+
pageCount: index.metadata.totalPages,
|
|
169
|
+
errorCount: 0,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
// If we can't read the metadata or index, just include the name
|
|
174
|
+
summaries.push({
|
|
175
|
+
name: snapshot.name,
|
|
176
|
+
date: "Unknown",
|
|
177
|
+
pageCount: 0,
|
|
178
|
+
errorCount: 0,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return summaries;
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (error.code === "ENOENT") {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=SnapshotRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SnapshotRepository.js","sourceRoot":"","sources":["../../../../src/core/snapshot/classes/SnapshotRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Dataset } from "crawlee";
|
|
2
|
+
import { logger } from "../../lib/logger";
|
|
3
|
+
import { BreakcheckCrawler } from "../crawler";
|
|
4
|
+
export { LoadedSnapshot } from "./classes/LoadedSnapshot";
|
|
5
|
+
export { SnapshotRepository } from "./classes/SnapshotRepository";
|
|
6
|
+
/**
|
|
7
|
+
* Creates a snapshot of a website based on the provided configuration.
|
|
8
|
+
* Orchestrates calls to Crawler and Snapshot Manager.
|
|
9
|
+
*/
|
|
10
|
+
export async function createSnapshot(config, snapshotRepository) {
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
const errors = [];
|
|
13
|
+
try {
|
|
14
|
+
// Validate input config
|
|
15
|
+
if (!config.baseUrl) {
|
|
16
|
+
logger.error({ config }, "Snapshot creation failed: baseUrl is required");
|
|
17
|
+
throw new Error("baseUrl is required");
|
|
18
|
+
}
|
|
19
|
+
if (!config.name) {
|
|
20
|
+
logger.error({ config }, "Snapshot creation failed: name is required");
|
|
21
|
+
throw new Error("name is required");
|
|
22
|
+
}
|
|
23
|
+
const crawler = new BreakcheckCrawler(config.crawlSettings);
|
|
24
|
+
const { datasetName, errors: crawlErrors } = await crawler.crawl();
|
|
25
|
+
errors.push(...crawlErrors);
|
|
26
|
+
const dataset = await Dataset.open(datasetName);
|
|
27
|
+
const pageCount = await snapshotRepository.saveSnapshot(config.name, {
|
|
28
|
+
dataset,
|
|
29
|
+
metadata: {
|
|
30
|
+
baseUrl: config.baseUrl,
|
|
31
|
+
timestamp: new Date().toISOString(),
|
|
32
|
+
crawlSettings: config.crawlSettings,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
let urlListPath;
|
|
36
|
+
if (config.urlListPath) {
|
|
37
|
+
urlListPath = await snapshotRepository.generateUrlList(config.name, config.urlListPath);
|
|
38
|
+
}
|
|
39
|
+
const duration = Date.now() - startTime;
|
|
40
|
+
return {
|
|
41
|
+
status: "success",
|
|
42
|
+
snapshotId: config.name,
|
|
43
|
+
timestamp: new Date().toISOString(),
|
|
44
|
+
baseUrl: config.baseUrl,
|
|
45
|
+
pageCount,
|
|
46
|
+
errors: errors,
|
|
47
|
+
metadata: {
|
|
48
|
+
crawlSettings: config.crawlSettings,
|
|
49
|
+
durationMs: duration,
|
|
50
|
+
},
|
|
51
|
+
urlListPath,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
errors.push({
|
|
56
|
+
url: config.baseUrl,
|
|
57
|
+
message: error instanceof Error ? error.message : "Unknown error occurred",
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
status: "failed",
|
|
61
|
+
snapshotId: config.name,
|
|
62
|
+
message: error instanceof Error ? error.message : "Unknown error occurred",
|
|
63
|
+
errors,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/snapshot/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { gunzip } from "zlib";
|
|
4
|
+
import { logger } from "../../lib/logger";
|
|
5
|
+
export const createIndexHandler = (comparisonDir) => {
|
|
6
|
+
return async (req, res) => {
|
|
7
|
+
try {
|
|
8
|
+
const indexPath = path.join(comparisonDir, "index.json");
|
|
9
|
+
const fileData = await fs.readFile(indexPath, "utf-8");
|
|
10
|
+
const indexData = JSON.parse(fileData);
|
|
11
|
+
const comparisonName = path.basename(comparisonDir);
|
|
12
|
+
const pages = Object.entries(indexData.urls).map(([url, page]) => ({
|
|
13
|
+
url,
|
|
14
|
+
...page,
|
|
15
|
+
}));
|
|
16
|
+
const pagesWithDifferences = pages.filter((p) => p.hasDifferences);
|
|
17
|
+
const unchangedPages = pages.filter((p) => !p.hasDifferences);
|
|
18
|
+
res.render("index", {
|
|
19
|
+
comparisonName,
|
|
20
|
+
pagesWithDifferences,
|
|
21
|
+
unchangedPages,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
logger.error({ err: error }, "Error reading index file");
|
|
26
|
+
res.status(500).send("Error reading comparison data");
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export const createDiffHandler = (comparisonDir) => {
|
|
31
|
+
return async (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
const pageUrl = req.query.page;
|
|
34
|
+
if (!pageUrl) {
|
|
35
|
+
res.status(400).send("Page URL is required");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const indexPath = path.join(comparisonDir, "index.json");
|
|
39
|
+
const fileData = await fs.readFile(indexPath, "utf-8");
|
|
40
|
+
const indexData = JSON.parse(fileData);
|
|
41
|
+
const comparisonName = path.basename(comparisonDir);
|
|
42
|
+
const page = indexData.urls[pageUrl];
|
|
43
|
+
if (!page) {
|
|
44
|
+
res.status(404).send("Page not found");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const diffPath = path.join(comparisonDir, "diffs", page.filename);
|
|
48
|
+
const compressedData = await fs.readFile(diffPath);
|
|
49
|
+
const decompressedData = await new Promise((resolve, reject) => {
|
|
50
|
+
gunzip(compressedData, (err, data) => {
|
|
51
|
+
if (err)
|
|
52
|
+
reject(err);
|
|
53
|
+
else
|
|
54
|
+
resolve(data);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
const diffData = JSON.parse(decompressedData.toString());
|
|
58
|
+
res.render("diff", {
|
|
59
|
+
comparisonName,
|
|
60
|
+
pageUrl,
|
|
61
|
+
diffData,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
logger.error({ err: error }, "Error reading diff file");
|
|
66
|
+
res.status(500).send("Error reading diff data");
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=index.handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.handlers.js","sourceRoot":"","sources":["../../../src/core/view/index.handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { logger } from "../../lib/logger";
|
|
5
|
+
import { findRootDir } from "../../lib/root";
|
|
6
|
+
import { createDiffHandler, createIndexHandler } from "./index.handlers";
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
export async function startViewServer(comparisonName, port = 8080) {
|
|
10
|
+
const app = express();
|
|
11
|
+
const rootDir = await findRootDir();
|
|
12
|
+
const comparisonDir = path.join(rootDir, "comparisons", comparisonName);
|
|
13
|
+
app.set("view engine", "ejs");
|
|
14
|
+
app.set("views", path.join(__dirname, "..", "..", "views"));
|
|
15
|
+
app.get("/", createIndexHandler(comparisonDir));
|
|
16
|
+
app.get("/diff", createDiffHandler(comparisonDir));
|
|
17
|
+
app.use(express.static(path.join(__dirname, "..", "..", "public")));
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const server = app.listen(port, () => {
|
|
20
|
+
// Once listening, we no longer need the startup error handler
|
|
21
|
+
server.removeListener('error', startupErrorHandler);
|
|
22
|
+
logger.info(`🌐 View server started at http://localhost:${port}`);
|
|
23
|
+
resolve(server);
|
|
24
|
+
});
|
|
25
|
+
// Define a specific error handler for startup
|
|
26
|
+
const startupErrorHandler = (err) => {
|
|
27
|
+
if (err.code === 'EADDRINUSE') {
|
|
28
|
+
reject(new Error(`Port ${port} is already in use.`));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
reject(err);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
server.on('error', startupErrorHandler);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export async function startCliViewServer(comparisonName, port) {
|
|
38
|
+
const server = await startViewServer(comparisonName, port);
|
|
39
|
+
logger.info("Press Ctrl+C to stop the server");
|
|
40
|
+
const shutdown = () => {
|
|
41
|
+
logger.info("\nGracefully shutting down. Please wait...");
|
|
42
|
+
server.close(() => {
|
|
43
|
+
logger.info("✅ Server has been shut down.");
|
|
44
|
+
process.exit(0);
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
process.on("SIGINT", shutdown);
|
|
48
|
+
process.on("SIGTERM", shutdown);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/view/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from "./api/index";
|
|
2
|
+
export { createSnapshotFromConfig, listSnapshots, runComparison } from "./api/index";
|
|
3
|
+
export * from "./core/view";
|
|
4
|
+
export { startCliViewServer } from "./core/view";
|
|
5
|
+
export * from "./lib/logger";
|
|
6
|
+
export { createLogger, logger } from "./lib/logger";
|
|
7
|
+
export * from "./types/api";
|
|
8
|
+
export type { ComparisonConfig, SnapshotConfig, SnapshotResult } from "./types/api";
|
|
9
|
+
export * from "./types/compare";
|
|
10
|
+
export * from "./types/crawler";
|
|
11
|
+
export type { CrawlError } from "./types/crawler";
|
|
12
|
+
export * from "./types/rules";
|
|
13
|
+
export * from "./types/snapshot";
|
|
14
|
+
export type { SnapshotSummary } from "./types/snapshot";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./api/index";
|
|
2
|
+
// Explicit exports for CLI and other consumers
|
|
3
|
+
export { createSnapshotFromConfig, listSnapshots, runComparison } from "./api/index";
|
|
4
|
+
export * from "./core/view";
|
|
5
|
+
export { startCliViewServer } from "./core/view";
|
|
6
|
+
export * from "./lib/logger";
|
|
7
|
+
export { createLogger, logger } from "./lib/logger";
|
|
8
|
+
export * from "./types/api";
|
|
9
|
+
export * from "./types/compare";
|
|
10
|
+
export * from "./types/crawler";
|
|
11
|
+
export * from "./types/rules";
|
|
12
|
+
export * from "./types/snapshot";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,+CAA+C;AAC/C,OAAO,EACH,wBAAwB,EACxB,aAAa,EACb,aAAa,EAChB,MAAM,aAAa,CAAC;AACrB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACpD,cAAc,aAAa,CAAC;AAM5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pino from "pino";
|
|
2
|
+
// Determine the log level from the environment variable, defaulting to 'info'
|
|
3
|
+
const logLevel = process.env.LOG_LEVEL || "info";
|
|
4
|
+
// Create a logger with the specified configuration
|
|
5
|
+
export function createLogger(config = {}) {
|
|
6
|
+
const { useJsonLogs = false, level = logLevel } = config;
|
|
7
|
+
// Configure transport based on useJsonLogs flag
|
|
8
|
+
const transport = useJsonLogs
|
|
9
|
+
? undefined // Use standard JSON logging
|
|
10
|
+
: {
|
|
11
|
+
target: "pino-pretty",
|
|
12
|
+
options: {
|
|
13
|
+
colorize: true,
|
|
14
|
+
translateTime: "SYS:standard",
|
|
15
|
+
ignore: "pid,hostname",
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
return pino({
|
|
19
|
+
level,
|
|
20
|
+
transport,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
// Create the default shared logger instance (for backward compatibility)
|
|
24
|
+
// This will use pretty logging by default for CLI, but can be overridden
|
|
25
|
+
export const logger = createLogger({ useJsonLogs: false });
|
|
26
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB,8EAA8E;AAC9E,MAAM,QAAQ,GAAc,OAAO,CAAC,GAAG,CAAC,SAAsB,IAAI,MAAM,CAAC;AAQzE,mDAAmD;AACnD,MAAM,UAAU,YAAY,CAAC,SAAuB,EAAE;IACpD,MAAM,EAAE,WAAW,GAAG,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC;IAEzD,gDAAgD;IAChD,MAAM,SAAS,GAAG,WAAW;QAC3B,CAAC,CAAC,SAAS,CAAC,4BAA4B;QACxC,CAAC,CAAC;YACE,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE;gBACP,QAAQ,EAAE,IAAI;gBACd,aAAa,EAAE,cAAc;gBAC7B,MAAM,EAAE,cAAc;aACvB;SACF,CAAC;IAEN,OAAO,IAAI,CAAC;QACV,KAAK;QACL,SAAS;KACV,CAAC,CAAC;AACL,CAAC;AAED,yEAAyE;AACzE,yEAAyE;AACzE,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC"}
|
package/dist/lib/root.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { findUp, pathExists } from "find-up";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
/**
|
|
5
|
+
* Finds the root directory of the project by looking for a package.json file
|
|
6
|
+
* that contains a "workspaces" configuration.
|
|
7
|
+
*
|
|
8
|
+
* @returns The absolute path to the root directory
|
|
9
|
+
*/
|
|
10
|
+
export async function findRootDir() {
|
|
11
|
+
const rootPackageJsonPath = await findUp(async (directory) => {
|
|
12
|
+
const packageJsonPath = path.join(directory, "package.json");
|
|
13
|
+
const exists = await pathExists(packageJsonPath);
|
|
14
|
+
if (exists) {
|
|
15
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
16
|
+
if (packageJson.workspaces) {
|
|
17
|
+
return packageJsonPath;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}, { type: "file" });
|
|
21
|
+
return rootPackageJsonPath
|
|
22
|
+
? path.dirname(rootPackageJsonPath)
|
|
23
|
+
: process.cwd();
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/lib/root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,mBAAmB,GAAG,MAAM,MAAM,CACtC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClB,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,eAAe,CAAC,CAAC;QACjD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,CAC3C,CAAC;YACF,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;gBAC3B,OAAO,eAAe,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC,EACD,EAAE,IAAI,EAAE,MAAM,EAAE,CACjB,CAAC;IAEF,OAAO,mBAAmB;QACxB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC;QACnC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;AACpB,CAAC"}
|