@se-studio/site-check 1.0.77 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/screaming-frog/audit.d.ts +9 -0
  3. package/dist/screaming-frog/audit.d.ts.map +1 -0
  4. package/dist/screaming-frog/audit.js +254 -0
  5. package/dist/screaming-frog/audit.js.map +1 -0
  6. package/dist/screaming-frog/compare-report.d.ts +22 -0
  7. package/dist/screaming-frog/compare-report.d.ts.map +1 -0
  8. package/dist/screaming-frog/compare-report.js +142 -0
  9. package/dist/screaming-frog/compare-report.js.map +1 -0
  10. package/dist/screaming-frog/compare.d.ts +5 -0
  11. package/dist/screaming-frog/compare.d.ts.map +1 -0
  12. package/dist/screaming-frog/compare.js +42 -0
  13. package/dist/screaming-frog/compare.js.map +1 -0
  14. package/dist/screaming-frog/crawl-state.d.ts +19 -0
  15. package/dist/screaming-frog/crawl-state.d.ts.map +1 -0
  16. package/dist/screaming-frog/crawl-state.js +30 -0
  17. package/dist/screaming-frog/crawl-state.js.map +1 -0
  18. package/dist/screaming-frog/crawl.d.ts +6 -0
  19. package/dist/screaming-frog/crawl.d.ts.map +1 -0
  20. package/dist/screaming-frog/crawl.js +76 -0
  21. package/dist/screaming-frog/crawl.js.map +1 -0
  22. package/dist/screaming-frog/csv.d.ts +6 -0
  23. package/dist/screaming-frog/csv.d.ts.map +1 -0
  24. package/dist/screaming-frog/csv.js +71 -0
  25. package/dist/screaming-frog/csv.js.map +1 -0
  26. package/dist/screaming-frog/env.d.ts +4 -0
  27. package/dist/screaming-frog/env.d.ts.map +1 -0
  28. package/dist/screaming-frog/env.js +53 -0
  29. package/dist/screaming-frog/env.js.map +1 -0
  30. package/dist/screaming-frog/index.d.ts +15 -0
  31. package/dist/screaming-frog/index.d.ts.map +1 -0
  32. package/dist/screaming-frog/index.js +15 -0
  33. package/dist/screaming-frog/index.js.map +1 -0
  34. package/dist/screaming-frog/list-crawls.d.ts +22 -0
  35. package/dist/screaming-frog/list-crawls.d.ts.map +1 -0
  36. package/dist/screaming-frog/list-crawls.js +107 -0
  37. package/dist/screaming-frog/list-crawls.js.map +1 -0
  38. package/dist/screaming-frog/load-config.d.ts +9 -0
  39. package/dist/screaming-frog/load-config.d.ts.map +1 -0
  40. package/dist/screaming-frog/load-config.js +86 -0
  41. package/dist/screaming-frog/load-config.js.map +1 -0
  42. package/dist/screaming-frog/load-profile.d.ts +9 -0
  43. package/dist/screaming-frog/load-profile.d.ts.map +1 -0
  44. package/dist/screaming-frog/load-profile.js +32 -0
  45. package/dist/screaming-frog/load-profile.js.map +1 -0
  46. package/dist/screaming-frog/paths.d.ts +5 -0
  47. package/dist/screaming-frog/paths.d.ts.map +1 -0
  48. package/dist/screaming-frog/paths.js +34 -0
  49. package/dist/screaming-frog/paths.js.map +1 -0
  50. package/dist/screaming-frog/rules.d.ts +14 -0
  51. package/dist/screaming-frog/rules.d.ts.map +1 -0
  52. package/dist/screaming-frog/rules.js +211 -0
  53. package/dist/screaming-frog/rules.js.map +1 -0
  54. package/dist/screaming-frog/sf-runner.d.ts +12 -0
  55. package/dist/screaming-frog/sf-runner.d.ts.map +1 -0
  56. package/dist/screaming-frog/sf-runner.js +37 -0
  57. package/dist/screaming-frog/sf-runner.js.map +1 -0
  58. package/dist/screaming-frog/types.d.ts +136 -0
  59. package/dist/screaming-frog/types.d.ts.map +1 -0
  60. package/dist/screaming-frog/types.js +2 -0
  61. package/dist/screaming-frog/types.js.map +1 -0
  62. package/dist/screaming-frog-cli.d.ts +4 -0
  63. package/dist/screaming-frog-cli.d.ts.map +1 -0
  64. package/dist/screaming-frog-cli.js +326 -0
  65. package/dist/screaming-frog-cli.js.map +1 -0
  66. package/package.json +1 -1
@@ -0,0 +1,76 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { resolveEnvFile } from './env.js';
3
+ import { findLatestCrawlForUrl, listStoredCrawls } from './list-crawls.js';
4
+ import { resolveCrawlTarget } from './load-profile.js';
5
+ import { defaultExportRoot, timestampExportDir } from './paths.js';
6
+ import { runScreamingFrogOrThrow } from './sf-runner.js';
7
+ /** CSV tabs exported after every crawl for audit analysis. */
8
+ export const DEFAULT_EXPORT_TABS = [
9
+ 'Internal:All',
10
+ 'Response Codes:All',
11
+ 'Canonicals:All',
12
+ 'Directives:All',
13
+ 'Structured Data:All',
14
+ 'H1:All',
15
+ 'Page Titles:All',
16
+ 'Meta Description:All',
17
+ 'All Inlinks',
18
+ ];
19
+ export const DEFAULT_EXPORT_REPORTS = ['Crawl Overview', 'Issues Overview'];
20
+ async function exportCrawlArtifacts(exportDir, crawlUrl, configFile) {
21
+ await mkdir(exportDir, { recursive: true });
22
+ const base = ['--headless', '--load-crawl', crawlUrl, '--config', configFile];
23
+ await runScreamingFrogOrThrow([
24
+ ...base,
25
+ '--export-tabs',
26
+ DEFAULT_EXPORT_TABS.join(','),
27
+ '--output-folder',
28
+ exportDir,
29
+ '--overwrite',
30
+ '--export-format',
31
+ 'csv',
32
+ ]);
33
+ await runScreamingFrogOrThrow([
34
+ ...base,
35
+ '--save-report',
36
+ DEFAULT_EXPORT_REPORTS.join(','),
37
+ '--output-folder',
38
+ exportDir,
39
+ '--overwrite',
40
+ '--export-format',
41
+ 'csv',
42
+ ]);
43
+ }
44
+ export async function runScreamingFrogCrawl(options) {
45
+ const envFile = await resolveEnvFile(options.workspaceRoot, options.envFile);
46
+ const target = await resolveCrawlTarget({
47
+ site: options.site,
48
+ profileName: options.profile,
49
+ envFile,
50
+ });
51
+ const exportRoot = options.exportRoot ?? defaultExportRoot();
52
+ const exportDir = options.exportDir ?? timestampExportDir(exportRoot);
53
+ if (!options.skipCrawl) {
54
+ await runScreamingFrogOrThrow([
55
+ '--headless',
56
+ '--crawl',
57
+ target.url,
58
+ '--config',
59
+ target.configFile,
60
+ '--save-crawl',
61
+ '--overwrite',
62
+ ], { inherit: true });
63
+ }
64
+ await exportCrawlArtifacts(exportDir, target.url, target.configFile);
65
+ const storedCrawls = await listStoredCrawls();
66
+ const latest = findLatestCrawlForUrl(storedCrawls, target.url);
67
+ return {
68
+ exportDir,
69
+ crawlUrl: target.url,
70
+ configFile: target.configFile,
71
+ profileName: options.profile,
72
+ siteLabel: options.site.siteLabel,
73
+ databaseId: latest?.databaseId,
74
+ };
75
+ }
76
+ //# sourceMappingURL=crawl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawl.js","sourceRoot":"","sources":["../../src/screaming-frog/crawl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Minimal RFC-style CSV row parser (handles quoted fields). */
2
+ export declare function parseCsv(text: string): string[][];
3
+ /** Screaming Frog exports wrap fields in double quotes. */
4
+ export declare function normalizeCsvCell(cell: string): string;
5
+ export declare function parseCsvRecords(text: string): Record<string, string>[];
6
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/screaming-frog/csv.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAgDjD;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAWtE"}
@@ -0,0 +1,71 @@
1
+ /** Minimal RFC-style CSV row parser (handles quoted fields). */
2
+ export function parseCsv(text) {
3
+ const rows = [];
4
+ let row = [];
5
+ let field = '';
6
+ let inQuotes = false;
7
+ for (let i = 0; i < text.length; i++) {
8
+ const ch = text[i];
9
+ const next = text[i + 1];
10
+ if (inQuotes) {
11
+ if (ch === '"' && next === '"') {
12
+ field += '"';
13
+ i++;
14
+ }
15
+ else if (ch === '"') {
16
+ inQuotes = false;
17
+ }
18
+ else {
19
+ field += ch;
20
+ }
21
+ continue;
22
+ }
23
+ if (ch === '"') {
24
+ inQuotes = true;
25
+ continue;
26
+ }
27
+ if (ch === ',') {
28
+ row.push(field);
29
+ field = '';
30
+ continue;
31
+ }
32
+ if (ch === '\n' || (ch === '\r' && next === '\n')) {
33
+ if (ch === '\r')
34
+ i++;
35
+ row.push(field);
36
+ if (row.some((cell) => cell.length > 0))
37
+ rows.push(row);
38
+ row = [];
39
+ field = '';
40
+ continue;
41
+ }
42
+ field += ch;
43
+ }
44
+ if (field.length > 0 || row.length > 0) {
45
+ row.push(field);
46
+ if (row.some((cell) => cell.length > 0))
47
+ rows.push(row);
48
+ }
49
+ return rows;
50
+ }
51
+ /** Screaming Frog exports wrap fields in double quotes. */
52
+ export function normalizeCsvCell(cell) {
53
+ return cell
54
+ .replace(/^\uFEFF/, '')
55
+ .replace(/^"|"$/g, '')
56
+ .trim();
57
+ }
58
+ export function parseCsvRecords(text) {
59
+ const table = parseCsv(text).map((row) => row.map(normalizeCsvCell));
60
+ if (table.length < 2)
61
+ return [];
62
+ const headers = table[0];
63
+ return table.slice(1).map((cells) => {
64
+ const record = {};
65
+ headers.forEach((header, index) => {
66
+ record[header] = cells[index] ?? '';
67
+ });
68
+ return record;
69
+ });
70
+ }
71
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.js","sourceRoot":"","sources":["../../src/screaming-frog/csv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function loadDotEnvFile(filePath: string): Promise<Record<string, string>>;
2
+ /** Walk up from startDir looking for .env.local */
3
+ export declare function resolveEnvFile(startDir: string, explicitPath?: string): Promise<string | undefined>;
4
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/screaming-frog/env.ts"],"names":[],"mappings":"AAGA,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAyBtF;AAWD,mDAAmD;AACnD,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAY7B"}
@@ -0,0 +1,53 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export async function loadDotEnvFile(filePath) {
4
+ let raw = '';
5
+ try {
6
+ raw = await readFile(filePath, 'utf8');
7
+ }
8
+ catch {
9
+ return {};
10
+ }
11
+ const values = {};
12
+ for (const line of raw.split('\n')) {
13
+ const trimmed = line.trim();
14
+ if (!trimmed || trimmed.startsWith('#'))
15
+ continue;
16
+ const eq = trimmed.indexOf('=');
17
+ if (eq <= 0)
18
+ continue;
19
+ const key = trimmed.slice(0, eq).trim();
20
+ let value = trimmed.slice(eq + 1).trim();
21
+ if ((value.startsWith('"') && value.endsWith('"')) ||
22
+ (value.startsWith("'") && value.endsWith("'"))) {
23
+ value = value.slice(1, -1);
24
+ }
25
+ values[key] = value;
26
+ }
27
+ return values;
28
+ }
29
+ async function fileReadable(filePath) {
30
+ try {
31
+ await readFile(filePath, 'utf8');
32
+ return true;
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ }
38
+ /** Walk up from startDir looking for .env.local */
39
+ export async function resolveEnvFile(startDir, explicitPath) {
40
+ if (explicitPath)
41
+ return path.resolve(explicitPath);
42
+ let current = path.resolve(startDir);
43
+ const { root } = path.parse(current);
44
+ while (true) {
45
+ const candidate = path.join(current, '.env.local');
46
+ if (await fileReadable(candidate))
47
+ return candidate;
48
+ if (current === root)
49
+ return undefined;
50
+ current = path.dirname(current);
51
+ }
52
+ }
53
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/screaming-frog/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,EAAE,IAAI,CAAC;YAAE,SAAS;QACtB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,QAAgB;IAC1C,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,YAAqB;IAErB,IAAI,YAAY;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAEpD,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAErC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACnD,IAAI,MAAM,YAAY,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QACpD,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACvC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ export * from './audit.js';
2
+ export * from './compare.js';
3
+ export * from './compare-report.js';
4
+ export * from './crawl.js';
5
+ export * from './crawl-state.js';
6
+ export * from './csv.js';
7
+ export * from './env.js';
8
+ export * from './list-crawls.js';
9
+ export * from './load-config.js';
10
+ export * from './load-profile.js';
11
+ export * from './paths.js';
12
+ export * from './rules.js';
13
+ export * from './sf-runner.js';
14
+ export * from './types.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/screaming-frog/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,15 @@
1
+ export * from './audit.js';
2
+ export * from './compare.js';
3
+ export * from './compare-report.js';
4
+ export * from './crawl.js';
5
+ export * from './crawl-state.js';
6
+ export * from './csv.js';
7
+ export * from './env.js';
8
+ export * from './list-crawls.js';
9
+ export * from './load-config.js';
10
+ export * from './load-profile.js';
11
+ export * from './paths.js';
12
+ export * from './rules.js';
13
+ export * from './sf-runner.js';
14
+ export * from './types.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/screaming-frog/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,22 @@
1
+ export interface StoredScreamingFrogCrawl {
2
+ databaseId: string;
3
+ name: string;
4
+ url: string;
5
+ mode: string;
6
+ urlCount: number;
7
+ percentComplete: number;
8
+ modified: string;
9
+ version: string;
10
+ }
11
+ export declare function parseListCrawlsOutput(output: string): StoredScreamingFrogCrawl[];
12
+ export declare function listStoredCrawls(): Promise<StoredScreamingFrogCrawl[]>;
13
+ /** Match tokens for siteHost — includes Vercel preview slugs (hellobrightline-com). */
14
+ export declare function siteHostMatchTokens(siteHost: string): string[];
15
+ export declare function filterCrawlsForSite(crawls: StoredScreamingFrogCrawl[], siteHost: string): StoredScreamingFrogCrawl[];
16
+ export declare function sortCrawlsByModifiedDesc(crawls: StoredScreamingFrogCrawl[]): StoredScreamingFrogCrawl[];
17
+ export declare function findLatestCrawlForUrl(crawls: StoredScreamingFrogCrawl[], crawlUrl: string): StoredScreamingFrogCrawl | undefined;
18
+ export declare function pickLatestPair(crawls: StoredScreamingFrogCrawl[]): {
19
+ current: StoredScreamingFrogCrawl;
20
+ previous: StoredScreamingFrogCrawl;
21
+ } | null;
22
+ //# sourceMappingURL=list-crawls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-crawls.d.ts","sourceRoot":"","sources":["../../src/screaming-frog/list-crawls.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAoCD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,EAAE,CA4BhF;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAG5E;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAU9D;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,wBAAwB,EAAE,EAClC,QAAQ,EAAE,MAAM,GACf,wBAAwB,EAAE,CAM5B;AAOD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,EAAE,GACjC,wBAAwB,EAAE,CAE5B;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,wBAAwB,EAAE,EAClC,QAAQ,EAAE,MAAM,GACf,wBAAwB,GAAG,SAAS,CAOtC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,wBAAwB,EAAE,GAAG;IAClE,OAAO,EAAE,wBAAwB,CAAC;IAClC,QAAQ,EAAE,wBAAwB,CAAC;CACpC,GAAG,IAAI,CAIP"}
@@ -0,0 +1,107 @@
1
+ import { runScreamingFrog } from './sf-runner.js';
2
+ const UUID_RE = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
3
+ function parseTableRow(line) {
4
+ if (!line.includes('║') || line.includes('Database Id'))
5
+ return null;
6
+ const idMatch = line.match(UUID_RE);
7
+ if (!idMatch)
8
+ return null;
9
+ const parts = line
10
+ .split('│')
11
+ .map((part) => part.replace(/[║]/g, '').trim())
12
+ .filter((part) => part.length > 0);
13
+ if (parts.length < 7)
14
+ return null;
15
+ const databaseId = idMatch[1];
16
+ const name = parts[1] ?? '';
17
+ const url = parts[2] ?? '';
18
+ const mode = parts[3] ?? '';
19
+ const urlCount = Number.parseInt(parts[4] ?? '0', 10) || 0;
20
+ const percentComplete = Number.parseInt(parts[5] ?? '0', 10) || 0;
21
+ const modified = parts[6] ?? '';
22
+ const version = parts[7] ?? '';
23
+ return {
24
+ databaseId,
25
+ name,
26
+ url,
27
+ mode,
28
+ urlCount,
29
+ percentComplete,
30
+ modified,
31
+ version,
32
+ };
33
+ }
34
+ export function parseListCrawlsOutput(output) {
35
+ const crawls = [];
36
+ let current = null;
37
+ for (const rawLine of output.split('\n')) {
38
+ const line = rawLine.trim();
39
+ if (!line.includes('║'))
40
+ continue;
41
+ const row = parseTableRow(line);
42
+ if (row?.databaseId) {
43
+ if (current)
44
+ crawls.push(current);
45
+ current = row;
46
+ continue;
47
+ }
48
+ if (!current)
49
+ continue;
50
+ const parts = line
51
+ .split('│')
52
+ .map((part) => part.replace(/[║]/g, '').trim())
53
+ .filter(Boolean);
54
+ if (parts.length >= 2) {
55
+ current.name += parts[0] ?? '';
56
+ current.url += parts[1] ?? '';
57
+ }
58
+ }
59
+ if (current)
60
+ crawls.push(current);
61
+ return crawls;
62
+ }
63
+ export async function listStoredCrawls() {
64
+ const result = await runScreamingFrog(['--headless', '--list-crawls']);
65
+ return parseListCrawlsOutput(`${result.stdout}\n${result.stderr}`);
66
+ }
67
+ /** Match tokens for siteHost — includes Vercel preview slugs (hellobrightline-com). */
68
+ export function siteHostMatchTokens(siteHost) {
69
+ const host = siteHost.toLowerCase().trim();
70
+ const tokens = new Set([host]);
71
+ const label = host.split('.')[0];
72
+ if (label && label !== host) {
73
+ tokens.add(label);
74
+ tokens.add(`${label}-com`);
75
+ }
76
+ tokens.add(host.replace(/\./g, '-'));
77
+ return [...tokens];
78
+ }
79
+ export function filterCrawlsForSite(crawls, siteHost) {
80
+ const tokens = siteHostMatchTokens(siteHost);
81
+ return crawls.filter((crawl) => {
82
+ const url = crawl.url.toLowerCase();
83
+ return tokens.some((token) => url.includes(token));
84
+ });
85
+ }
86
+ function modifiedTimestamp(modified) {
87
+ const parsed = Date.parse(modified.replace(',', ''));
88
+ return Number.isNaN(parsed) ? 0 : parsed;
89
+ }
90
+ export function sortCrawlsByModifiedDesc(crawls) {
91
+ return [...crawls].sort((a, b) => modifiedTimestamp(b.modified) - modifiedTimestamp(a.modified));
92
+ }
93
+ export function findLatestCrawlForUrl(crawls, crawlUrl) {
94
+ const normalized = crawlUrl.toLowerCase().replace(/\/$/, '');
95
+ const matches = crawls.filter((crawl) => {
96
+ const url = crawl.url.toLowerCase().replace(/\/$/, '');
97
+ return url.startsWith(normalized) || normalized.startsWith(url);
98
+ });
99
+ return sortCrawlsByModifiedDesc(matches)[0];
100
+ }
101
+ export function pickLatestPair(crawls) {
102
+ const sorted = sortCrawlsByModifiedDesc(crawls);
103
+ if (sorted.length < 2)
104
+ return null;
105
+ return { current: sorted[0], previous: sorted[1] };
106
+ }
107
+ //# sourceMappingURL=list-crawls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-crawls.js","sourceRoot":"","sources":["../../src/screaming-frog/list-crawls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ResolvedSiteConfig, ScreamingFrogProfileManifest, ScreamingFrogSiteConfig } from './types.js';
2
+ export declare const DEFAULT_MANIFEST_RELATIVE = "seo/screaming-frog.json";
3
+ export declare function parseSiteConfig(manifest: ScreamingFrogProfileManifest): ScreamingFrogSiteConfig;
4
+ export declare function findManifestPath(startDir: string): Promise<string | null>;
5
+ export declare function loadSiteConfig(options: {
6
+ cwd?: string;
7
+ configPath?: string;
8
+ }): Promise<ResolvedSiteConfig>;
9
+ //# sourceMappingURL=load-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../../src/screaming-frog/load-config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAElB,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,yBAAyB,4BAA4B,CAAC;AAcnE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,4BAA4B,GAAG,uBAAuB,CAkB/F;AAWD,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAU/E;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAqB9B"}
@@ -0,0 +1,86 @@
1
+ import { access, readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export const DEFAULT_MANIFEST_RELATIVE = 'seo/screaming-frog.json';
4
+ function manifestProfileToCrawlProfile(key, entry) {
5
+ return {
6
+ label: entry.label ?? key,
7
+ startUrl: entry.url ?? '',
8
+ configFile: entry.configFile,
9
+ urlEnv: entry.urlEnv,
10
+ };
11
+ }
12
+ export function parseSiteConfig(manifest) {
13
+ const profiles = {};
14
+ for (const [key, entry] of Object.entries(manifest.profiles)) {
15
+ profiles[key] = manifestProfileToCrawlProfile(key, entry);
16
+ }
17
+ return {
18
+ site: manifest.site,
19
+ siteLabel: manifest.siteLabel,
20
+ defaultSiteUrl: manifest.defaultSiteUrl,
21
+ siteHost: manifest.siteHost,
22
+ outputMdName: manifest.outputMdName,
23
+ compareMdName: manifest.compareMdName,
24
+ productionAuditDoc: manifest.productionAuditDoc,
25
+ defaultProfile: manifest.defaultProfile,
26
+ profiles,
27
+ issueRuleOverrides: manifest.issueRuleOverrides,
28
+ };
29
+ }
30
+ async function fileExists(filePath) {
31
+ try {
32
+ await access(filePath);
33
+ return true;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ export async function findManifestPath(startDir) {
40
+ let current = path.resolve(startDir);
41
+ const { root } = path.parse(current);
42
+ while (true) {
43
+ const candidate = path.join(current, DEFAULT_MANIFEST_RELATIVE);
44
+ if (await fileExists(candidate))
45
+ return candidate;
46
+ if (current === root)
47
+ return null;
48
+ current = path.dirname(current);
49
+ }
50
+ }
51
+ export async function loadSiteConfig(options) {
52
+ const cwd = options.cwd ?? process.cwd();
53
+ const manifestPath = options.configPath
54
+ ? path.resolve(options.configPath)
55
+ : await findManifestPath(cwd);
56
+ if (!manifestPath) {
57
+ throw new Error(`No Screaming Frog site config found. Add ${DEFAULT_MANIFEST_RELATIVE} to the project or pass --config <path>.`);
58
+ }
59
+ const raw = await readFile(manifestPath, 'utf8');
60
+ const manifest = JSON.parse(raw);
61
+ validateManifest(manifest);
62
+ return {
63
+ config: parseSiteConfig(manifest),
64
+ workspaceRoot: path.dirname(path.dirname(manifestPath)),
65
+ manifestPath,
66
+ };
67
+ }
68
+ function validateManifest(manifest) {
69
+ const required = [
70
+ 'site',
71
+ 'siteLabel',
72
+ 'siteHost',
73
+ 'defaultSiteUrl',
74
+ 'outputMdName',
75
+ 'profiles',
76
+ ];
77
+ for (const key of required) {
78
+ if (!manifest[key]) {
79
+ throw new Error(`seo/screaming-frog.json is missing required field "${key}".`);
80
+ }
81
+ }
82
+ if (Object.keys(manifest.profiles).length === 0) {
83
+ throw new Error('seo/screaming-frog.json must define at least one profile.');
84
+ }
85
+ }
86
+ //# sourceMappingURL=load-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../src/screaming-frog/load-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ScreamingFrogCrawlProfile, ScreamingFrogCrawlTarget, ScreamingFrogSiteConfig } from './types.js';
2
+ export declare function getProfilesForSite(site: ScreamingFrogSiteConfig): Record<string, ScreamingFrogCrawlProfile>;
3
+ export declare function resolveCrawlTarget(options: {
4
+ site: ScreamingFrogSiteConfig;
5
+ profileName: string;
6
+ envFile?: string;
7
+ }): Promise<ScreamingFrogCrawlTarget>;
8
+ export declare function defaultProfileName(site: ScreamingFrogSiteConfig): string;
9
+ //# sourceMappingURL=load-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-profile.d.ts","sourceRoot":"","sources":["../../src/screaming-frog/load-profile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,uBAAuB,GAC5B,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAE3C;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,IAAI,EAAE,uBAAuB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CA6BpC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,GAAG,MAAM,CAExE"}
@@ -0,0 +1,32 @@
1
+ import { loadDotEnvFile } from './env.js';
2
+ import { expandHome } from './paths.js';
3
+ export function getProfilesForSite(site) {
4
+ return site.profiles;
5
+ }
6
+ export async function resolveCrawlTarget(options) {
7
+ const profiles = getProfilesForSite(options.site);
8
+ const profile = profiles[options.profileName];
9
+ if (!profile) {
10
+ throw new Error(`Unknown profile "${options.profileName}" for ${options.site.site}. Available: ${Object.keys(profiles).join(', ')}`);
11
+ }
12
+ let startUrl = profile.startUrl;
13
+ if (options.envFile) {
14
+ const env = await loadDotEnvFile(options.envFile);
15
+ if (profile.urlEnv && env[profile.urlEnv]) {
16
+ startUrl = env[profile.urlEnv];
17
+ }
18
+ }
19
+ if (!startUrl) {
20
+ throw new Error(`Profile "${options.profileName}" has no start URL. Set "url" in the manifest or provide ${profile.urlEnv ?? 'a url'}.`);
21
+ }
22
+ return {
23
+ name: options.profileName,
24
+ url: startUrl.endsWith('/') ? startUrl : `${startUrl}/`,
25
+ configFile: expandHome(profile.configFile),
26
+ label: `${options.site.siteLabel} — ${profile.label}`,
27
+ };
28
+ }
29
+ export function defaultProfileName(site) {
30
+ return site.defaultProfile ?? Object.keys(site.profiles)[0] ?? 'production';
31
+ }
32
+ //# sourceMappingURL=load-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-profile.js","sourceRoot":"","sources":["../../src/screaming-frog/load-profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAOxC,MAAM,UAAU,kBAAkB,CAChC,IAA6B;IAE7B,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAIxC;IACC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,oBAAoB,OAAO,CAAC,WAAW,SAAS,OAAO,CAAC,IAAI,CAAC,IAAI,gBAAgB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpH,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAChC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1C,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAE,CAAC;QAClC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,YAAY,OAAO,CAAC,WAAW,4DAA4D,OAAO,CAAC,MAAM,IAAI,OAAO,GAAG,CACxH,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,WAAW;QACzB,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG;QACvD,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC;QAC1C,KAAK,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,MAAM,OAAO,CAAC,KAAK,EAAE;KACtD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAA6B;IAC9D,OAAO,IAAI,CAAC,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function expandHome(filePath: string): string;
2
+ export declare function defaultExportRoot(): string;
3
+ export declare function resolveScreamingFrogCli(): string;
4
+ export declare function timestampExportDir(exportRoot: string): string;
5
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/screaming-frog/paths.ts"],"names":[],"mappings":"AASA,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAIhD;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAY7D"}
@@ -0,0 +1,34 @@
1
+ import { homedir } from 'node:os';
2
+ import path from 'node:path';
3
+ const MAC_SF_CLI = '/Applications/Screaming Frog SEO Spider.app/Contents/MacOS/ScreamingFrogSEOSpiderLauncher';
4
+ const WIN_SF_CLI = 'C:\\Program Files (x86)\\Screaming Frog SEO Spider\\ScreamingFrogSEOSpiderLauncher.exe';
5
+ export function expandHome(filePath) {
6
+ if (filePath.startsWith('~/')) {
7
+ return path.join(homedir(), filePath.slice(2));
8
+ }
9
+ return filePath;
10
+ }
11
+ export function defaultExportRoot() {
12
+ return expandHome('~/spider/exports');
13
+ }
14
+ export function resolveScreamingFrogCli() {
15
+ if (process.platform === 'darwin')
16
+ return MAC_SF_CLI;
17
+ if (process.platform === 'win32')
18
+ return WIN_SF_CLI;
19
+ return MAC_SF_CLI;
20
+ }
21
+ export function timestampExportDir(exportRoot) {
22
+ const now = new Date();
23
+ const pad = (n) => String(n).padStart(2, '0');
24
+ const label = [
25
+ now.getFullYear(),
26
+ pad(now.getMonth() + 1),
27
+ pad(now.getDate()),
28
+ pad(now.getHours()),
29
+ pad(now.getMinutes()),
30
+ pad(now.getSeconds()),
31
+ ].join('.');
32
+ return path.join(exportRoot, label);
33
+ }
34
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/screaming-frog/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,GACd,2FAA2F,CAAC;AAE9F,MAAM,UAAU,GACd,wFAAwF,CAAC;AAE3F,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC;IACrD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,UAAU,CAAC;IACpD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG;QACZ,GAAG,CAAC,WAAW,EAAE;QACjB,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACvB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAClB,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACnB,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;QACrB,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;KACtB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AACtC,CAAC"}