@cleaver/breakcheck-core 0.2.4 → 0.3.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 (69) hide show
  1. package/README.md +134 -1
  2. package/dist/api/index.d.ts +23 -5
  3. package/dist/api/index.js +71 -10
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/core/check/index.d.ts +9 -0
  6. package/dist/core/check/index.js +116 -0
  7. package/dist/core/check/index.js.map +1 -0
  8. package/dist/core/compare/classes/ComparisonRepository.d.ts +11 -0
  9. package/dist/core/compare/classes/ComparisonRepository.js +26 -4
  10. package/dist/core/compare/classes/ComparisonRepository.js.map +1 -1
  11. package/dist/core/compare/index.js +22 -6
  12. package/dist/core/compare/index.js.map +1 -1
  13. package/dist/core/crawler/implementations/cheerio.d.ts +1 -1
  14. package/dist/core/crawler/implementations/cheerio.js +2 -2
  15. package/dist/core/crawler/implementations/cheerio.js.map +1 -1
  16. package/dist/core/crawler/implementations/playwright.d.ts +1 -1
  17. package/dist/core/crawler/implementations/playwright.js +2 -2
  18. package/dist/core/crawler/implementations/playwright.js.map +1 -1
  19. package/dist/core/crawler/index.d.ts +13 -2
  20. package/dist/core/crawler/index.js +25 -6
  21. package/dist/core/crawler/index.js.map +1 -1
  22. package/dist/core/crawler/url-paths.d.ts +18 -0
  23. package/dist/core/crawler/url-paths.js +113 -0
  24. package/dist/core/crawler/url-paths.js.map +1 -0
  25. package/dist/core/rules/RulesDsl.js +273 -56
  26. package/dist/core/rules/RulesDsl.js.map +1 -1
  27. package/dist/core/rules/RulesEngine.js +56 -17
  28. package/dist/core/rules/RulesEngine.js.map +1 -1
  29. package/dist/core/snapshot/classes/SnapshotRepository.d.ts +15 -2
  30. package/dist/core/snapshot/classes/SnapshotRepository.js +110 -9
  31. package/dist/core/snapshot/classes/SnapshotRepository.js.map +1 -1
  32. package/dist/core/snapshot/index.d.ts +3 -2
  33. package/dist/core/snapshot/index.js +41 -3
  34. package/dist/core/snapshot/index.js.map +1 -1
  35. package/dist/core/snapshot/route-coverage.d.ts +3 -0
  36. package/dist/core/snapshot/route-coverage.js +18 -0
  37. package/dist/core/snapshot/route-coverage.js.map +1 -0
  38. package/dist/core/snapshot/update-routes.d.ts +4 -0
  39. package/dist/core/snapshot/update-routes.js +58 -0
  40. package/dist/core/snapshot/update-routes.js.map +1 -0
  41. package/dist/core/view/index.d.ts +2 -1
  42. package/dist/core/view/index.js +4 -4
  43. package/dist/core/view/index.js.map +1 -1
  44. package/dist/index.d.ts +7 -2
  45. package/dist/index.js +5 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/lib/config.d.ts +18 -0
  48. package/dist/lib/config.js +363 -0
  49. package/dist/lib/config.js.map +1 -0
  50. package/dist/lib/crawlee-logger.d.ts +2 -0
  51. package/dist/lib/crawlee-logger.js +43 -0
  52. package/dist/lib/crawlee-logger.js.map +1 -0
  53. package/dist/lib/logger.d.ts +8 -3
  54. package/dist/lib/logger.js +11 -1
  55. package/dist/lib/logger.js.map +1 -1
  56. package/dist/lib/root.d.ts +1 -1
  57. package/dist/lib/root.js +17 -6
  58. package/dist/lib/root.js.map +1 -1
  59. package/dist/lib/storage.d.ts +12 -0
  60. package/dist/lib/storage.js +76 -0
  61. package/dist/lib/storage.js.map +1 -0
  62. package/dist/types/api.d.ts +47 -0
  63. package/dist/types/compare.d.ts +5 -2
  64. package/dist/types/config.d.ts +72 -0
  65. package/dist/types/config.js +2 -0
  66. package/dist/types/config.js.map +1 -0
  67. package/dist/types/rules.d.ts +8 -3
  68. package/dist/types/rules.js.map +1 -1
  69. package/package.json +11 -10
package/README.md CHANGED
@@ -20,6 +20,63 @@ Node.js 22 or newer is required.
20
20
 
21
21
  ---
22
22
 
23
+ ## Project configuration and storage
24
+
25
+ The core package does not implicitly read `breakcheck.config.json`; library
26
+ callers choose configuration explicitly. `resolveProjectConfig` loads and
27
+ validates version-1 and version-2 JSON files, while `writeProjectConfig`
28
+ creates one with exclusive file creation:
29
+
30
+ ```typescript
31
+ import {
32
+ resolveProjectConfig,
33
+ writeProjectConfig,
34
+ } from "@cleaver/breakcheck-core";
35
+
36
+ const project = await resolveProjectConfig({
37
+ configPath: "./breakcheck.config.json",
38
+ });
39
+ await writeProjectConfig("./new-breakcheck.config.json", {
40
+ version: 1,
41
+ baseUrl: "https://example.com",
42
+ storageDir: ".breakcheck",
43
+ });
44
+ ```
45
+
46
+ Config paths resolve relative to the config file. `storageDir` is the artifact
47
+ root containing `snapshots/` and `comparisons`; Crawlee's temporary datasets
48
+ and queues are separate. An omitted storage option in the operational API
49
+ keeps the legacy `findRootDir()` behavior. To use another root, pass the same
50
+ `{ storageDir }` option to each operation:
51
+
52
+ ```typescript
53
+ const storage = { storageDir: "/work/site/.breakcheck" };
54
+ await createSnapshotFromConfig(snapshotConfig, storage);
55
+ await listSnapshots(storage);
56
+ await runComparison(comparisonConfig, storage);
57
+ await startViewServer("v1-vs-v2-comparison", 8080, storage);
58
+ ```
59
+
60
+ Changing the storage root does not migrate existing artifacts.
61
+
62
+ ### Compiling a rules DSL
63
+
64
+ Use `compileRulesDsl` to parse and validate a directory containing
65
+ `rules.breakcheck`. It returns the intermediate `RulesDocument` shape used
66
+ by the rules engine; the runtime-only `Ruleset.name` metadata is not included.
67
+
68
+ ```javascript
69
+ import { compileRulesDsl } from "@cleaver/breakcheck-core";
70
+
71
+ const document = await compileRulesDsl("./my-rules");
72
+ console.log(JSON.stringify(document, null, 2));
73
+ ```
74
+
75
+ Relative directories resolve from `process.cwd()`. Syntax and engine
76
+ validation errors reject the promise before a document is returned.
77
+
78
+ ---
79
+
23
80
  ## API Usage
24
81
 
25
82
  The `@cleaver/breakcheck-core` library exposes a set of simple, asynchronous functions to manage the entire workflow.
@@ -66,6 +123,82 @@ async function takeSnapshot() {
66
123
  takeSnapshot();
67
124
  ```
68
125
 
126
+ #### Crawling an exact path list
127
+
128
+ Programmatic callers can set `SnapshotConfig.urlPaths` to supply an in-memory
129
+ manifest. It uses the same root-relative path format as `--url-file` and
130
+ switches off link discovery. Only the listed paths are requested; the base
131
+ path `/` must be included explicitly when needed.
132
+
133
+ ```javascript
134
+ const snapshotConfig = {
135
+ baseUrl: "https://example.com",
136
+ name: "selected-pages",
137
+ urlPaths: ["/", "/about-me", "/blog/the-nephew-effect"],
138
+ crawlSettings: {
139
+ baseUrl: "https://example.com",
140
+ crawlerType: "cheerio",
141
+ maxConcurrency: 5,
142
+ },
143
+ };
144
+
145
+ const result = await createSnapshotFromConfig(snapshotConfig);
146
+ ```
147
+
148
+ `urlPaths` is validated against `baseUrl`; entries must be root-relative and
149
+ same-origin, and duplicates are removed in first-seen order. An invalid or
150
+ empty manifest fails before crawling. File and stdin reading are CLI concerns;
151
+ the core API receives the already-created array.
152
+
153
+ ### Exact-route regression checks
154
+
155
+ `runSiteCheck` captures only the supplied routes, verifies crawl coverage, then
156
+ compares them with a stored baseline using the same comparison engine as
157
+ `runComparison`. Added or removed routes count as differences. The core API
158
+ does not start a site; the CLI handles preparation and server lifecycle. The
159
+ route list scopes page comparisons and added/removed route results, so snapshot
160
+ changes outside the manifest do not affect a route-scoped check.
161
+
162
+ ```typescript
163
+ import { runSiteCheck } from "@cleaver/breakcheck-core";
164
+
165
+ const result = await runSiteCheck(
166
+ {
167
+ baseUrl: "http://127.0.0.1:4000",
168
+ name: "check-local-run-1",
169
+ baselineSnapshotId: "site-baseline",
170
+ routes: ["/", "/about"],
171
+ crawlSettings: {
172
+ baseUrl: "http://127.0.0.1:4000",
173
+ crawlerType: "cheerio",
174
+ maxConcurrency: 5,
175
+ },
176
+ ruleset: "./rules",
177
+ },
178
+ { storageDir: "./.breakcheck" },
179
+ );
180
+ ```
181
+
182
+ The result status is `passed`, `differences`, or `failed`. The generated after
183
+ snapshot and detailed comparison remain in the configured storage directory.
184
+ `updateSnapshotRoutesFromConfig` is a separate API for explicitly replacing
185
+ selected baseline routes; it stages and validates the new pages before
186
+ committing them and preserves routes outside the selected list. Pass an
187
+ `AbortSignal` in `OperationOptions` to cancel an active crawl; baseline updates
188
+ check cancellation before replacing the stored routes.
189
+
190
+ ### Configuring Logs
191
+
192
+ The core package uses pretty Pino logs by default. Call `configureLogger` before
193
+ starting a workflow to select JSON output or a different log level. Crawlee
194
+ messages emitted during snapshotting use the same configured logger.
195
+
196
+ ```javascript
197
+ import { configureLogger } from "@cleaver/breakcheck-core";
198
+
199
+ configureLogger({ useJsonLogs: true });
200
+ ```
201
+
69
202
  ### Listing Snapshots
70
203
 
71
204
  You can get a list of all locally saved snapshots using the `listSnapshots` function.
@@ -84,7 +217,7 @@ async function showSnapshots() {
84
217
  console.log("Available Snapshots:");
85
218
  snapshots.forEach((snapshot) => {
86
219
  console.log(
87
- `- ${snapshot.name} (${snapshot.pageCount} pages, created on ${snapshot.date})`
220
+ `- ${snapshot.name} (${snapshot.pageCount} pages, created on ${snapshot.date})`,
88
221
  );
89
222
  });
90
223
  }
@@ -1,10 +1,15 @@
1
1
  import { logger } from "../lib/logger.js";
2
- import type { ComparisonConfig, ComparisonSummary, SnapshotConfig, SnapshotResult } from "../types/api.js";
2
+ import type { ComparisonConfig, ComparisonSummary, SiteCheckConfig, SiteCheckResult, SnapshotRouteUpdateResult, SnapshotConfig, SnapshotResult, OperationOptions, StorageOptions } from "../types/api.js";
3
+ import type { RulesDocument } from "../types/rules.js";
3
4
  /**
4
5
  * Creates a snapshot of a website based on the provided configuration.
5
6
  * Orchestrates calls to Crawler and Snapshot Manager.
6
7
  */
7
- export declare function createSnapshotFromConfig(config: SnapshotConfig): Promise<SnapshotResult>;
8
+ export declare function createSnapshotFromConfig(config: SnapshotConfig, options?: OperationOptions): Promise<SnapshotResult>;
9
+ /** Runs one exact-route regression check against a stored baseline snapshot. */
10
+ export declare function runSiteCheck(config: SiteCheckConfig, options?: OperationOptions): Promise<SiteCheckResult>;
11
+ /** Crawls selected routes and atomically merges them into a stored baseline. */
12
+ export declare function updateSnapshotRoutesFromConfig(config: SnapshotConfig, baselineSnapshotId: string, options?: OperationOptions): Promise<SnapshotRouteUpdateResult>;
8
13
  /**
9
14
  * Starts a snapshot job and returns the job status.
10
15
  */
@@ -16,9 +21,22 @@ export declare function createSnapshotFromConfig(config: SnapshotConfig): Promis
16
21
  * Orchestrates calls to Snapshot Manager, Rules Engine Parser (if needed),
17
22
  * DOM Processor, and Diff Engine.
18
23
  */
19
- export declare function runComparison(config: ComparisonConfig): Promise<ComparisonSummary>;
24
+ export declare function runComparison(config: ComparisonConfig, options?: StorageOptions): Promise<ComparisonSummary>;
25
+ /**
26
+ * Parses and validates a rules.breakcheck directory into the intermediate
27
+ * JSON document used by the rules engine.
28
+ */
29
+ export declare function compileRulesDsl(rulesDirectory: string): Promise<RulesDocument>;
20
30
  /**
21
31
  * Lists all available snapshots with their details
22
32
  */
23
- export declare function listSnapshots(): Promise<import("../index.js").SnapshotSummary[]>;
24
- export { logger, type ComparisonConfig, type SnapshotConfig };
33
+ export declare function listSnapshots(options?: StorageOptions): Promise<import("../index.js").SnapshotSummary[]>;
34
+ /** Deletes one named snapshot. */
35
+ export declare function deleteSnapshot(name: string, options?: StorageOptions): Promise<boolean>;
36
+ /** Deletes all snapshots and returns the names that were removed. */
37
+ export declare function deleteAllSnapshots(options?: StorageOptions): Promise<string[]>;
38
+ /** Deletes one named comparison. */
39
+ export declare function deleteComparison(name: string, options?: StorageOptions): Promise<boolean>;
40
+ /** Deletes all comparisons and returns the names that were removed. */
41
+ export declare function deleteAllComparisons(options?: StorageOptions): Promise<string[]>;
42
+ export { logger, type ComparisonConfig, type OperationOptions, type SiteCheckConfig, type SiteCheckResult, type SnapshotRouteUpdateResult, type SnapshotConfig, type StorageOptions, };
package/dist/api/index.js CHANGED
@@ -2,14 +2,37 @@ import { compareSnapshots } from "../core/compare/index.js";
2
2
  import { ComparisonRepository } from "../core/compare/classes/ComparisonRepository.js";
3
3
  import { RulesEngine } from "../core/rules/RulesEngine.js";
4
4
  import { createSnapshot, SnapshotRepository } from "../core/snapshot/index.js";
5
+ import { runSiteCheck as runSiteCheckWorkflow } from "../core/check/index.js";
6
+ import { updateSnapshotRoutes } from "../core/snapshot/update-routes.js";
5
7
  import { logger } from "../lib/logger.js";
8
+ import { resolveStorageContext } from "../lib/storage.js";
9
+ import { processRulesDsl } from "../core/rules/RulesDsl.js";
6
10
  /**
7
11
  * Creates a snapshot of a website based on the provided configuration.
8
12
  * Orchestrates calls to Crawler and Snapshot Manager.
9
13
  */
10
- export async function createSnapshotFromConfig(config) {
11
- const snapshotRepository = await SnapshotRepository.create();
12
- return createSnapshot(config, snapshotRepository);
14
+ export async function createSnapshotFromConfig(config, options = {}) {
15
+ const storageContext = await resolveStorageContext(options.storageDir);
16
+ const snapshotRepository = SnapshotRepository.createWithStorageContext(storageContext);
17
+ return createSnapshot(config, snapshotRepository, options.signal);
18
+ }
19
+ /** Runs one exact-route regression check against a stored baseline snapshot. */
20
+ export async function runSiteCheck(config, options = {}) {
21
+ const storageContext = await resolveStorageContext(options.storageDir);
22
+ const snapshotRepository = SnapshotRepository.createWithStorageContext(storageContext);
23
+ return runSiteCheckWorkflow(config, {
24
+ snapshotRepository,
25
+ createSnapshot: (snapshotConfig, signal) => createSnapshot(snapshotConfig, snapshotRepository, signal),
26
+ runComparison: (comparisonConfig) => runComparison(comparisonConfig, {
27
+ storageDir: storageContext.storageDir,
28
+ }),
29
+ }, options.signal);
30
+ }
31
+ /** Crawls selected routes and atomically merges them into a stored baseline. */
32
+ export async function updateSnapshotRoutesFromConfig(config, baselineSnapshotId, options = {}) {
33
+ const storageContext = await resolveStorageContext(options.storageDir);
34
+ const snapshotRepository = SnapshotRepository.createWithStorageContext(storageContext);
35
+ return updateSnapshotRoutes(config, baselineSnapshotId, snapshotRepository, (snapshotConfig, signal) => createSnapshot(snapshotConfig, snapshotRepository, signal), options.signal);
13
36
  }
14
37
  /**
15
38
  * Starts a snapshot job and returns the job status.
@@ -28,16 +51,17 @@ export async function createSnapshotFromConfig(config) {
28
51
  * Orchestrates calls to Snapshot Manager, Rules Engine Parser (if needed),
29
52
  * DOM Processor, and Diff Engine.
30
53
  */
31
- export async function runComparison(config) {
32
- const snapshotRepository = await SnapshotRepository.create();
54
+ export async function runComparison(config, options = {}) {
55
+ const storageContext = await resolveStorageContext(options.storageDir);
56
+ const snapshotRepository = SnapshotRepository.createWithStorageContext(storageContext);
33
57
  const rulesEngine = await RulesEngine.create(config.ruleset);
34
- const comparisonRepository = await ComparisonRepository.create(config.comparisonName, {
58
+ const comparisonRepository = await ComparisonRepository.createWithStorageContext(config.comparisonName, {
35
59
  beforeSnapshotId: config.beforeSnapshotId,
36
60
  afterSnapshotId: config.afterSnapshotId,
37
61
  rulesUsedIdentifier: typeof config.ruleset === "string"
38
62
  ? config.ruleset
39
63
  : config.ruleset?.name,
40
- });
64
+ }, storageContext);
41
65
  const diff = await compareSnapshots(config, snapshotRepository, comparisonRepository, rulesEngine);
42
66
  // Convert SnapshotDiff to ComparisonSummary
43
67
  return {
@@ -58,12 +82,49 @@ export async function runComparison(config) {
58
82
  resultsPath: diff.resultsPath,
59
83
  };
60
84
  }
85
+ /**
86
+ * Parses and validates a rules.breakcheck directory into the intermediate
87
+ * JSON document used by the rules engine.
88
+ */
89
+ export async function compileRulesDsl(rulesDirectory) {
90
+ const ruleset = await processRulesDsl(rulesDirectory);
91
+ await RulesEngine.create(ruleset);
92
+ return {
93
+ rules: ruleset.rules,
94
+ regions: ruleset.regions ?? [],
95
+ };
96
+ }
61
97
  /**
62
98
  * Lists all available snapshots with their details
63
99
  */
64
- export async function listSnapshots() {
65
- const snapshotRepository = await SnapshotRepository.create();
100
+ export async function listSnapshots(options = {}) {
101
+ const storageContext = await resolveStorageContext(options.storageDir);
102
+ const snapshotRepository = SnapshotRepository.createWithStorageContext(storageContext);
66
103
  return snapshotRepository.listSnapshots();
67
104
  }
68
- export { logger };
105
+ /** Deletes one named snapshot. */
106
+ export async function deleteSnapshot(name, options = {}) {
107
+ const storageContext = await resolveStorageContext(options.storageDir);
108
+ const snapshotRepository = SnapshotRepository.createWithStorageContext(storageContext);
109
+ return snapshotRepository.deleteSnapshot(name);
110
+ }
111
+ /** Deletes all snapshots and returns the names that were removed. */
112
+ export async function deleteAllSnapshots(options = {}) {
113
+ const storageContext = await resolveStorageContext(options.storageDir);
114
+ const snapshotRepository = SnapshotRepository.createWithStorageContext(storageContext);
115
+ return snapshotRepository.deleteAllSnapshots();
116
+ }
117
+ /** Deletes one named comparison. */
118
+ export async function deleteComparison(name, options = {}) {
119
+ const storageContext = await resolveStorageContext(options.storageDir);
120
+ const comparisonRepository = ComparisonRepository.openWithStorageContext(storageContext);
121
+ return comparisonRepository.deleteComparison(name);
122
+ }
123
+ /** Deletes all comparisons and returns the names that were removed. */
124
+ export async function deleteAllComparisons(options = {}) {
125
+ const storageContext = await resolveStorageContext(options.storageDir);
126
+ const comparisonRepository = ComparisonRepository.openWithStorageContext(storageContext);
127
+ return comparisonRepository.deleteAllComparisons();
128
+ }
129
+ export { logger, };
69
130
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAQ1C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAAsB;IAEtB,MAAM,kBAAkB,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,CAAC;IAC7D,OAAO,cAAc,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,0BAA0B;AAC1B,2BAA2B;AAC3B,2CAA2C;AAE3C;;GAEG;AACH,8BAA8B;AAC9B,kBAAkB;AAClB,2CAA2C;AAE3C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAwB;IAExB,MAAM,kBAAkB,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,CAAC;IAC7D,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,oBAAoB,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAC5D,MAAM,CAAC,cAAc,EACrB;QACE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,mBAAmB,EACjB,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;YAChC,CAAC,CAAC,MAAM,CAAC,OAAO;YAChB,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI;KAC3B,CACF,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,gBAAgB,CACjC,MAAM,EACN,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,CACZ,CAAC;IAEF,4CAA4C;IAC5C,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,cAAc;QACnC,MAAM,EAAE,WAAW;QACnB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;QAC3C,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;QAC/C,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,uBAAuB,EAAE,EAAE;QAC3B,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,kBAAkB,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,CAAC;IAC7D,OAAO,kBAAkB,CAAC,aAAa,EAAE,CAAC;AAC5C,CAAC;AAED,OAAO,EAAE,MAAM,EAA8C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ComparisonConfig, ComparisonSummary, SiteCheckConfig, SiteCheckResult, SnapshotConfig, SnapshotResult } from "../../types/api.js";
2
+ import type { SnapshotRepository } from "../snapshot/classes/SnapshotRepository.js";
3
+ export interface SiteCheckOperations {
4
+ snapshotRepository: SnapshotRepository;
5
+ createSnapshot: (config: SnapshotConfig, signal?: AbortSignal) => Promise<SnapshotResult>;
6
+ runComparison: (config: ComparisonConfig) => Promise<ComparisonSummary>;
7
+ }
8
+ /** Runs an exact-route regression check using the supplied core operations. */
9
+ export declare function runSiteCheck(config: SiteCheckConfig, operations: SiteCheckOperations, signal?: AbortSignal): Promise<SiteCheckResult>;
@@ -0,0 +1,116 @@
1
+ import { resolveUrlPaths } from "../crawler/url-paths.js";
2
+ import { validateSnapshotRoutes } from "../snapshot/route-coverage.js";
3
+ /** Runs an exact-route regression check using the supplied core operations. */
4
+ export async function runSiteCheck(config, operations, signal) {
5
+ signal?.throwIfAborted();
6
+ if (config.name === config.baselineSnapshotId) {
7
+ throw new Error("The check snapshot name must differ from the baseline snapshot ID.");
8
+ }
9
+ if ((config.crawlSettings.includePatterns?.length ?? 0) > 0 ||
10
+ (config.crawlSettings.excludePatterns?.length ?? 0) > 0) {
11
+ throw new Error("Exact-route checks cannot use crawl includePatterns or excludePatterns.");
12
+ }
13
+ const manifest = resolveUrlPaths(config.baseUrl, config.routes);
14
+ if (manifest.issues.length > 0) {
15
+ throw new Error(manifest.issues.map((issue) => issue.message).join("; "));
16
+ }
17
+ await operations.snapshotRepository.loadSnapshot(config.baselineSnapshotId);
18
+ signal?.throwIfAborted();
19
+ const snapshotResult = await operations.createSnapshot({
20
+ baseUrl: config.baseUrl,
21
+ name: config.name,
22
+ urlPaths: manifest.paths,
23
+ crawlSettings: {
24
+ ...config.crawlSettings,
25
+ baseUrl: config.baseUrl,
26
+ includePatterns: [],
27
+ excludePatterns: [],
28
+ },
29
+ }, signal);
30
+ if (snapshotResult.status === "failed") {
31
+ return {
32
+ status: "failed",
33
+ baseUrl: config.baseUrl,
34
+ baselineSnapshotId: config.baselineSnapshotId,
35
+ snapshotId: snapshotResult.snapshotId,
36
+ routes: manifest.paths,
37
+ crawlErrors: snapshotResult.errors.map((error) => ({
38
+ url: config.baseUrl,
39
+ message: error.message,
40
+ ...(error.statusCode === undefined
41
+ ? {}
42
+ : { statusCode: error.statusCode }),
43
+ })),
44
+ errors: [snapshotResult.message],
45
+ };
46
+ }
47
+ signal?.throwIfAborted();
48
+ const afterSnapshot = await operations.snapshotRepository.loadSnapshot(snapshotResult.snapshotId);
49
+ const failures = snapshotResult.errors.map((error) => `${error.url}: ${error.message}`);
50
+ failures.push(...(await validateSnapshotRoutes(afterSnapshot, manifest.paths)));
51
+ signal?.throwIfAborted();
52
+ if (failures.length > 0) {
53
+ return {
54
+ status: "failed",
55
+ baseUrl: config.baseUrl,
56
+ baselineSnapshotId: config.baselineSnapshotId,
57
+ snapshotId: snapshotResult.snapshotId,
58
+ routes: manifest.paths,
59
+ crawlErrors: snapshotResult.errors,
60
+ errors: failures,
61
+ };
62
+ }
63
+ let comparison;
64
+ try {
65
+ comparison = await operations.runComparison({
66
+ beforeSnapshotId: config.baselineSnapshotId,
67
+ afterSnapshotId: snapshotResult.snapshotId,
68
+ comparisonName: config.name,
69
+ ruleset: config.ruleset,
70
+ urls: manifest.paths,
71
+ });
72
+ }
73
+ catch (error) {
74
+ return {
75
+ status: "failed",
76
+ baseUrl: config.baseUrl,
77
+ baselineSnapshotId: config.baselineSnapshotId,
78
+ snapshotId: snapshotResult.snapshotId,
79
+ routes: manifest.paths,
80
+ crawlErrors: [],
81
+ errors: [error instanceof Error ? error.message : String(error)],
82
+ };
83
+ }
84
+ if (signal?.aborted)
85
+ signal.throwIfAborted();
86
+ if (comparison.pagesWithErrors > 0) {
87
+ return {
88
+ status: "failed",
89
+ baseUrl: config.baseUrl,
90
+ baselineSnapshotId: config.baselineSnapshotId,
91
+ snapshotId: snapshotResult.snapshotId,
92
+ routes: manifest.paths,
93
+ crawlErrors: [],
94
+ errors: [
95
+ `Comparison could not process ${comparison.pagesWithErrors} page(s).`,
96
+ ],
97
+ comparison,
98
+ };
99
+ }
100
+ const status = comparison.pagesWithDifferences > 0 ||
101
+ comparison.newUrls.length > 0 ||
102
+ comparison.removedUrls.length > 0
103
+ ? "differences"
104
+ : "passed";
105
+ return {
106
+ status,
107
+ baseUrl: config.baseUrl,
108
+ baselineSnapshotId: config.baselineSnapshotId,
109
+ snapshotId: snapshotResult.snapshotId,
110
+ routes: manifest.paths,
111
+ crawlErrors: [],
112
+ errors: [],
113
+ comparison,
114
+ };
115
+ }
116
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/check/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { type StorageContext } from "../../../lib/storage.js";
1
2
  import { ComparisonIndex, ComparisonMetadata, PageDiff } from "../../../types/compare.js";
2
3
  export declare class ComparisonRepository {
3
4
  private readonly comparisonsDir;
@@ -9,6 +10,12 @@ export declare class ComparisonRepository {
9
10
  * Creates a new comparison repository instance and initializes it with the given name and metadata.
10
11
  */
11
12
  static create(name: string, metadata: ComparisonMetadata, comparisonsDir?: string): Promise<ComparisonRepository>;
13
+ /** Creates and initializes a comparison in a shared artifact context. */
14
+ static createWithStorageContext(name: string, metadata: ComparisonMetadata, storageContext: StorageContext): Promise<ComparisonRepository>;
15
+ /** Opens the comparison store without initializing a named comparison. */
16
+ static open(comparisonsDir?: string): Promise<ComparisonRepository>;
17
+ /** Opens a comparison store in a shared artifact context. */
18
+ static openWithStorageContext(storageContext: StorageContext): ComparisonRepository;
12
19
  /**
13
20
  * Initializes a new comparison, creating directories and saving initial metadata.
14
21
  */
@@ -22,4 +29,8 @@ export declare class ComparisonRepository {
22
29
  */
23
30
  finalizeComparison(): Promise<string>;
24
31
  getIndex(): ComparisonIndex;
32
+ /** Deletes one named comparison without removing the storage root. */
33
+ deleteComparison(name: string): Promise<boolean>;
34
+ /** Deletes all direct child comparison directories and returns their names. */
35
+ deleteAllComparisons(): Promise<string[]>;
25
36
  }
@@ -3,6 +3,7 @@ import * as path from "path";
3
3
  import { promisify } from "util";
4
4
  import * as zlib from "zlib";
5
5
  import { findRootDir } from "../../../lib/root.js";
6
+ import { deleteAllStorageEntries, deleteStorageEntry, resolveStorageEntry, } from "../../../lib/storage.js";
6
7
  const gzip = promisify(zlib.gzip);
7
8
  export class ComparisonRepository {
8
9
  comparisonsDir;
@@ -16,18 +17,31 @@ export class ComparisonRepository {
16
17
  * Creates a new comparison repository instance and initializes it with the given name and metadata.
17
18
  */
18
19
  static async create(name, metadata, comparisonsDir) {
19
- const rootDir = await findRootDir();
20
- const defaultComparisonsDir = path.join(rootDir, "comparisons");
21
- const instance = new ComparisonRepository(comparisonsDir || defaultComparisonsDir);
20
+ const instance = await ComparisonRepository.open(comparisonsDir);
22
21
  await instance.initialize(name, metadata);
23
22
  return instance;
24
23
  }
24
+ /** Creates and initializes a comparison in a shared artifact context. */
25
+ static async createWithStorageContext(name, metadata, storageContext) {
26
+ const instance = ComparisonRepository.openWithStorageContext(storageContext);
27
+ await instance.initialize(name, metadata);
28
+ return instance;
29
+ }
30
+ /** Opens the comparison store without initializing a named comparison. */
31
+ static async open(comparisonsDir) {
32
+ const resolvedComparisonsDir = comparisonsDir ?? path.join(await findRootDir(), "comparisons");
33
+ return new ComparisonRepository(resolvedComparisonsDir);
34
+ }
35
+ /** Opens a comparison store in a shared artifact context. */
36
+ static openWithStorageContext(storageContext) {
37
+ return new ComparisonRepository(storageContext.comparisonsDir);
38
+ }
25
39
  /**
26
40
  * Initializes a new comparison, creating directories and saving initial metadata.
27
41
  */
28
42
  async initialize(name, metadata) {
43
+ this.comparisonDir = resolveStorageEntry(this.comparisonsDir, name, "comparison");
29
44
  await fs.mkdir(this.comparisonsDir, { recursive: true });
30
- this.comparisonDir = path.join(this.comparisonsDir, name);
31
45
  this.diffsDir = path.join(this.comparisonDir, "diffs");
32
46
  // Clean up previous results
33
47
  await fs.rm(this.comparisonDir, { recursive: true, force: true });
@@ -73,5 +87,13 @@ export class ComparisonRepository {
73
87
  getIndex() {
74
88
  return this.index;
75
89
  }
90
+ /** Deletes one named comparison without removing the storage root. */
91
+ async deleteComparison(name) {
92
+ return deleteStorageEntry(this.comparisonsDir, name, "comparison");
93
+ }
94
+ /** Deletes all direct child comparison directories and returns their names. */
95
+ async deleteAllComparisons() {
96
+ return deleteAllStorageEntries(this.comparisonsDir, "comparison");
97
+ }
76
98
  }
77
99
  //# sourceMappingURL=ComparisonRepository.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ComparisonRepository.js","sourceRoot":"","sources":["../../../../src/core/compare/classes/ComparisonRepository.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,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAOnD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,OAAO,oBAAoB;IACd,cAAc,CAAS;IAChC,aAAa,CAAU;IACvB,QAAQ,CAAU;IAClB,KAAK,GAAoB,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAS,EAAE,CAAC;IAEnE,YAAoB,cAAsB;QACxC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,IAAY,EACZ,QAA4B,EAC5B,cAAuB;QAEvB,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;QACpC,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,IAAI,oBAAoB,CACvC,cAAc,IAAI,qBAAqB,CACxC,CAAC;QACF,MAAM,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,UAAU,CACtB,IAAY,EACZ,QAA4B;QAE5B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAEvD,4BAA4B;QAC5B,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAElE,2BAA2B;QAC3B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEnD,qCAAqC;QACrC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;QACpE,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG;YACpB,GAAG,QAAQ;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,UAAU,EAAE,CAAC;YACb,oBAAoB,EAAE,CAAC;SACxB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,QAAkB;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,QAAQ,GACZ,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC;QAC/D,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;QAEnE,eAAe;QACf,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG;YAC9B,QAAQ;YACR,cAAc,EAAE,QAAQ,CAAC,cAAc;SACxC,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACjC,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,oBAAoB,EAAE,CAAC;QAC7C,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;QAC9D,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
1
+ {"version":3,"file":"ComparisonRepository.js","sourceRoot":"","sources":["../../../../src/core/compare/classes/ComparisonRepository.ts"],"names":[],"mappings":"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"}
@@ -9,13 +9,20 @@ export async function comparePage(before, after) {
9
9
  const beautify = jsbeautify.html;
10
10
  const beautifyOptions = {
11
11
  indent_size: 2,
12
- preserve_newlines: true,
13
- unformatted: ["a", "span", "b", "strong", "i", "em"],
12
+ preserve_newlines: false,
13
+ unformatted: ["span", "b", "strong", "i", "em"],
14
14
  };
15
15
  const beforeContent = beautify(before.content, beautifyOptions);
16
16
  const afterContent = beautify(after.content, beautifyOptions);
17
17
  const patch = createPatch(url, beforeContent, afterContent);
18
- const differences = diffLines(beforeContent, afterContent);
18
+ const differences = diffLines(beforeContent, afterContent).map((change) => {
19
+ const { added, removed, ...unchangedFields } = change;
20
+ return {
21
+ ...unchangedFields,
22
+ ...(added ? { added } : {}),
23
+ ...(removed ? { removed } : {}),
24
+ };
25
+ });
19
26
  const hasDifferences = differences.some((diff) => diff.added || diff.removed);
20
27
  return { url, differences, hasDifferences, patch };
21
28
  }
@@ -31,15 +38,20 @@ export async function compareSnapshots(config, snapshotRepository, comparisonRep
31
38
  const beforeUrls = Object.keys(beforeSnapshot.index.urls);
32
39
  const afterUrls = new Set(Object.keys(afterSnapshot.index.urls));
33
40
  const urlsToCompare = config.urls || beforeUrls;
41
+ const requestedUrls = config.urls === undefined ? undefined : new Set(config.urls);
34
42
  const rulesUsedIdentifier = typeof config.ruleset === "string" ? config.ruleset : config.ruleset?.name;
35
43
  // Find new and removed URLs
36
- const newUrls = [...afterUrls].filter((url) => !beforeUrls.includes(url));
37
- const removedUrls = beforeUrls.filter((url) => !afterUrls.has(url));
44
+ const newUrls = [...afterUrls].filter((url) => !beforeUrls.includes(url) &&
45
+ (requestedUrls === undefined || requestedUrls.has(url)));
46
+ const removedUrls = beforeUrls.filter((url) => !afterUrls.has(url) &&
47
+ (requestedUrls === undefined || requestedUrls.has(url)));
38
48
  let pagesWithErrors = 0;
39
49
  // Compare pages one by one
40
50
  for (const url of urlsToCompare) {
41
51
  if (!afterUrls.has(url))
42
52
  continue; // Skip URLs not in the "after" snapshot
53
+ if (!beforeSnapshot.index.urls[url])
54
+ continue; // New URLs are reported above
43
55
  const beforePage = await beforeSnapshot.getPage(url);
44
56
  const afterPage = await afterSnapshot.getPage(url);
45
57
  if (!beforePage || !afterPage) {
@@ -61,7 +73,11 @@ export async function compareSnapshots(config, snapshotRepository, comparisonRep
61
73
  // Return the summary data
62
74
  return {
63
75
  status: "completed",
64
- overallResult: finalIndex.metadata.pagesWithDifferences > 0 ? "fail" : "pass",
76
+ overallResult: finalIndex.metadata.pagesWithDifferences > 0 ||
77
+ newUrls.length > 0 ||
78
+ removedUrls.length > 0
79
+ ? "fail"
80
+ : "pass",
65
81
  beforeSnapshotId: config.beforeSnapshotId,
66
82
  afterSnapshotId: config.afterSnapshotId,
67
83
  rulesUsedIdentifier,