@cleaver/breakcheck 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 (50) hide show
  1. package/README.md +180 -13
  2. package/agent-guide.md +107 -0
  3. package/dist/cli/commands/baseline.d.ts +19 -0
  4. package/dist/cli/commands/baseline.js +133 -0
  5. package/dist/cli/commands/baseline.js.map +1 -0
  6. package/dist/cli/commands/check.d.ts +27 -0
  7. package/dist/cli/commands/check.js +204 -0
  8. package/dist/cli/commands/check.js.map +1 -0
  9. package/dist/cli/commands/clean.d.ts +25 -0
  10. package/dist/cli/commands/clean.js +123 -0
  11. package/dist/cli/commands/clean.js.map +1 -0
  12. package/dist/cli/commands/compare.js +28 -6
  13. package/dist/cli/commands/compare.js.map +1 -1
  14. package/dist/cli/commands/compile.d.ts +5 -0
  15. package/dist/cli/commands/compile.js +23 -0
  16. package/dist/cli/commands/compile.js.map +1 -0
  17. package/dist/cli/commands/help.js +173 -7
  18. package/dist/cli/commands/help.js.map +1 -1
  19. package/dist/cli/commands/init.d.ts +26 -0
  20. package/dist/cli/commands/init.js +224 -0
  21. package/dist/cli/commands/init.js.map +1 -0
  22. package/dist/cli/commands/list-snapshots.js +7 -3
  23. package/dist/cli/commands/list-snapshots.js.map +1 -1
  24. package/dist/cli/commands/new.d.ts +9 -0
  25. package/dist/cli/commands/new.js +27 -0
  26. package/dist/cli/commands/new.js.map +1 -0
  27. package/dist/cli/commands/snapshot.d.ts +20 -0
  28. package/dist/cli/commands/snapshot.js +128 -26
  29. package/dist/cli/commands/snapshot.js.map +1 -1
  30. package/dist/cli/commands/view.d.ts +1 -1
  31. package/dist/cli/commands/view.js +12 -6
  32. package/dist/cli/commands/view.js.map +1 -1
  33. package/dist/cli/config.d.ts +14 -0
  34. package/dist/cli/config.js +58 -0
  35. package/dist/cli/config.js.map +1 -0
  36. package/dist/cli/rules.d.ts +12 -0
  37. package/dist/cli/rules.js +110 -0
  38. package/dist/cli/rules.js.map +1 -0
  39. package/dist/cli/site-environment.d.ts +12 -0
  40. package/dist/cli/site-environment.js +231 -0
  41. package/dist/cli/site-environment.js.map +1 -0
  42. package/dist/cli/url-manifest.d.ts +18 -0
  43. package/dist/cli/url-manifest.js +63 -0
  44. package/dist/cli/url-manifest.js.map +1 -0
  45. package/dist/cli/utils.d.ts +1 -0
  46. package/dist/cli/utils.js +5 -2
  47. package/dist/cli/utils.js.map +1 -1
  48. package/dist/index.js +14 -0
  49. package/dist/index.js.map +1 -1
  50. package/package.json +8 -7
@@ -1 +1 @@
1
- {"version":3,"file":"list-snapshots.js","sourceRoot":"","sources":["../../../src/cli/commands/list-snapshots.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"list-snapshots.js","sourceRoot":"","sources":["../../../src/cli/commands/list-snapshots.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { InteractiveCommand } from "interactive-commander";
2
+ import { type RuleScaffoldOptions, type RuleScaffoldResult } from "../rules.js";
3
+ export interface NewRuleOptions extends RuleScaffoldOptions {
4
+ jsonLogs?: boolean;
5
+ noJsonLogs?: boolean;
6
+ }
7
+ export declare function runNewRule(name: string, options?: NewRuleOptions, cwd?: string): Promise<RuleScaffoldResult>;
8
+ export declare const ruleCommand: InteractiveCommand;
9
+ export declare const newCommand: InteractiveCommand;
@@ -0,0 +1,27 @@
1
+ import { InteractiveCommand } from "interactive-commander";
2
+ import { createRuleScaffold, } from "../rules.js";
3
+ import { configureLogger } from "../utils.js";
4
+ export async function runNewRule(name, options = {}, cwd = process.cwd()) {
5
+ return createRuleScaffold(name, options, cwd);
6
+ }
7
+ export const ruleCommand = new InteractiveCommand("rule")
8
+ .description("Create a new ruleset scaffold")
9
+ .argument("<name>", "Name of the new ruleset directory")
10
+ .option("-d, --directory <parent>", "Parent directory for the new ruleset (relative to the current directory)")
11
+ .option("--json-logs", "Output logs in JSON format")
12
+ .option("--no-json-logs", "Output logs in pretty format (default)")
13
+ .action(async (name, options) => {
14
+ const logger = configureLogger(options);
15
+ try {
16
+ const result = await runNewRule(name, options);
17
+ logger.info(`✅ Created rules file: ${result.filePath}`);
18
+ logger.info(`Next: breakcheck compare --rules ${result.directory}`);
19
+ }
20
+ catch (error) {
21
+ logger.error({ err: error }, "❌ Error creating rules file");
22
+ process.exit(1);
23
+ }
24
+ });
25
+ export const newCommand = new InteractiveCommand("new").description("Create project files");
26
+ newCommand.addCommand(ruleCommand);
27
+ //# sourceMappingURL=new.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new.js","sourceRoot":"","sources":["../../../src/cli/commands/new.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,kBAAkB,GAGnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAO9C,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAY,EACZ,OAAO,GAAmB,EAAE,EAC5B,GAAG,GAAW,OAAO,CAAC,GAAG,EAAE;IAE3B,OAAO,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,kBAAkB,CAAC,MAAM,CAAC;KACtD,WAAW,CAAC,+BAA+B,CAAC;KAC5C,QAAQ,CAAC,QAAQ,EAAE,mCAAmC,CAAC;KACvD,MAAM,CACL,0BAA0B,EAC1B,0EAA0E,CAC3E;KACA,MAAM,CAAC,aAAa,EAAE,4BAA4B,CAAC;KACnD,MAAM,CAAC,gBAAgB,EAAE,wCAAwC,CAAC;KAClE,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAAuB,EAAE,EAAE;IACtD,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,yBAAyB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,oCAAoC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,6BAA6B,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC,WAAW,CACjE,sBAAsB,CACvB,CAAC;AAEF,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC"}
@@ -1,2 +1,22 @@
1
+ import type { SnapshotConfig } from "@cleaver/breakcheck-core";
1
2
  import { InteractiveCommand } from "interactive-commander";
3
+ export interface SnapshotCommandOptions {
4
+ url?: string;
5
+ name?: string;
6
+ depth?: string;
7
+ concurrency?: string;
8
+ include?: string[] | false;
9
+ exclude?: string[] | false;
10
+ noInclude?: boolean;
11
+ noExclude?: boolean;
12
+ type?: string;
13
+ writeUrls?: string;
14
+ urlFile?: string;
15
+ jsonLogs?: boolean;
16
+ noJsonLogs?: boolean;
17
+ }
18
+ export declare function buildSnapshotInvocation(options: SnapshotCommandOptions, args?: readonly string[], cwd?: string): Promise<{
19
+ config: SnapshotConfig;
20
+ storageDir: string;
21
+ }>;
2
22
  export declare const snapshotCommand: InteractiveCommand;
@@ -1,43 +1,134 @@
1
1
  import { createSnapshotFromConfig } from "@cleaver/breakcheck-core";
2
2
  import { InteractiveCommand } from "interactive-commander";
3
+ import path from "node:path";
4
+ import { addConfigOptions, hasCliOption, resolveCliProjectConfig, } from "../config.js";
5
+ import { parseUrlManifest, readUrlManifest } from "../url-manifest.js";
3
6
  import { configureLogger } from "../utils.js";
4
- export const snapshotCommand = new InteractiveCommand("snapshot")
7
+ const DEFAULT_CRAWLER_TYPE = "cheerio";
8
+ const DEFAULT_MAX_DEPTH = 3;
9
+ const DEFAULT_MAX_CONCURRENCY = 5;
10
+ function parseInteger(value, fallback, field, minimum) {
11
+ if (value === undefined)
12
+ return fallback;
13
+ const parsed = Number(value);
14
+ if (!Number.isInteger(parsed) || parsed < minimum) {
15
+ throw new Error(`${field} must be an integer greater than or equal to ${minimum}.`);
16
+ }
17
+ return parsed;
18
+ }
19
+ function requireHttpUrl(value) {
20
+ if (value === undefined) {
21
+ throw new Error("A base URL is required. Provide --url <url> or set baseUrl in breakcheck.config.json.");
22
+ }
23
+ // Preserve the existing API-level failure response for an explicitly empty
24
+ // CLI value while still making an omitted URL actionable at the CLI layer.
25
+ if (value.trim().length === 0)
26
+ return value;
27
+ let parsed;
28
+ try {
29
+ parsed = new URL(value);
30
+ }
31
+ catch (error) {
32
+ throw new Error("Base URL must be a valid HTTP(S) URL.", { cause: error });
33
+ }
34
+ if ((parsed.protocol !== "http:" && parsed.protocol !== "https:") ||
35
+ !parsed.hostname) {
36
+ throw new Error("Base URL must be a valid HTTP(S) URL.");
37
+ }
38
+ return value;
39
+ }
40
+ function resolveCrawlerType(value, configured) {
41
+ const crawlerType = value ?? configured ?? DEFAULT_CRAWLER_TYPE;
42
+ if (crawlerType !== "cheerio" && crawlerType !== "playwright") {
43
+ throw new Error('Crawler type must be either "cheerio" or "playwright".');
44
+ }
45
+ return crawlerType;
46
+ }
47
+ export async function buildSnapshotInvocation(options, args = process.argv.slice(2), cwd = process.cwd()) {
48
+ const projectConfig = await resolveCliProjectConfig(args, cwd);
49
+ const crawl = projectConfig.crawl;
50
+ const baseUrl = requireHttpUrl(options.url ?? projectConfig.baseUrl);
51
+ const positiveInclude = Array.isArray(options.include);
52
+ const positiveExclude = Array.isArray(options.exclude);
53
+ const noInclude = options.noInclude === true || options.include === false;
54
+ const noExclude = options.noExclude === true || options.exclude === false;
55
+ if (noInclude && (positiveInclude || hasCliOption(args, "--include"))) {
56
+ throw new Error("--include and --no-include cannot be used together.");
57
+ }
58
+ if (noExclude && (positiveExclude || hasCliOption(args, "--exclude"))) {
59
+ throw new Error("--exclude and --no-exclude cannot be used together.");
60
+ }
61
+ const includePatterns = noInclude
62
+ ? []
63
+ : Array.isArray(options.include)
64
+ ? options.include
65
+ : crawl.includePatterns;
66
+ const excludePatterns = noExclude
67
+ ? []
68
+ : Array.isArray(options.exclude)
69
+ ? options.exclude
70
+ : crawl.excludePatterns;
71
+ if (options.urlFile !== undefined &&
72
+ ((includePatterns?.length ?? 0) > 0 || (excludePatterns?.length ?? 0) > 0)) {
73
+ throw new Error("--include and --exclude cannot be used with --url-file because the manifest is exact; configured URL filters are active. Use --no-include and --no-exclude to clear them.");
74
+ }
75
+ const urlPaths = options.urlFile === undefined
76
+ ? undefined
77
+ : parseUrlManifest(await readUrlManifest(options.urlFile === "-"
78
+ ? options.urlFile
79
+ : path.resolve(cwd, options.urlFile)), baseUrl, options.urlFile === "-"
80
+ ? "stdin"
81
+ : path.resolve(cwd, options.urlFile));
82
+ if (urlPaths?.issues.length) {
83
+ throw new Error(formatManifestIssues(urlPaths.issues));
84
+ }
85
+ const maxDepth = parseInteger(options.depth, crawl.maxDepth ?? DEFAULT_MAX_DEPTH, "Depth", 0);
86
+ const maxConcurrency = parseInteger(options.concurrency, crawl.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY, "Concurrency", 1);
87
+ const config = {
88
+ baseUrl,
89
+ name: options.name ??
90
+ `snapshot_${new Date()
91
+ .toISOString()
92
+ .replace(/[:.]/g, "-")
93
+ .replace(/T/, "_")
94
+ .replace(/Z$/, "Z")}`,
95
+ crawlSettings: {
96
+ baseUrl,
97
+ crawlerType: resolveCrawlerType(options.type, crawl.crawlerType),
98
+ maxDepth,
99
+ maxConcurrency,
100
+ includePatterns,
101
+ excludePatterns,
102
+ },
103
+ urlPaths: urlPaths?.paths,
104
+ urlListPath: options.writeUrls === undefined
105
+ ? undefined
106
+ : path.resolve(cwd, options.writeUrls),
107
+ };
108
+ return { config, storageDir: projectConfig.storageDir };
109
+ }
110
+ export const snapshotCommand = addConfigOptions(new InteractiveCommand("snapshot")
5
111
  .description("Create a snapshot of a website")
6
- .requiredOption("-u, --url <url>", "Base URL to crawl")
112
+ .option("-u, --url <url>", "Base URL to crawl")
7
113
  .option("-n, --name <name>", "Name for the snapshot")
8
- .option("-d, --depth <number>", "Maximum crawl depth", "3")
9
- .option("-c, --concurrency <number>", "Number of concurrent requests", "5")
114
+ .option("-d, --depth <number>", "Maximum crawl depth")
115
+ .option("-c, --concurrency <number>", "Number of concurrent requests")
10
116
  .option("-i, --include <patterns...>", "URL patterns to include")
11
117
  .option("-e, --exclude <patterns...>", "URL patterns to exclude")
12
- .option("-t, --type <type>", "Crawler type (cheerio or playwright)", "cheerio")
118
+ .option("--no-include", "Clear configured include patterns")
119
+ .option("--no-exclude", "Clear configured exclude patterns")
120
+ .option("-t, --type <type>", "Crawler type (cheerio or playwright)")
13
121
  .option("-w, --write-urls <path>", "Generate a URL list file at the specified path")
122
+ .option("--url-file <path>", "Read an exact root-relative URL manifest from a file, or '-' for stdin")
14
123
  .option("--json-logs", "Output logs in JSON format")
15
124
  .option("--no-json-logs", "Output logs in pretty format (default)")
16
125
  .action(async (options) => {
17
126
  // Configure logger based on options
18
127
  const logger = configureLogger(options);
19
128
  try {
20
- // Map CLI options to SnapshotConfig
21
- const config = {
22
- baseUrl: options.url,
23
- name: options.name ||
24
- `snapshot_${new Date()
25
- .toISOString()
26
- .replace(/[:.]/g, "-")
27
- .replace(/T/, "_")
28
- .replace(/Z$/, "Z")}`,
29
- crawlSettings: {
30
- baseUrl: options.url,
31
- crawlerType: options.type,
32
- maxDepth: parseInt(options.depth, 10),
33
- maxConcurrency: parseInt(options.concurrency, 10),
34
- includePatterns: options.include,
35
- excludePatterns: options.exclude,
36
- },
37
- urlListPath: options.writeUrls,
38
- };
129
+ const invocation = await buildSnapshotInvocation(options);
39
130
  // Call API to create snapshot
40
- const result = await createSnapshotFromConfig(config);
131
+ const result = await createSnapshotFromConfig(invocation.config, { storageDir: invocation.storageDir });
41
132
  // Display results
42
133
  if (result.status === "success") {
43
134
  logger.info(`✅ Snapshot created successfully: ${result.snapshotId}`);
@@ -58,11 +149,22 @@ export const snapshotCommand = new InteractiveCommand("snapshot")
58
149
  result.errors.forEach((error) => {
59
150
  logger.error(` - ${error.message}`);
60
151
  });
152
+ process.exitCode = 1;
61
153
  }
62
154
  }
63
155
  catch (error) {
64
156
  logger.error({ err: error }, "❌ Error creating snapshot");
65
157
  process.exit(1);
66
158
  }
67
- });
159
+ }));
160
+ function formatManifestIssues(issues) {
161
+ return issues
162
+ .map((issue) => {
163
+ if (issue.type === "entry") {
164
+ return `${issue.source}:${issue.line}: ${issue.message} (${issue.value})`;
165
+ }
166
+ return `${issue.source}: ${issue.message}`;
167
+ })
168
+ .join("\n");
169
+ }
68
170
  //# sourceMappingURL=snapshot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/cli/commands/snapshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/cli/commands/snapshot.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { InteractiveCommand } from "interactive-commander";
2
- export declare function startViewServerWithPortSelection(comparisonName: string, requestedPort: number | undefined): Promise<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>;
2
+ export declare function startViewServerWithPortSelection(comparisonName: string, requestedPort: number | undefined, storageDir?: string): Promise<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>;
3
3
  export declare const viewCommand: InteractiveCommand;
@@ -1,5 +1,6 @@
1
1
  import { startViewServer } from "@cleaver/breakcheck-core";
2
2
  import { InteractiveCommand } from "interactive-commander";
3
+ import { addConfigOptions, resolveCliProjectConfig } from "../config.js";
3
4
  import { configureLogger } from "../utils.js";
4
5
  const DEFAULT_VIEW_PORT = 8080;
5
6
  const MAX_VIEW_PORT = 65534;
@@ -12,11 +13,13 @@ function isPortInUseError(error) {
12
13
  }
13
14
  return error.code === "EADDRINUSE";
14
15
  }
15
- export async function startViewServerWithPortSelection(comparisonName, requestedPort) {
16
+ export async function startViewServerWithPortSelection(comparisonName, requestedPort, storageDir) {
16
17
  let port = requestedPort ?? DEFAULT_VIEW_PORT;
17
18
  while (port <= MAX_VIEW_PORT) {
18
19
  try {
19
- return await startViewServer(comparisonName, port);
20
+ return storageDir === undefined
21
+ ? await startViewServer(comparisonName, port)
22
+ : await startViewServer(comparisonName, port, { storageDir });
20
23
  }
21
24
  catch (error) {
22
25
  if (requestedPort !== undefined || !isPortInUseError(error)) {
@@ -27,7 +30,7 @@ export async function startViewServerWithPortSelection(comparisonName, requested
27
30
  }
28
31
  throw new Error(`No available port found between ${DEFAULT_VIEW_PORT} and ${MAX_VIEW_PORT}.`);
29
32
  }
30
- export const viewCommand = new InteractiveCommand("view")
33
+ export const viewCommand = addConfigOptions(new InteractiveCommand("view")
31
34
  .description("View the results of a comparison")
32
35
  .argument("[comparison-name]", "Name of the comparison to view", "compare_default")
33
36
  .option("-p, --port <number>", "Port to run the view server on (defaults to 8080 and increments when occupied)")
@@ -37,14 +40,17 @@ export const viewCommand = new InteractiveCommand("view")
37
40
  // Configure logger based on options
38
41
  const logger = configureLogger(options);
39
42
  try {
43
+ const projectConfig = await resolveCliProjectConfig();
40
44
  logger.info(`🔍 Viewing comparison: ${comparisonName}`);
41
45
  const requestedPort = options.port === undefined ? undefined : parseInt(options.port, 10);
42
46
  if (requestedPort !== undefined &&
43
- (isNaN(requestedPort) || requestedPort < 1024 || requestedPort > 65535)) {
47
+ (isNaN(requestedPort) ||
48
+ requestedPort < 1024 ||
49
+ requestedPort > 65535)) {
44
50
  logger.error("Port must be a number between 1024 and 65535");
45
51
  process.exit(1);
46
52
  }
47
- const server = await startViewServerWithPortSelection(comparisonName, requestedPort);
53
+ const server = await startViewServerWithPortSelection(comparisonName, requestedPort, projectConfig.storageDir);
48
54
  logger.info("Press Ctrl+C to stop the server");
49
55
  await new Promise((resolve) => {
50
56
  const shutdown = () => {
@@ -63,5 +69,5 @@ export const viewCommand = new InteractiveCommand("view")
63
69
  logger.error({ err: error }, "❌ Error starting view server");
64
70
  process.exit(1);
65
71
  }
66
- });
72
+ }));
67
73
  //# sourceMappingURL=view.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/cli/commands/view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/cli/commands/view.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type ProjectCrawlConfig, type ResolvedProjectConfig } from "@cleaver/breakcheck-core";
2
+ import type { InteractiveCommand } from "interactive-commander";
3
+ export interface ConfigSelection {
4
+ configPath?: string;
5
+ noConfig?: boolean;
6
+ }
7
+ export declare function hasCliFlag(args: readonly string[], flag: string): boolean;
8
+ export declare function hasCliOption(args: readonly string[], option: string): boolean;
9
+ /** Adds configuration-selection flags to a CLI command. */
10
+ export declare function addConfigOptions<T extends InteractiveCommand>(command: T): T;
11
+ /** Reads shared config flags regardless of whether they precede or follow a command. */
12
+ export declare function parseConfigSelection(args?: readonly string[]): ConfigSelection;
13
+ export declare function resolveCliProjectConfig(args?: readonly string[], cwd?: string): Promise<ResolvedProjectConfig>;
14
+ export declare function mergeCrawlConfig(config: ResolvedProjectConfig, overrides: Partial<ProjectCrawlConfig>): ProjectCrawlConfig;
@@ -0,0 +1,58 @@
1
+ import { resolveProjectConfig, } from "@cleaver/breakcheck-core";
2
+ export function hasCliFlag(args, flag) {
3
+ return args.some((argument) => argument === flag);
4
+ }
5
+ export function hasCliOption(args, option) {
6
+ return args.some((argument) => argument === option || argument.startsWith(`${option}=`));
7
+ }
8
+ /** Adds configuration-selection flags to a CLI command. */
9
+ export function addConfigOptions(command) {
10
+ command
11
+ .option("--config <file>", "Use this configuration file instead of discovering one")
12
+ .option("--no-config", "Disable project configuration discovery");
13
+ return command;
14
+ }
15
+ /** Reads shared config flags regardless of whether they precede or follow a command. */
16
+ export function parseConfigSelection(args = process.argv.slice(2)) {
17
+ let configPath;
18
+ let noConfig = false;
19
+ for (let index = 0; index < args.length; index += 1) {
20
+ const argument = args[index];
21
+ if (argument === "--no-config") {
22
+ noConfig = true;
23
+ continue;
24
+ }
25
+ if (argument === "--config") {
26
+ const value = args[index + 1];
27
+ if (value === undefined || value.startsWith("-")) {
28
+ throw new Error("--config requires a file path.");
29
+ }
30
+ configPath = value;
31
+ index += 1;
32
+ continue;
33
+ }
34
+ if (argument.startsWith("--config=")) {
35
+ const value = argument.slice("--config=".length);
36
+ if (value.length === 0)
37
+ throw new Error("--config requires a file path.");
38
+ configPath = value;
39
+ }
40
+ }
41
+ if (configPath !== undefined && noConfig) {
42
+ throw new Error("--config and --no-config cannot be used together.");
43
+ }
44
+ return {
45
+ ...(configPath === undefined ? {} : { configPath }),
46
+ ...(noConfig ? { noConfig: true } : {}),
47
+ };
48
+ }
49
+ export async function resolveCliProjectConfig(args = process.argv.slice(2), cwd = process.cwd()) {
50
+ return resolveProjectConfig({ ...parseConfigSelection(args), cwd });
51
+ }
52
+ export function mergeCrawlConfig(config, overrides) {
53
+ return {
54
+ ...config.crawl,
55
+ ...overrides,
56
+ };
57
+ }
58
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const RULES_FILE_NAME = "rules.breakcheck";
2
+ export interface RuleScaffoldOptions {
3
+ directory?: string;
4
+ }
5
+ export interface RuleScaffoldResult {
6
+ name: string;
7
+ directory: string;
8
+ filePath: string;
9
+ }
10
+ export declare function validateRuleName(name: string): string;
11
+ export declare function renderRuleScaffold(name: string): string;
12
+ export declare function createRuleScaffold(name: string, options?: RuleScaffoldOptions, cwd?: string): Promise<RuleScaffoldResult>;
@@ -0,0 +1,110 @@
1
+ import { lstat, mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export const RULES_FILE_NAME = "rules.breakcheck";
4
+ const RULE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
5
+ function isNodeError(error) {
6
+ return (error instanceof Error && "code" in error && typeof error.code === "string");
7
+ }
8
+ function isWithinDirectory(root, candidate) {
9
+ const relative = path.relative(root, candidate);
10
+ return (relative === "" ||
11
+ (!relative.startsWith(`..${path.sep}`) &&
12
+ relative !== ".." &&
13
+ !path.isAbsolute(relative)));
14
+ }
15
+ async function assertDirectory(directory) {
16
+ const stats = await lstat(directory);
17
+ if (stats.isSymbolicLink()) {
18
+ throw new Error(`Cannot use symlinked rules directory: ${directory}`);
19
+ }
20
+ if (!stats.isDirectory()) {
21
+ throw new Error(`Rules destination is not a directory: ${directory}`);
22
+ }
23
+ }
24
+ async function ensureDirectoryTree(rootDirectory, destination) {
25
+ await assertDirectory(rootDirectory);
26
+ let current = rootDirectory;
27
+ const relative = path.relative(rootDirectory, destination);
28
+ for (const segment of relative.split(path.sep).filter(Boolean)) {
29
+ current = path.join(current, segment);
30
+ try {
31
+ await assertDirectory(current);
32
+ }
33
+ catch (error) {
34
+ if (!isNodeError(error) || error.code !== "ENOENT")
35
+ throw error;
36
+ try {
37
+ await mkdir(current);
38
+ }
39
+ catch (mkdirError) {
40
+ if (!isNodeError(mkdirError) || mkdirError.code !== "EEXIST")
41
+ throw mkdirError;
42
+ await assertDirectory(current);
43
+ }
44
+ }
45
+ }
46
+ }
47
+ function resolveParentDirectory(cwd, directory) {
48
+ const absoluteCwd = path.resolve(cwd);
49
+ if (directory !== undefined) {
50
+ if (directory.trim().length === 0) {
51
+ throw new Error("--directory must not be empty.");
52
+ }
53
+ if (path.isAbsolute(directory)) {
54
+ throw new Error("--directory must be relative to the current directory.");
55
+ }
56
+ }
57
+ const parent = path.resolve(absoluteCwd, directory ?? ".");
58
+ if (!isWithinDirectory(absoluteCwd, parent)) {
59
+ throw new Error("--directory must resolve inside the current working directory.");
60
+ }
61
+ return { cwd: absoluteCwd, parent };
62
+ }
63
+ export function validateRuleName(name) {
64
+ if (typeof name !== "string" || !RULE_NAME_PATTERN.test(name)) {
65
+ throw new Error("Rule name must be a single path-safe segment containing only letters, numbers, '.', '_' or '-'.");
66
+ }
67
+ return name;
68
+ }
69
+ export function renderRuleScaffold(name) {
70
+ return `-- Breakcheck Rules File
71
+ -- Ruleset: ${name}
72
+ --
73
+ -- Add active rules below. Examples:
74
+ -- css:.ad-container do: exclude
75
+ -- css:[data-dynamic] do: remove_attr attr:"data-id"
76
+ -- css:.timestamp do: rewrite_content regex:"\\d+" replace:"STATIC"
77
+ `;
78
+ }
79
+ export async function createRuleScaffold(name, options = {}, cwd = process.cwd()) {
80
+ const validName = validateRuleName(name);
81
+ const resolved = resolveParentDirectory(cwd, options.directory);
82
+ const parentDirectory = resolved.parent;
83
+ const directory = path.join(parentDirectory, validName);
84
+ const filePath = path.join(directory, RULES_FILE_NAME);
85
+ await ensureDirectoryTree(resolved.cwd, directory);
86
+ try {
87
+ await lstat(filePath);
88
+ throw new Error(`Rules file already exists: ${filePath}`);
89
+ }
90
+ catch (error) {
91
+ if (!isNodeError(error) || error.code !== "ENOENT")
92
+ throw error;
93
+ }
94
+ try {
95
+ await writeFile(filePath, renderRuleScaffold(validName), {
96
+ encoding: "utf8",
97
+ flag: "wx",
98
+ });
99
+ }
100
+ catch (error) {
101
+ if (isNodeError(error) && error.code === "EEXIST") {
102
+ throw new Error(`Rules file already exists: ${filePath}`, {
103
+ cause: error,
104
+ });
105
+ }
106
+ throw error;
107
+ }
108
+ return { name: validName, directory, filePath };
109
+ }
110
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/cli/rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ResolvedProjectCheckConfig } from "@cleaver/breakcheck-core";
2
+ interface SignalEventSource {
3
+ on(signal: "SIGINT" | "SIGTERM", listener: () => void): unknown;
4
+ removeListener(signal: "SIGINT" | "SIGTERM", listener: () => void): unknown;
5
+ }
6
+ /** Runs configured preparation and server commands around a site operation. */
7
+ export declare function withCheckEnvironment<T>(check: ResolvedProjectCheckConfig, baseUrl: string, useExistingServer: boolean, operation: (signal: AbortSignal) => Promise<T>, options?: {
8
+ signal?: AbortSignal;
9
+ signals?: SignalEventSource;
10
+ stopGracePeriodMs?: number;
11
+ }): Promise<T>;
12
+ export {};