@riverbankcms/qa 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/dist/cli.js +2178 -0
- package/dist/cli.js.map +1 -0
- package/package.json +58 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/run.ts","../src/cli/program.ts","../src/cli/qa-command.ts","../src/url/scope.ts","../src/extract/links.ts","../src/http/fetch.ts","../src/url/normalize.ts","../src/crawl/sitemap.ts","../src/crawl/seeds.ts","../src/extract/seo.ts","../src/report/constants.ts","../src/crawl/url-policy.ts","../src/http/host-delay.ts","../src/crawl/robots.ts","../src/crawl/crawler.ts","../src/config/defaults.ts","../src/config/load-config.ts","../src/config/merge.ts","../src/config/parse-headers.ts","../src/report/write-json.ts","../src/checks/generate-issues.ts","../src/report/write-html.ts","../src/report/build-html.ts","../src/report/create.ts","../src/render/select-pages.ts","../src/render/screenshot-path.ts","../src/render/run.ts","../src/checks/generate-render-issues.ts","../src/output/run-dir.ts","../src/report/write-run-metadata.ts","../src/version.ts","../src/config/validate.ts","../src/checks/check-external-links.ts","../src/cli.ts"],"sourcesContent":["import { CommanderError } from \"commander\";\nimport type { CliIo } from \"./program.js\";\nimport { createProgram } from \"./program.js\";\n\nexport async function runCli(argv: string[], io: CliIo): Promise<number> {\n const program = createProgram(io);\n program.exitOverride();\n\n try {\n await program.parseAsync(argv);\n return 0;\n } catch (error) {\n if (error instanceof CommanderError) {\n if (error.code === \"commander.helpDisplayed\") return 0;\n return typeof error.exitCode === \"number\" ? error.exitCode : 1;\n }\n throw error;\n }\n}\n\n","import { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { runQaCommand } from \"./qa-command.js\";\n\nexport type CliIo = {\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n};\n\nfunction readPackageVersion(): string {\n try {\n const raw = readFileSync(new URL(\"../../package.json\", import.meta.url), \"utf8\");\n const pkg = JSON.parse(raw) as { version?: unknown };\n return typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nexport function createProgram(io: CliIo): Command {\n const program = new Command();\n\n program\n .name(\"qa\")\n .description(\"Website QA CLI\")\n .version(readPackageVersion())\n .showHelpAfterError()\n .configureOutput({\n writeOut: (str) => {\n io.stdout.write(str);\n },\n writeErr: (str) => {\n io.stderr.write(str);\n },\n });\n\n program\n .argument(\"[url]\", \"Base URL (e.g. https://example.com)\")\n .option(\"--config <path>\", \"Path to YAML/JSON config file\")\n .option(\"--output <dir>\", \"Output root directory\", \"qa-output\")\n .option(\"--mode <mode>\", \"crawl|render|full\", \"crawl\")\n .option(\"--ci\", \"CI mode (non-zero exit when issues found)\")\n .option(\"--fail-on <severity>\", \"none|info|warning|error\", \"none\")\n .option(\"--max-pages <n>\", \"Hard cap on total pages\", (v) => Number(v))\n .option(\"--max-depth <n>\", \"Hard cap on link depth\", (v) => Number(v))\n .option(\"--max-render-pages <n>\", \"Hard cap on rendered pages (Phase 4)\", (v) => Number(v))\n .option(\"--concurrency <n>\", \"Concurrent requests\", (v) => Number(v))\n .option(\"--timeout <ms>\", \"Request timeout in milliseconds\", (v) => Number(v))\n .option(\"--respect-robots <boolean>\", \"Respect robots.txt (v1)\")\n .option(\"--sitemap <boolean>\", \"Seed from /sitemap.xml\")\n .option(\"--external-links <boolean>\", \"Check external links (v1)\")\n .option(\"--max-external-links <n>\", \"Hard cap on external links checked (v1)\", (v) => Number(v))\n .option(\"--ignore-https-errors <boolean>\", \"Ignore HTTPS certificate errors\")\n .option(\"--user-agent <string>\", \"Custom user-agent\")\n .option(\"--headers <json>\", \"Headers JSON object (e.g. '{\\\"K\\\":\\\"V\\\"}')\")\n .option(\n \"--header <pair>\",\n \"Header pair (repeatable) (e.g. 'Authorization: Bearer ...')\",\n (value: string, previous: string[] | undefined) => {\n const next = previous ?? [];\n next.push(value);\n return next;\n },\n )\n .option(\n \"--include <pattern>\",\n \"Include path regex (repeatable)\",\n (value: string, previous: string[] | undefined) => {\n const next = previous ?? [];\n next.push(value);\n return next;\n },\n )\n .option(\n \"--exclude <pattern>\",\n \"Exclude path regex (repeatable)\",\n (value: string, previous: string[] | undefined) => {\n const next = previous ?? [];\n next.push(value);\n return next;\n },\n )\n .option(\"--report-format <format>\", \"html|json|both\", \"both\")\n .action(async (url: string | undefined, options) => {\n await runQaCommand(program, io, url, options);\n });\n\n program\n .command(\"hello\")\n .description(\"Smoke-test command\")\n .argument(\"[name]\", \"Name to greet\", \"world\")\n .action((name: string) => {\n io.stdout.write(`Hello, ${name}!\\n`);\n });\n\n return program;\n}\n","import { CommanderError, type Command } from \"commander\";\nimport { crawlSite } from \"../crawl/crawler.js\";\nimport { defaultConfigForBaseUrl } from \"../config/defaults.js\";\nimport { loadConfigFile } from \"../config/load-config.js\";\nimport { mergeConfig } from \"../config/merge.js\";\nimport { parseHeaderPairs } from \"../config/parse-headers.js\";\nimport type { CliIo } from \"./program.js\";\nimport type { CrawlConfig, Mode, ReportFormat } from \"../config/schema.js\";\nimport { writeJsonReport } from \"../report/write-json.js\";\nimport { generateIssues } from \"../checks/generate-issues.js\";\nimport { writeHtmlReport } from \"../report/write-html.js\";\nimport { createEmptyReport } from \"../report/create.js\";\nimport { selectRenderUrlsForFull, selectRenderUrlsForRenderOnly } from \"../render/select-pages.js\";\nimport { renderPages } from \"../render/run.js\";\nimport { generateRenderIssues } from \"../checks/generate-render-issues.js\";\nimport { createRunOutputDir } from \"../output/run-dir.js\";\nimport { writeRunMetadata } from \"../report/write-run-metadata.js\";\nimport { validateRegexList } from \"../config/validate.js\";\nimport { checkExternalLinks } from \"../checks/check-external-links.js\";\n\ntype RawOptions = {\n config?: string;\n output?: string;\n mode?: Mode;\n reportFormat?: ReportFormat;\n ci?: boolean;\n failOn?: string;\n maxPages?: number;\n maxDepth?: number;\n maxRenderPages?: number;\n maxRedirects?: number;\n concurrency?: number;\n timeout?: number;\n respectRobots?: boolean;\n sitemap?: boolean;\n externalLinks?: boolean;\n maxExternalLinks?: number;\n ignoreHttpsErrors?: boolean;\n userAgent?: string;\n headers?: string;\n header?: string[];\n include?: string[];\n exclude?: string[];\n};\n\nfunction parseBoolean(value: unknown): boolean | undefined {\n if (typeof value === \"boolean\") return value;\n if (typeof value !== \"string\") return undefined;\n const lower = value.toLowerCase().trim();\n if (lower === \"true\" || lower === \"1\" || lower === \"yes\") return true;\n if (lower === \"false\" || lower === \"0\" || lower === \"no\") return false;\n return undefined;\n}\n\nfunction parseJsonObject(value: string | undefined): Record<string, string> {\n if (!value) return {};\n const parsed = JSON.parse(value) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\"--headers must be a JSON object\");\n }\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(parsed)) {\n if (typeof v !== \"string\") continue;\n out[k] = v;\n }\n return out;\n}\n\nfunction failWithHelp(program: Command, io: CliIo, exitCode: number, message: string): never {\n io.stderr.write(`${message}\\n`);\n program.outputHelp();\n throw new CommanderError(exitCode, \"qa.usage\", message);\n}\n\nfunction toNumber(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n }\n return undefined;\n}\n\nfunction resolveMode(value: unknown): Mode | undefined {\n if (value === \"crawl\" || value === \"render\" || value === \"full\") return value;\n return undefined;\n}\n\nfunction resolveReportFormat(value: unknown): ReportFormat | undefined {\n if (value === \"html\" || value === \"json\" || value === \"both\") return value;\n return undefined;\n}\n\ntype FailOn = \"none\" | \"info\" | \"warning\" | \"error\";\n\nfunction resolveFailOn(value: unknown): FailOn | undefined {\n if (value === \"none\" || value === \"info\" || value === \"warning\" || value === \"error\") return value;\n return undefined;\n}\n\nfunction computeFailExitCode(failOn: FailOn, issuesCount: { error: number; warning: number; info: number }): number {\n if (failOn === \"none\") return 0;\n if (failOn === \"error\") return issuesCount.error > 0 ? 2 : 0;\n if (failOn === \"warning\") return issuesCount.error + issuesCount.warning > 0 ? 2 : 0;\n return issuesCount.error + issuesCount.warning + issuesCount.info > 0 ? 2 : 0;\n}\n\nfunction countIssuesBySeverity(issues: Array<{ severity: string }>): { error: number; warning: number; info: number } {\n let error = 0;\n let warning = 0;\n let info = 0;\n for (const i of issues) {\n if (i.severity === \"error\") error += 1;\n else if (i.severity === \"warning\") warning += 1;\n else if (i.severity === \"info\") info += 1;\n }\n return { error, warning, info };\n}\n\nfunction validateUrlOrFail(program: Command, io: CliIo, url: string): void {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n failWithHelp(program, io, 1, `Invalid URL: ${url}`);\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n failWithHelp(program, io, 1, `URL must be http(s): ${url}`);\n }\n}\n\nexport async function runQaCommand(\n program: Command,\n io: CliIo,\n url: string | undefined,\n options: RawOptions,\n): Promise<void> {\n if (!url) {\n failWithHelp(program, io, 1, \"Missing required argument: url\");\n }\n validateUrlOrFail(program, io, url);\n\n const defaults = defaultConfigForBaseUrl(url);\n\n const startedAtIso = new Date().toISOString();\n\n let fileConfig = {};\n if (options.config) {\n try {\n fileConfig = loadConfigFile(options.config);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n failWithHelp(program, io, 1, `Failed to load config: ${msg}`);\n }\n }\n\n const cliHeadersFromJson = (() => {\n try {\n return parseJsonObject(options.headers);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n failWithHelp(program, io, 1, msg);\n }\n })();\n const cliHeadersFromPairs = parseHeaderPairs(options.header);\n\n const merged = mergeConfig(defaults, fileConfig);\n\n const outputRootDir = options.output ?? merged.outputDir;\n\n const parsedMode = resolveMode(options.mode);\n if (options.mode !== undefined && !parsedMode) {\n failWithHelp(program, io, 1, `Invalid --mode: ${String(options.mode)} (expected crawl|render|full)`);\n }\n const parsedReportFormat = resolveReportFormat(options.reportFormat);\n if (options.reportFormat !== undefined && !parsedReportFormat) {\n failWithHelp(\n program,\n io,\n 1,\n `Invalid --report-format: ${String(options.reportFormat)} (expected html|json|both)`,\n );\n }\n\n const parsedFailOn = resolveFailOn(options.failOn);\n if (options.failOn !== undefined && !parsedFailOn) {\n failWithHelp(program, io, 1, `Invalid --fail-on: ${String(options.failOn)} (expected none|info|warning|error)`);\n }\n\n const cfg: CrawlConfig = mergeConfig(merged, {\n outputDir: createRunOutputDir(outputRootDir, url, startedAtIso),\n mode: parsedMode ?? merged.mode,\n reportFormat: parsedReportFormat ?? merged.reportFormat,\n\n maxPages: toNumber(options.maxPages) ?? merged.maxPages,\n maxDepth: toNumber(options.maxDepth) ?? merged.maxDepth,\n render: {\n maxRenderPages: toNumber(options.maxRenderPages) ?? merged.render.maxRenderPages,\n },\n maxRedirects: toNumber(options.maxRedirects) ?? merged.maxRedirects,\n concurrency: toNumber(options.concurrency) ?? merged.concurrency,\n timeoutMs: toNumber(options.timeout) ?? merged.timeoutMs,\n\n respectRobots: parseBoolean(options.respectRobots) ?? merged.respectRobots,\n sitemap: parseBoolean(options.sitemap) ?? merged.sitemap,\n externalLinks: parseBoolean(options.externalLinks) ?? merged.externalLinks,\n external: {\n maxExternalLinksChecked: toNumber(options.maxExternalLinks) ?? merged.external.maxExternalLinksChecked,\n },\n ignoreHttpsErrors: parseBoolean(options.ignoreHttpsErrors) ?? merged.ignoreHttpsErrors,\n userAgent: options.userAgent ?? merged.userAgent,\n\n headers: {\n ...merged.headers,\n ...cliHeadersFromJson,\n ...cliHeadersFromPairs,\n },\n include: options.include ?? merged.include,\n exclude: options.exclude ?? merged.exclude,\n });\n\n if (cfg.maxPages <= 0) failWithHelp(program, io, 1, \"--max-pages must be > 0\");\n if (cfg.maxDepth < 0) failWithHelp(program, io, 1, \"--max-depth must be >= 0\");\n if (cfg.render.maxRenderPages <= 0) failWithHelp(program, io, 1, \"--max-render-pages must be > 0\");\n if (cfg.maxRedirects < 0) failWithHelp(program, io, 1, \"--max-redirects must be >= 0\");\n if (cfg.concurrency <= 0) failWithHelp(program, io, 1, \"--concurrency must be > 0\");\n if (cfg.timeoutMs <= 0) failWithHelp(program, io, 1, \"--timeout must be > 0\");\n if (cfg.crawl.perPathQueryVariantLimit <= 0) failWithHelp(program, io, 1, \"crawl.perPathQueryVariantLimit must be > 0\");\n if (cfg.crawl.paginationLimit <= 0) failWithHelp(program, io, 1, \"crawl.paginationLimit must be > 0\");\n if (cfg.crawl.requestDelayMs < 0) failWithHelp(program, io, 1, \"crawl.requestDelayMs must be >= 0\");\n if (cfg.external.maxExternalLinksChecked < 0) failWithHelp(program, io, 1, \"external.maxExternalLinksChecked must be >= 0\");\n\n try {\n validateRegexList(cfg.include, \"--include\");\n validateRegexList(cfg.exclude, \"--exclude\");\n validateRegexList(cfg.url.ignoredQueryParamPatterns, \"url.ignoredQueryParamPatterns\");\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n failWithHelp(program, io, 1, msg);\n }\n\n cfg.url.allowlistedQueryParams = cfg.url.allowlistedQueryParams.map((s) => s.toLowerCase());\n cfg.url.trackingParamNames = cfg.url.trackingParamNames.map((s) => s.toLowerCase());\n cfg.url.trackingParamPrefixes = cfg.url.trackingParamPrefixes.map((s) => s.toLowerCase());\n cfg.crawl.paginationParamNames = cfg.crawl.paginationParamNames.map((s) => s.toLowerCase());\n\n const preWarnings: string[] = [];\n if (cfg.externalLinks) {\n if (cfg.external.maxExternalLinksChecked <= 0) {\n preWarnings.push(\n \"External link checking is enabled but maxExternalLinksChecked=0; no external links will be checked.\",\n );\n }\n }\n\n let report = createEmptyReport(cfg, startedAtIso);\n\n if (cfg.mode === \"crawl\") {\n const crawled = await crawlSite(cfg);\n report = crawled.report;\n report.run.warnings.unshift(...preWarnings);\n report.issues = generateIssues(cfg, report);\n report.issues = [...(report.issues ?? []), ...(await checkExternalLinks(cfg, report))];\n } else if (cfg.mode === \"full\") {\n const crawled = await crawlSite(cfg);\n report = crawled.report;\n report.run.warnings.unshift(...preWarnings);\n report.issues = generateIssues(cfg, report);\n report.issues = [...(report.issues ?? []), ...(await checkExternalLinks(cfg, report))];\n\n const renderUrls = await selectRenderUrlsForFull(cfg, report);\n await renderPages(cfg, report, renderUrls);\n report.issues = [...(report.issues ?? []), ...generateRenderIssues(report)];\n report.run.endedAt = new Date().toISOString();\n } else if (cfg.mode === \"render\") {\n report.run.warnings.unshift(...preWarnings);\n const renderUrls = await selectRenderUrlsForRenderOnly(cfg);\n await renderPages(cfg, report, renderUrls);\n report.issues = [...(report.issues ?? []), ...generateRenderIssues(report)];\n report.run.endedAt = new Date().toISOString();\n }\n\n const outputs: string[] = [];\n if (cfg.reportFormat === \"json\" || cfg.reportFormat === \"both\") {\n outputs.push(writeJsonReport(cfg.outputDir, report));\n }\n if (cfg.reportFormat === \"html\" || cfg.reportFormat === \"both\") {\n outputs.push(writeHtmlReport(cfg.outputDir, report));\n }\n outputs.push(writeRunMetadata(cfg.outputDir, report, outputs));\n\n for (const out of outputs) io.stdout.write(`Wrote ${out}\\n`);\n\n const effectiveFailOn: FailOn = (options.ci ? \"error\" : undefined) ?? parsedFailOn ?? \"none\";\n const counts = countIssuesBySeverity(report.issues ?? []);\n const exitCode = computeFailExitCode(effectiveFailOn, counts);\n if (exitCode !== 0) {\n io.stderr.write(\n `qa: failing due to issues (error=${counts.error}, warning=${counts.warning}, info=${counts.info}); output: ${cfg.outputDir}\\n`,\n );\n throw new CommanderError(exitCode, \"qa.failOn\", \"Issues found\");\n }\n}\n","export type CompiledPathPatterns = {\n include: RegExp[];\n exclude: RegExp[];\n};\n\nexport function compilePathPatterns(include: string[], exclude: string[]): CompiledPathPatterns {\n return {\n include: include.map((p) => new RegExp(p)),\n exclude: exclude.map((p) => new RegExp(p)),\n };\n}\n\nexport function isAllowedOrigin(target: URL, allowedOrigins: Set<string>): boolean {\n return allowedOrigins.has(target.origin);\n}\n\nexport function isInScopePath(pathname: string, patterns: CompiledPathPatterns): boolean {\n for (const rx of patterns.exclude) {\n if (rx.test(pathname)) return false;\n }\n if (patterns.include.length === 0) return true;\n for (const rx of patterns.include) {\n if (rx.test(pathname)) return true;\n }\n return false;\n}\n\nexport function isNonHttpLink(href: string): boolean {\n const trimmed = href.trim();\n if (trimmed === \"\" || trimmed === \"#\") return true;\n const lower = trimmed.toLowerCase();\n return (\n lower.startsWith(\"mailto:\") ||\n lower.startsWith(\"tel:\") ||\n lower.startsWith(\"javascript:\")\n );\n}\n\n","import { isNonHttpLink } from \"../url/scope.js\";\n\nexport type DiscoveredLink = {\n href: string;\n anchorText?: string | undefined;\n};\n\nfunction decodeHtmlEntities(text: string): string {\n return text\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll(\""\", '\"')\n .replaceAll(\"'\", \"'\");\n}\n\nfunction normalizeWhitespace(text: string): string {\n return text.replace(/\\s+/g, \" \").trim();\n}\n\nexport function extractAnchorLinks(html: string): DiscoveredLink[] {\n const links: DiscoveredLink[] = [];\n\n const anchorRe =\n /<a\\b[^>]*?\\bhref\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>]+))[^>]*>([\\s\\S]*?)<\\/a>/gi;\n\n let match: RegExpExecArray | null;\n while ((match = anchorRe.exec(html))) {\n const href = match[1] ?? match[2] ?? match[3] ?? \"\";\n if (!href || isNonHttpLink(href)) continue;\n\n const inner = match[4] ?? \"\";\n const text = normalizeWhitespace(\n decodeHtmlEntities(inner.replace(/<[^>]+>/g, \"\")),\n );\n\n links.push({\n href,\n anchorText: text || undefined,\n });\n }\n\n return links;\n}\n","import { Agent } from \"undici\";\n\nexport type FetchOptions = {\n timeoutMs: number;\n maxRedirects: number;\n headers: Record<string, string>;\n userAgent?: string | undefined;\n ignoreHttpsErrors: boolean;\n readBody?: boolean | undefined;\n};\n\nexport type RedirectHop = {\n url: string;\n status: number;\n location?: string | undefined;\n};\n\nexport type FetchResult = {\n url: string;\n finalUrl: string;\n status?: number | undefined;\n redirectChain: RedirectHop[];\n contentType?: string | undefined;\n bodyText?: string | undefined;\n error?: string | undefined;\n};\n\nfunction createDispatcher(ignoreHttpsErrors: boolean): Agent | undefined {\n if (!ignoreHttpsErrors) return undefined;\n return new Agent({\n connect: {\n rejectUnauthorized: false,\n },\n });\n}\n\nfunction mergeHeaders(\n headers: Record<string, string>,\n userAgent?: string,\n): Headers {\n const h = new Headers();\n for (const [k, v] of Object.entries(headers)) {\n h.set(k, v);\n }\n if (userAgent) h.set(\"user-agent\", userAgent);\n return h;\n}\n\nexport async function fetchWithRedirects(\n url: string,\n opts: FetchOptions,\n): Promise<FetchResult> {\n const dispatcher = createDispatcher(opts.ignoreHttpsErrors);\n const headers = mergeHeaders(opts.headers, opts.userAgent);\n\n const redirectChain: RedirectHop[] = [];\n let current = url;\n\n for (let hop = 0; hop <= opts.maxRedirects; hop++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), opts.timeoutMs);\n\n try {\n const init: RequestInit = {\n method: \"GET\",\n redirect: \"manual\",\n headers,\n signal: controller.signal,\n };\n\n if (dispatcher) {\n (init as unknown as { dispatcher?: unknown }).dispatcher = dispatcher;\n }\n\n const res = await fetch(current, init);\n\n const status = res.status;\n const contentType = res.headers.get(\"content-type\") ?? undefined;\n\n if (status >= 300 && status < 400) {\n const location = res.headers.get(\"location\") ?? undefined;\n redirectChain.push({\n url: current,\n status,\n ...(location ? { location } : {}),\n });\n\n if (!location) {\n const base: FetchResult = {\n url,\n finalUrl: current,\n status,\n redirectChain,\n };\n if (contentType) base.contentType = contentType;\n return base;\n }\n\n if (hop === opts.maxRedirects) {\n const base: FetchResult = {\n url,\n finalUrl: current,\n status,\n redirectChain,\n error: `maxRedirects exceeded (${opts.maxRedirects})`,\n };\n if (contentType) base.contentType = contentType;\n return base;\n }\n\n const next = new URL(location, current).toString();\n current = next;\n continue;\n }\n\n const shouldReadBody =\n opts.readBody === true ||\n contentType?.toLowerCase().includes(\"text/\") === true ||\n contentType?.toLowerCase().includes(\"application/xml\") === true ||\n contentType?.toLowerCase().includes(\"application/xhtml+xml\") === true ||\n contentType?.toLowerCase().includes(\"application/json\") === true;\n\n const bodyText = shouldReadBody ? await res.text() : undefined;\n\n const base: FetchResult = {\n url,\n finalUrl: current,\n status,\n redirectChain,\n };\n if (contentType) base.contentType = contentType;\n if (bodyText !== undefined) base.bodyText = bodyText;\n return base;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n url,\n finalUrl: current,\n redirectChain,\n error: message,\n };\n } finally {\n clearTimeout(timeout);\n }\n }\n\n return {\n url,\n finalUrl: current,\n redirectChain,\n error: `maxRedirects exceeded (${opts.maxRedirects})`,\n };\n}\n","import type { UrlHandlingConfig } from \"../config/schema.js\";\n\nexport type NormalizedUrl = {\n normalizedUrl: string;\n key: string;\n};\n\nfunction isDefaultPort(scheme: string, port: string): boolean {\n if (!port) return false;\n if (scheme === \"http:\" && port === \"80\") return true;\n if (scheme === \"https:\" && port === \"443\") return true;\n return false;\n}\n\nfunction shouldStripParam(\n paramName: string,\n cfg: UrlHandlingConfig,\n): boolean {\n const lower = paramName.toLowerCase();\n\n for (const pattern of cfg.ignoredQueryParamPatterns) {\n try {\n if (new RegExp(pattern, \"i\").test(lower)) return true;\n } catch {\n // ignore invalid patterns; validated elsewhere for include/exclude, but not here\n }\n }\n\n if (cfg.dropQueryParamsExceptAllowlist) {\n return !cfg.allowlistedQueryParams.includes(lower);\n }\n\n if (!cfg.stripTrackingParams) return false;\n\n if (cfg.trackingParamNames.includes(lower)) return true;\n for (const prefix of cfg.trackingParamPrefixes) {\n if (lower.startsWith(prefix)) return true;\n }\n return false;\n}\n\nexport function normalizeUrl(input: string, cfg: UrlHandlingConfig): NormalizedUrl {\n const url = new URL(input);\n\n url.hash = \"\";\n url.protocol = url.protocol.toLowerCase();\n url.hostname = url.hostname.toLowerCase();\n\n if (isDefaultPort(url.protocol, url.port)) {\n url.port = \"\";\n }\n\n if (cfg.normalizeTrailingSlash) {\n if (url.pathname !== \"/\" && url.pathname.endsWith(\"/\")) {\n url.pathname = url.pathname.slice(0, -1);\n }\n }\n\n const params = new URLSearchParams(url.search);\n const kept: Array<[string, string]> = [];\n for (const [key, value] of params.entries()) {\n if (shouldStripParam(key, cfg)) continue;\n kept.push([key, value]);\n }\n kept.sort((a, b) => {\n if (a[0] < b[0]) return -1;\n if (a[0] > b[0]) return 1;\n if (a[1] < b[1]) return -1;\n if (a[1] > b[1]) return 1;\n return 0;\n });\n\n url.search = kept.length === 0 ? \"\" : new URLSearchParams(kept).toString();\n\n const normalizedUrl = url.toString();\n return { normalizedUrl, key: normalizedUrl };\n}\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport { fetchWithRedirects } from \"../http/fetch.js\";\n\nfunction extractLocs(xml: string): string[] {\n const locs: string[] = [];\n const re = /<loc>\\s*([^<\\s]+)\\s*<\\/loc>/gi;\n let match: RegExpExecArray | null;\n while ((match = re.exec(xml))) {\n const raw = match[1]?.trim();\n if (raw) locs.push(raw);\n }\n return locs;\n}\n\nfunction looksLikeSitemapIndex(xml: string): boolean {\n return /<sitemapindex\\b/i.test(xml);\n}\n\nexport async function fetchSitemapUrls(sitemapUrl: string, cfg: CrawlConfig): Promise<string[]> {\n const res = await fetchWithRedirects(sitemapUrl, {\n timeoutMs: cfg.timeoutMs,\n maxRedirects: cfg.maxRedirects,\n headers: cfg.headers,\n userAgent: cfg.userAgent,\n ignoreHttpsErrors: cfg.ignoreHttpsErrors,\n readBody: true,\n });\n\n if (!res.status || res.status >= 400) {\n throw new Error(`HTTP ${res.status ?? \"error\"}`);\n }\n\n const xml = res.bodyText ?? \"\";\n if (!xml) return [];\n\n const locs = extractLocs(xml);\n if (!looksLikeSitemapIndex(xml)) {\n return locs;\n }\n\n const urls: string[] = [];\n const maxChildSitemaps = 25;\n\n for (const child of locs.slice(0, maxChildSitemaps)) {\n const childRes = await fetchWithRedirects(child, {\n timeoutMs: cfg.timeoutMs,\n maxRedirects: cfg.maxRedirects,\n headers: cfg.headers,\n userAgent: cfg.userAgent,\n ignoreHttpsErrors: cfg.ignoreHttpsErrors,\n readBody: true,\n });\n if (!childRes.status || childRes.status >= 400) continue;\n const childXml = childRes.bodyText ?? \"\";\n for (const url of extractLocs(childXml)) urls.push(url);\n }\n\n return urls;\n}\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport { fetchSitemapUrls } from \"./sitemap.js\";\n\nexport type SeedResult = {\n seeds: string[];\n warnings: string[];\n};\n\nexport async function discoverSeeds(cfg: CrawlConfig): Promise<SeedResult> {\n const warnings: string[] = [];\n const base = new URL(cfg.baseUrl);\n\n const seeds = [base.toString()];\n\n if (cfg.sitemap) {\n const sitemapUrl = new URL(\"/sitemap.xml\", base.origin).toString();\n try {\n const urls = await fetchSitemapUrls(sitemapUrl, cfg);\n for (const url of urls) seeds.push(url);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n warnings.push(`Failed to fetch sitemap: ${sitemapUrl} (${msg})`);\n }\n }\n\n return { seeds, warnings };\n}\n\n","import * as cheerio from \"cheerio\";\n\nexport type ExtractedSeo = {\n title?: string | undefined;\n description?: string | undefined;\n h1Text?: string | undefined;\n h1Count: number;\n canonical?: string | undefined;\n robots?: string | undefined;\n lang?: string | undefined;\n};\n\nfunction cleanText(value: string): string {\n return value.replace(/\\s+/g, \" \").trim();\n}\n\nexport function extractSeo(html: string): ExtractedSeo {\n const $ = cheerio.load(html);\n\n const rawTitle = $(\"head > title\").first().text();\n const title = cleanText(rawTitle);\n\n const rawDescription = $('meta[name=\"description\"]').attr(\"content\") ?? \"\";\n const description = cleanText(rawDescription);\n\n const h1s = $(\"h1\");\n const h1Count = h1s.length;\n const h1Text = h1Count > 0 ? cleanText(h1s.first().text()) : \"\";\n\n const canonical = cleanText($('link[rel=\"canonical\"]').attr(\"href\") ?? \"\");\n const robots = cleanText($('meta[name=\"robots\"]').attr(\"content\") ?? \"\");\n const lang = cleanText($(\"html\").attr(\"lang\") ?? \"\");\n\n const out: ExtractedSeo = { h1Count };\n if (title) out.title = title;\n if (description) out.description = description;\n if (h1Text) out.h1Text = h1Text;\n if (canonical) out.canonical = canonical;\n if (robots) out.robots = robots;\n if (lang) out.lang = lang;\n return out;\n}\n\n","export const REPORT_SCHEMA_VERSION = \"1.0.0\";\n\n","import type { CrawlConfig } from \"../config/schema.js\";\n\nexport type UrlPolicyDecision =\n | { allow: true }\n | { allow: false; reason: \"pagination_limit\" | \"limited_path\" | \"query_variant_limit\" };\n\nexport type UrlPolicyState = {\n variantsByPath: Map<string, Set<string>>;\n warnedPaths: Set<string>;\n};\n\nfunction pathKey(u: URL): string {\n return `${u.origin}${u.pathname}`;\n}\n\nfunction isLimitedPath(cfg: CrawlConfig, u: URL): boolean {\n return cfg.crawl.limitedPathPrefixes.some((p) => u.pathname.startsWith(p));\n}\n\nfunction getPaginationValue(cfg: CrawlConfig, u: URL): number | undefined {\n for (const name of cfg.crawl.paginationParamNames) {\n const v = u.searchParams.get(name);\n if (!v) continue;\n const n = Number(v);\n if (Number.isFinite(n)) return n;\n }\n return undefined;\n}\n\nfunction hasOnlyAllowedQueryParams(allowed: Set<string>, u: URL): boolean {\n for (const key of u.searchParams.keys()) {\n if (!allowed.has(key.toLowerCase())) return false;\n }\n return true;\n}\n\nexport function createUrlPolicyState(): UrlPolicyState {\n return {\n variantsByPath: new Map(),\n warnedPaths: new Set(),\n };\n}\n\nexport function shouldEnqueueUrl(cfg: CrawlConfig, state: UrlPolicyState, normalizedUrl: string): UrlPolicyDecision {\n const u = new URL(normalizedUrl);\n\n const paginationValue = getPaginationValue(cfg, u);\n if (paginationValue !== undefined && paginationValue > cfg.crawl.paginationLimit) {\n return { allow: false, reason: \"pagination_limit\" };\n }\n\n if (isLimitedPath(cfg, u) && u.search) {\n const allowed = new Set(cfg.crawl.paginationParamNames.map((n) => n.toLowerCase()));\n if (!hasOnlyAllowedQueryParams(allowed, u)) {\n return { allow: false, reason: \"limited_path\" };\n }\n }\n\n const key = pathKey(u);\n const variant = u.search; // already normalized\n const set = state.variantsByPath.get(key) ?? new Set<string>();\n if (!set.has(variant) && set.size >= cfg.crawl.perPathQueryVariantLimit) {\n return { allow: false, reason: \"query_variant_limit\" };\n }\n set.add(variant);\n state.variantsByPath.set(key, set);\n\n return { allow: true };\n}\n\n","export class HostDelayLimiter {\n private readonly delayMs: number;\n private readonly tailByOrigin = new Map<string, Promise<void>>();\n private readonly lastByOrigin = new Map<string, number>();\n\n constructor(delayMs: number) {\n this.delayMs = Math.max(0, Math.floor(delayMs));\n }\n\n async waitFor(url: string): Promise<void> {\n if (this.delayMs <= 0) return;\n\n const origin = new URL(url).origin;\n const prev = this.tailByOrigin.get(origin) ?? Promise.resolve();\n\n const next = prev.then(async () => {\n const now = Date.now();\n const last = this.lastByOrigin.get(origin) ?? 0;\n const remaining = this.delayMs - (now - last);\n if (remaining > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, remaining));\n }\n this.lastByOrigin.set(origin, Date.now());\n });\n\n this.tailByOrigin.set(\n origin,\n next.catch(() => {\n // keep chain alive even if a waiter throws\n }),\n );\n\n await next;\n }\n}\n\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport { fetchWithRedirects } from \"../http/fetch.js\";\n\ntype RobotsGroup = {\n userAgents: string[];\n allow: string[];\n disallow: string[];\n};\n\nfunction normalizeUserAgent(ua: string): string {\n return ua.trim().toLowerCase();\n}\n\nfunction parseRobotsTxt(text: string): RobotsGroup[] {\n const lines = text\n .split(/\\r?\\n/)\n .map((l) => l.replace(/#.*$/, \"\").trim())\n .filter(Boolean);\n\n const groups: RobotsGroup[] = [];\n let current: RobotsGroup | null = null;\n\n for (const line of lines) {\n const idx = line.indexOf(\":\");\n if (idx <= 0) continue;\n const key = line.slice(0, idx).trim().toLowerCase();\n const value = line.slice(idx + 1).trim();\n\n if (key === \"user-agent\") {\n if (!current || (current.allow.length + current.disallow.length) > 0) {\n current = { userAgents: [], allow: [], disallow: [] };\n groups.push(current);\n }\n current.userAgents.push(normalizeUserAgent(value));\n continue;\n }\n\n if (!current) continue;\n if (key === \"allow\") current.allow.push(value);\n if (key === \"disallow\") current.disallow.push(value);\n }\n\n return groups;\n}\n\nfunction longestMatch(pathname: string, rules: string[]): number {\n let best = -1;\n for (const rule of rules) {\n if (rule === \"\") continue;\n if (rule === \"/\") return Math.max(best, 1);\n if (pathname.startsWith(rule)) best = Math.max(best, rule.length);\n }\n return best;\n}\n\nfunction pickGroups(groups: RobotsGroup[], ua: string): RobotsGroup[] {\n const uaNorm = normalizeUserAgent(ua);\n const specific = groups.filter((g) => g.userAgents.some((a) => a !== \"*\" && uaNorm.includes(a)));\n if (specific.length) return specific;\n const star = groups.filter((g) => g.userAgents.includes(\"*\"));\n return star;\n}\n\nexport type RobotsPolicyResult =\n | { allowed: true }\n | { allowed: false; reason: \"disallowed\" };\n\nexport class RobotsPolicy {\n private readonly cache = new Map<string, RobotsGroup[] | \"missing\" | \"error\">();\n\n constructor(private readonly cfg: CrawlConfig) {}\n\n async canFetch(url: string): Promise<RobotsPolicyResult> {\n if (!this.cfg.respectRobots) return { allowed: true };\n const u = new URL(url);\n const origin = u.origin;\n\n const cached = this.cache.get(origin);\n let parsed: RobotsGroup[] | \"missing\" | \"error\" | undefined = cached;\n if (!parsed) {\n const robotsUrl = new URL(\"/robots.txt\", origin).toString();\n const res = await fetchWithRedirects(robotsUrl, {\n timeoutMs: this.cfg.timeoutMs,\n maxRedirects: this.cfg.maxRedirects,\n headers: this.cfg.headers,\n userAgent: this.cfg.userAgent,\n ignoreHttpsErrors: this.cfg.ignoreHttpsErrors,\n readBody: true,\n });\n\n if (!res.status || res.status >= 400) {\n parsed = \"missing\";\n } else {\n try {\n const txt = res.bodyText ?? \"\";\n parsed = txt ? parseRobotsTxt(txt) : \"missing\";\n } catch {\n parsed = \"error\";\n }\n }\n this.cache.set(origin, parsed);\n }\n\n if (parsed === \"missing\" || parsed === \"error\") return { allowed: true };\n\n const groups = pickGroups(parsed, this.cfg.userAgent ?? \"qa\");\n if (groups.length === 0) return { allowed: true };\n\n const pathname = u.pathname || \"/\";\n const allowLen = Math.max(...groups.map((g) => longestMatch(pathname, g.allow)), -1);\n const disallowLen = Math.max(...groups.map((g) => longestMatch(pathname, g.disallow)), -1);\n\n if (disallowLen > allowLen) return { allowed: false, reason: \"disallowed\" };\n return { allowed: true };\n }\n}\n\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport { extractAnchorLinks } from \"../extract/links.js\";\nimport { fetchWithRedirects } from \"../http/fetch.js\";\nimport { normalizeUrl } from \"../url/normalize.js\";\nimport {\n compilePathPatterns,\n isAllowedOrigin,\n isInScopePath,\n isNonHttpLink,\n} from \"../url/scope.js\";\nimport type { PageRecord, RunReport } from \"../report/schema.js\";\nimport { discoverSeeds } from \"./seeds.js\";\nimport { extractSeo } from \"../extract/seo.js\";\nimport { REPORT_SCHEMA_VERSION } from \"../report/constants.js\";\nimport { createUrlPolicyState, shouldEnqueueUrl } from \"./url-policy.js\";\nimport { HostDelayLimiter } from \"../http/host-delay.js\";\nimport { RobotsPolicy } from \"./robots.js\";\n\nexport type CrawlResult = {\n report: RunReport;\n};\n\ntype Referrer = { url: string; anchorText?: string | undefined };\n\ntype CrawlState = {\n pagesByKey: Map<string, PageRecord>;\n referrersByKey: Map<string, Referrer[]>;\n};\n\nfunction addReferrer(state: CrawlState, targetKey: string, ref: Referrer): void {\n const list = state.referrersByKey.get(targetKey) ?? [];\n if (list.some((r) => r.url === ref.url && r.anchorText === ref.anchorText)) {\n return;\n }\n list.push(ref);\n state.referrersByKey.set(targetKey, list);\n}\n\nfunction uniqByKey<T>(items: T[], keyFn: (t: T) => string): T[] {\n const seen = new Set<string>();\n const out: T[] = [];\n for (const item of items) {\n const key = keyFn(item);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(item);\n }\n return out;\n}\n\nasync function runWithConcurrency<T>(\n concurrency: number,\n tasks: Array<() => Promise<T>>,\n): Promise<T[]> {\n const results: T[] = new Array(tasks.length);\n let index = 0;\n\n async function worker(): Promise<void> {\n while (true) {\n const current = index;\n index += 1;\n if (current >= tasks.length) return;\n const task = tasks[current];\n if (!task) return;\n results[current] = await task();\n }\n }\n\n const workers = Array.from({ length: Math.max(1, concurrency) }, () => worker());\n await Promise.all(workers);\n return results;\n}\n\nexport async function crawlSite(cfg: CrawlConfig): Promise<CrawlResult> {\n const startedAt = new Date().toISOString();\n const runWarnings: string[] = [];\n const capsReached = {\n maxPages: false,\n maxDepth: false,\n maxRedirects: false,\n queryVariantLimit: false,\n paginationLimit: false,\n externalLinksLimit: false,\n };\n\n const allowedOrigins = new Set<string>([new URL(cfg.baseUrl).origin]);\n const patterns = compilePathPatterns(cfg.include, cfg.exclude);\n\n const state: CrawlState = {\n pagesByKey: new Map(),\n referrersByKey: new Map(),\n };\n const urlPolicyState = createUrlPolicyState();\n const hostDelay = new HostDelayLimiter(cfg.crawl.requestDelayMs);\n const robots = new RobotsPolicy(cfg);\n\n const seedResult = await discoverSeeds(cfg);\n runWarnings.push(...seedResult.warnings);\n\n const normalizedSeeds = uniqByKey(\n seedResult.seeds\n .filter((u) => {\n try {\n const parsed = new URL(u);\n if (!isAllowedOrigin(parsed, allowedOrigins)) return false;\n return isInScopePath(parsed.pathname, patterns);\n } catch {\n return false;\n }\n })\n .map((u) => normalizeUrl(u, cfg.url))\n .filter((n) => {\n const decision = shouldEnqueueUrl(cfg, urlPolicyState, n.normalizedUrl);\n if (decision.allow) return true;\n if (decision.reason === \"pagination_limit\") capsReached.paginationLimit = true;\n if (decision.reason === \"query_variant_limit\") capsReached.queryVariantLimit = true;\n return false;\n }),\n (n) => n.key,\n ).sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n\n let fetchedCount = 0;\n\n type FrontierItem = {\n url: string;\n key: string;\n depth: number;\n };\n\n let frontier: FrontierItem[] = normalizedSeeds.map((s) => ({\n url: s.normalizedUrl,\n key: s.key,\n depth: 0,\n }));\n\n for (let depth = 0; depth <= cfg.maxDepth; depth++) {\n if (frontier.length === 0) break;\n\n const remaining = cfg.maxPages - fetchedCount;\n if (remaining <= 0) {\n capsReached.maxPages = true;\n break;\n }\n\n const batch = frontier\n .filter((f) => !state.pagesByKey.has(f.key))\n .slice(0, remaining);\n\n if (batch.length < frontier.filter((f) => !state.pagesByKey.has(f.key)).length) {\n capsReached.maxPages = true;\n }\n\n type BatchResult = {\n item: FrontierItem;\n page: PageRecord;\n discovered: Array<{ url: string; anchorText?: string | undefined }>;\n };\n\n const tasks = batch.map((item) => async (): Promise<BatchResult> => {\n await hostDelay.waitFor(item.url);\n\n const robotsDecision = await robots.canFetch(item.url);\n if (!robotsDecision.allowed) {\n const page: PageRecord = {\n url: item.url,\n finalUrl: item.url,\n redirectChain: [],\n depth: item.depth,\n discoveredFrom: [],\n error: \"Disallowed by robots.txt\",\n };\n return { item, page, discovered: [] };\n }\n\n const res = await fetchWithRedirects(item.url, {\n timeoutMs: cfg.timeoutMs,\n maxRedirects: cfg.maxRedirects,\n headers: cfg.headers,\n userAgent: cfg.userAgent,\n ignoreHttpsErrors: cfg.ignoreHttpsErrors,\n });\n\n if (res.error === `maxRedirects exceeded (${cfg.maxRedirects})`) {\n capsReached.maxRedirects = true;\n }\n\n const page: PageRecord = {\n url: item.url,\n finalUrl: res.finalUrl,\n status: res.status,\n redirectChain: res.redirectChain.map((h) => ({\n url: h.url,\n status: h.status,\n location: h.location,\n })),\n depth: item.depth,\n discoveredFrom: [],\n error: res.error,\n };\n\n const discovered: Array<{ url: string; anchorText?: string | undefined }> = [];\n const isHtml =\n res.contentType?.toLowerCase().includes(\"text/html\") === true ||\n res.contentType?.toLowerCase().includes(\"application/xhtml+xml\") === true;\n if (res.bodyText && isHtml) {\n const seo = extractSeo(res.bodyText);\n page.title = seo.title;\n page.description = seo.description;\n page.h1Text = seo.h1Text;\n page.h1Count = seo.h1Count;\n page.canonical = seo.canonical;\n page.robots = seo.robots;\n page.lang = seo.lang;\n\n const links = extractAnchorLinks(res.bodyText);\n const outboundLinks: NonNullable<PageRecord[\"outboundLinks\"]> = [];\n for (const link of links) {\n const href = link.href;\n if (!href || isNonHttpLink(href)) continue;\n try {\n const abs = new URL(href, res.finalUrl).toString();\n const absUrl = new URL(abs);\n const internal = isAllowedOrigin(absUrl, allowedOrigins);\n outboundLinks.push({\n url: href,\n resolvedUrl: abs,\n internal,\n anchorText: link.anchorText,\n });\n if (!internal) continue;\n if (!isInScopePath(absUrl.pathname, patterns)) continue;\n discovered.push({ url: abs, anchorText: link.anchorText });\n } catch {\n // ignore invalid URLs\n }\n }\n page.outboundLinks = outboundLinks;\n }\n\n return { item, page, discovered };\n });\n\n const results = await runWithConcurrency(cfg.concurrency, tasks);\n\n const nextKeys = new Set<string>();\n for (const r of results) {\n fetchedCount += 1;\n state.pagesByKey.set(r.item.key, r.page);\n\n const refs = state.referrersByKey.get(r.item.key) ?? [];\n r.page.discoveredFrom = refs\n .slice()\n .sort((a, b) => (a.url < b.url ? -1 : a.url > b.url ? 1 : 0));\n\n for (const d of r.discovered) {\n const normalized = normalizeUrl(d.url, cfg.url);\n const decision = shouldEnqueueUrl(cfg, urlPolicyState, normalized.normalizedUrl);\n if (!decision.allow) {\n if (decision.reason === \"pagination_limit\") {\n capsReached.paginationLimit = true;\n const msg = `Pagination limit hit (limit=${cfg.crawl.paginationLimit}) for ${normalized.normalizedUrl}`;\n if (!runWarnings.includes(msg)) runWarnings.push(msg);\n } else if (decision.reason === \"query_variant_limit\") {\n capsReached.queryVariantLimit = true;\n const msg = `Query variant limit hit (limit=${cfg.crawl.perPathQueryVariantLimit}) for ${new URL(normalized.normalizedUrl).pathname}`;\n if (!runWarnings.includes(msg)) runWarnings.push(msg);\n }\n continue;\n }\n\n nextKeys.add(normalized.key);\n addReferrer(state, normalized.key, { url: r.page.finalUrl, anchorText: d.anchorText });\n }\n }\n\n if (depth === cfg.maxDepth) {\n if (nextKeys.size > 0) capsReached.maxDepth = true;\n break;\n }\n\n const nextCandidates: FrontierItem[] = [];\n for (const key of nextKeys) {\n if (state.pagesByKey.has(key)) continue;\n nextCandidates.push({ url: key, key, depth: depth + 1 });\n }\n\n frontier = uniqByKey(nextCandidates, (n) => n.key).sort((a, b) =>\n a.key < b.key ? -1 : a.key > b.key ? 1 : 0,\n );\n }\n\n const endedAt = new Date().toISOString();\n\n const pages = Array.from(state.pagesByKey.entries())\n .map(([, p]) => p)\n .sort((a, b) => {\n if (a.depth !== b.depth) return a.depth - b.depth;\n if (a.url < b.url) return -1;\n if (a.url > b.url) return 1;\n return 0;\n });\n\n const report: RunReport = {\n schemaVersion: REPORT_SCHEMA_VERSION,\n run: {\n startedAt,\n endedAt,\n baseUrl: cfg.baseUrl,\n config: {\n mode: cfg.mode,\n reportFormat: cfg.reportFormat,\n outputDir: cfg.outputDir,\n maxPages: cfg.maxPages,\n maxDepth: cfg.maxDepth,\n maxRedirects: cfg.maxRedirects,\n concurrency: cfg.concurrency,\n timeoutMs: cfg.timeoutMs,\n sitemap: cfg.sitemap,\n respectRobots: cfg.respectRobots,\n externalLinks: cfg.externalLinks,\n ignoreHttpsErrors: cfg.ignoreHttpsErrors,\n render: {\n maxRenderPages: cfg.render.maxRenderPages,\n screenshotFullPage: cfg.render.screenshotFullPage,\n breakpoints: cfg.render.breakpoints,\n },\n crawl: {\n perPathQueryVariantLimit: cfg.crawl.perPathQueryVariantLimit,\n paginationParamNames: cfg.crawl.paginationParamNames,\n paginationLimit: cfg.crawl.paginationLimit,\n limitedPathPrefixes: cfg.crawl.limitedPathPrefixes,\n requestDelayMs: cfg.crawl.requestDelayMs,\n },\n external: {\n maxExternalLinksChecked: cfg.external.maxExternalLinksChecked,\n },\n include: cfg.include,\n exclude: cfg.exclude,\n userAgent: cfg.userAgent,\n },\n warnings: runWarnings,\n capsReached,\n },\n pages,\n };\n\n return { report };\n}\n","import type { CrawlConfig } from \"./schema.js\";\n\nexport function defaultConfigForBaseUrl(baseUrl: string): CrawlConfig {\n const url = new URL(baseUrl);\n const isLocalhost =\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\";\n\n return {\n baseUrl,\n outputDir: \"qa-output\",\n mode: \"crawl\",\n reportFormat: \"both\",\n\n maxPages: 500,\n maxDepth: 6,\n maxRedirects: 10,\n concurrency: 4,\n timeoutMs: 15_000,\n\n sitemap: true,\n respectRobots: !isLocalhost,\n externalLinks: false,\n\n ignoreHttpsErrors: false,\n userAgent: undefined,\n headers: {},\n\n include: [],\n exclude: [],\n\n url: {\n normalizeTrailingSlash: true,\n stripTrackingParams: true,\n trackingParamPrefixes: [\"utm_\"],\n trackingParamNames: [\"gclid\", \"fbclid\", \"msclkid\", \"ref\"],\n dropQueryParamsExceptAllowlist: false,\n allowlistedQueryParams: [\"page\"],\n ignoredQueryParamPatterns: [\"session\", \"token\"],\n },\n seo: {\n titleMin: 10,\n titleMax: 60,\n descriptionMin: 50,\n descriptionMax: 160,\n },\n render: {\n maxRenderPages: 50,\n screenshotFullPage: true,\n breakpoints: {\n mobile: { width: 375, height: 812 },\n tablet: { width: 768, height: 1024 },\n desktop: { width: 1440, height: 900 },\n },\n },\n crawl: {\n perPathQueryVariantLimit: 3,\n paginationParamNames: [\"page\"],\n paginationLimit: 5,\n limitedPathPrefixes: [\"/search\", \"/calendar\", \"/events\"],\n requestDelayMs: 0,\n },\n external: {\n maxExternalLinksChecked: 0,\n },\n };\n}\n","import { readFileSync } from \"node:fs\";\nimport YAML from \"yaml\";\nimport type { PartialCrawlConfig } from \"./schema.js\";\n\nexport function loadConfigFile(configPath: string): PartialCrawlConfig {\n const raw = readFileSync(configPath, \"utf8\");\n const trimmed = raw.trim();\n\n if (configPath.endsWith(\".json\")) {\n return JSON.parse(trimmed) as PartialCrawlConfig;\n }\n\n try {\n const parsed = YAML.parse(trimmed) as unknown;\n if (parsed && typeof parsed === \"object\") return parsed as PartialCrawlConfig;\n } catch {\n // fall through to JSON attempt\n }\n\n return JSON.parse(trimmed) as PartialCrawlConfig;\n}\n\n","import type { CrawlConfig, PartialCrawlConfig } from \"./schema.js\";\n\nexport function mergeConfig(base: CrawlConfig, override: PartialCrawlConfig): CrawlConfig {\n return {\n ...base,\n ...override,\n headers: {\n ...base.headers,\n ...(override.headers ?? {}),\n },\n include: override.include ?? base.include,\n exclude: override.exclude ?? base.exclude,\n url: {\n ...base.url,\n ...(override.url ?? {}),\n },\n seo: {\n ...base.seo,\n ...(override.seo ?? {}),\n },\n render: {\n ...base.render,\n ...(override.render ?? {}),\n breakpoints: {\n ...base.render.breakpoints,\n ...(override.render?.breakpoints ?? {}),\n },\n },\n crawl: {\n ...base.crawl,\n ...(override.crawl ?? {}),\n },\n external: {\n ...base.external,\n ...(override.external ?? {}),\n },\n };\n}\n","export function parseHeaderPairs(pairs: string[] | undefined): Record<string, string> {\n if (!pairs || pairs.length === 0) return {};\n\n const headers: Record<string, string> = {};\n for (const pair of pairs) {\n const index = pair.indexOf(\":\");\n if (index <= 0) continue;\n const key = pair.slice(0, index).trim();\n const value = pair.slice(index + 1).trim();\n if (!key) continue;\n headers[key] = value;\n }\n return headers;\n}\n\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { RunReport } from \"./schema.js\";\n\nexport function writeJsonReport(outputDir: string, report: RunReport): string {\n mkdirSync(outputDir, { recursive: true });\n const path = join(outputDir, \"report.json\");\n writeFileSync(path, `${JSON.stringify(report, null, 2)}\\n`, \"utf8\");\n return path;\n}\n\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport type { Issue, IssueSeverity, RunReport } from \"../report/schema.js\";\n\nfunction isLocalBaseUrl(baseUrl: string): boolean {\n const u = new URL(baseUrl);\n return u.hostname === \"localhost\" || u.hostname === \"127.0.0.1\" || u.hostname === \"::1\";\n}\n\nfunction severityForNoindex(cfg: CrawlConfig): IssueSeverity {\n return isLocalBaseUrl(cfg.baseUrl) ? \"info\" : \"warning\";\n}\n\nfunction push(\n issues: Issue[],\n severity: IssueSeverity,\n type: string,\n message: string,\n context?: Record<string, unknown>,\n): void {\n const issue: Issue = { severity, type, message };\n if (context) issue.context = context;\n issues.push(issue);\n}\n\nfunction hasNoindex(robots: string | undefined): boolean {\n if (!robots) return false;\n return robots.toLowerCase().split(\",\").some((t) => t.trim() === \"noindex\");\n}\n\nexport function generateIssues(cfg: CrawlConfig, report: RunReport): Issue[] {\n const issues: Issue[] = [];\n const baseOrigin = new URL(cfg.baseUrl).origin;\n\n for (const page of report.pages) {\n if (page.redirectChain.length >= 2) {\n push(issues, \"warning\", \"redirect_chain_long\", `Redirect chain length ${page.redirectChain.length}`, {\n url: page.url,\n finalUrl: page.finalUrl,\n chain: page.redirectChain,\n referrers: page.discoveredFrom,\n });\n } else if (page.redirectChain.length === 1) {\n push(issues, \"info\", \"redirect\", \"URL redirects\", {\n url: page.url,\n finalUrl: page.finalUrl,\n chain: page.redirectChain,\n referrers: page.discoveredFrom,\n });\n }\n\n if (page.error) {\n if (page.error === \"Disallowed by robots.txt\") {\n push(issues, \"info\", \"robots_disallowed\", \"Skipped (disallowed by robots.txt)\", {\n url: page.url,\n referrers: page.discoveredFrom,\n });\n continue;\n }\n push(issues, \"error\", \"fetch_error\", \"Failed to fetch page\", {\n url: page.url,\n error: page.error,\n referrers: page.discoveredFrom,\n });\n continue;\n }\n\n if (typeof page.status === \"number\" && page.status >= 400) {\n push(issues, \"error\", \"broken_internal_link\", `HTTP ${page.status}`, {\n url: page.url,\n status: page.status,\n referrers: page.discoveredFrom,\n });\n continue;\n }\n\n const isHtml = page.h1Count !== undefined || page.title !== undefined || page.description !== undefined;\n if (!isHtml) continue;\n if (page.status !== 200) continue;\n\n if (!page.title) {\n push(issues, \"error\", \"missing_title\", \"Missing <title>\", { url: page.finalUrl });\n } else {\n if (page.title.length < cfg.seo.titleMin) {\n push(issues, \"warning\", \"title_too_short\", \"Title too short\", {\n url: page.finalUrl,\n length: page.title.length,\n min: cfg.seo.titleMin,\n });\n }\n if (page.title.length > cfg.seo.titleMax) {\n push(issues, \"warning\", \"title_too_long\", \"Title too long\", {\n url: page.finalUrl,\n length: page.title.length,\n max: cfg.seo.titleMax,\n });\n }\n }\n\n if (!page.description) {\n push(issues, \"warning\", \"missing_meta_description\", 'Missing meta[name=\"description\"]', {\n url: page.finalUrl,\n });\n } else {\n if (page.description.length < cfg.seo.descriptionMin) {\n push(issues, \"warning\", \"meta_description_too_short\", \"Meta description too short\", {\n url: page.finalUrl,\n length: page.description.length,\n min: cfg.seo.descriptionMin,\n });\n }\n if (page.description.length > cfg.seo.descriptionMax) {\n push(issues, \"warning\", \"meta_description_too_long\", \"Meta description too long\", {\n url: page.finalUrl,\n length: page.description.length,\n max: cfg.seo.descriptionMax,\n });\n }\n }\n\n const h1Count = page.h1Count ?? 0;\n if (h1Count === 0) {\n push(issues, \"warning\", \"missing_h1\", \"Missing <h1>\", { url: page.finalUrl });\n } else if (h1Count > 1) {\n push(issues, \"warning\", \"multiple_h1\", \"Multiple <h1> elements\", {\n url: page.finalUrl,\n count: h1Count,\n });\n }\n\n if (!page.canonical) {\n push(issues, \"warning\", \"missing_canonical\", \"Missing canonical link\", { url: page.finalUrl });\n } else {\n try {\n const canonicalUrl = new URL(page.canonical, page.finalUrl);\n if (canonicalUrl.origin !== baseOrigin) {\n push(issues, \"warning\", \"canonical_cross_origin\", \"Canonical points to different origin\", {\n url: page.finalUrl,\n canonical: canonicalUrl.toString(),\n baseOrigin,\n });\n }\n } catch {\n push(issues, \"warning\", \"canonical_invalid\", \"Canonical is not a valid URL\", {\n url: page.finalUrl,\n canonical: page.canonical,\n });\n }\n }\n\n if (hasNoindex(page.robots)) {\n push(issues, severityForNoindex(cfg), \"noindex\", \"noindex present\", {\n url: page.finalUrl,\n robots: page.robots,\n });\n }\n }\n\n function duplicateIssue(type: string, value: string, urls: string[]): void {\n if (urls.length <= 1) return;\n push(issues, \"warning\", type, `Duplicate ${type.replace(\"duplicate_\", \"\").replaceAll(\"_\", \" \")}`, {\n value,\n pages: urls.slice().sort(),\n count: urls.length,\n });\n }\n\n const titles = new Map<string, string[]>();\n const descriptions = new Map<string, string[]>();\n const h1s = new Map<string, string[]>();\n\n for (const page of report.pages) {\n if (page.status !== 200) continue;\n if (page.title) titles.set(page.title, [...(titles.get(page.title) ?? []), page.finalUrl]);\n if (page.description)\n descriptions.set(page.description, [\n ...(descriptions.get(page.description) ?? []),\n page.finalUrl,\n ]);\n if (page.h1Text) h1s.set(page.h1Text, [...(h1s.get(page.h1Text) ?? []), page.finalUrl]);\n }\n\n for (const [value, urls] of titles) duplicateIssue(\"duplicate_title\", value, urls);\n for (const [value, urls] of descriptions) duplicateIssue(\"duplicate_description\", value, urls);\n for (const [value, urls] of h1s) duplicateIssue(\"duplicate_h1\", value, urls);\n\n return issues;\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { RunReport } from \"./schema.js\";\nimport { buildHtmlReport } from \"./build-html.js\";\n\nexport function writeHtmlReport(outputDir: string, report: RunReport): string {\n mkdirSync(outputDir, { recursive: true });\n const path = join(outputDir, \"report.html\");\n const html = buildHtmlReport(report);\n writeFileSync(path, html, \"utf8\");\n return path;\n}\n\n","import type { Issue, PageRecord, RunReport } from \"./schema.js\";\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nfunction formatDurationMs(startedAt: string, endedAt: string): string | undefined {\n const start = Date.parse(startedAt);\n const end = Date.parse(endedAt);\n if (!Number.isFinite(start) || !Number.isFinite(end)) return undefined;\n const ms = Math.max(0, end - start);\n if (ms < 1_000) return `${ms}ms`;\n const s = Math.round(ms / 100) / 10;\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rs = Math.round((s - m * 60) * 10) / 10;\n return `${m}m ${rs}s`;\n}\n\nfunction issueCountsByType(issues: Issue[]): Array<{ type: string; count: number }> {\n const counts = new Map<string, number>();\n for (const issue of issues) counts.set(issue.type, (counts.get(issue.type) ?? 0) + 1);\n return [...counts.entries()]\n .map(([type, count]) => ({ type, count }))\n .sort((a, b) => (b.count - a.count) || a.type.localeCompare(b.type));\n}\n\nfunction issueCountsBySeverity(issues: Issue[]): Record<string, number> {\n const out: Record<string, number> = { error: 0, warning: 0, info: 0 };\n for (const issue of issues) out[issue.severity] = (out[issue.severity] ?? 0) + 1;\n return out;\n}\n\nfunction getPageIssues(issues: Issue[], page: PageRecord): Issue[] {\n const urls = new Set<string>([page.url, page.finalUrl]);\n return issues.filter((i) => {\n const u = i.context?.url;\n return typeof u === \"string\" && urls.has(u);\n });\n}\n\nfunction safeString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nfunction brokenLinkEntries(issues: Issue[]): Array<{ url: string; issue: Issue }> {\n const broken = issues.filter((i) => i.type === \"broken_internal_link\" || i.type === \"fetch_error\");\n const out: Array<{ url: string; issue: Issue }> = [];\n for (const issue of broken) {\n const url = safeString(issue.context?.url);\n if (!url) continue;\n out.push({ url, issue });\n }\n return out.sort((a, b) => a.url.localeCompare(b.url));\n}\n\nfunction duplicateClusters(issues: Issue[]): Issue[] {\n const types = new Set([\"duplicate_title\", \"duplicate_description\", \"duplicate_h1\"]);\n return issues\n .filter((i) => types.has(i.type))\n .sort((a, b) => a.type.localeCompare(b.type));\n}\n\nfunction pageTitle(page: PageRecord): string {\n return page.title ?? page.finalUrl;\n}\n\nfunction statusBadge(page: PageRecord): string {\n const status = page.status;\n if (typeof status !== \"number\") return `<span class=\"badge badge-muted\">unknown</span>`;\n if (status >= 400) return `<span class=\"badge badge-error\">${status}</span>`;\n if (status >= 300) return `<span class=\"badge badge-warn\">${status}</span>`;\n return `<span class=\"badge badge-ok\">${status}</span>`;\n}\n\nfunction formatMaybe(value: string | undefined): string {\n return value ? escapeHtml(value) : `<span class=\"muted\">—</span>`;\n}\n\nfunction renderReferrers(referrers: unknown): string {\n if (!Array.isArray(referrers) || referrers.length === 0) {\n return `<div class=\"muted\">No referrers captured.</div>`;\n }\n const rows = referrers\n .map((r) => {\n if (!r || typeof r !== \"object\") return \"\";\n const rec = r as Record<string, unknown>;\n const url = safeString(rec[\"url\"]);\n const anchorText = safeString(rec[\"anchorText\"]);\n const urlHtml = url ? `<a href=\"${escapeHtml(url)}\">${escapeHtml(url)}</a>` : `<span class=\"muted\">—</span>`;\n const anchorHtml = anchorText ? escapeHtml(anchorText) : `<span class=\"muted\">—</span>`;\n return `<li>${urlHtml}<span class=\"sep\"> · </span><span class=\"muted\">${anchorHtml}</span></li>`;\n })\n .filter(Boolean)\n .join(\"\");\n return `<ul class=\"list\">${rows}</ul>`;\n}\n\nexport function buildHtmlReport(report: RunReport): string {\n const issues = report.issues ?? [];\n const bySeverity = issueCountsBySeverity(issues);\n const byType = issueCountsByType(issues);\n const duration = formatDurationMs(report.run.startedAt, report.run.endedAt);\n const caps = report.run.capsReached;\n\n const brokenLinks = brokenLinkEntries(issues);\n const duplicates = duplicateClusters(issues);\n\n const pagesHtml = report.pages\n .map((page) => {\n const pageIssues = getPageIssues(issues, page);\n const inboundCount = page.discoveredFrom.length;\n const outbound = page.outboundLinks ?? [];\n const outboundInternal = outbound.filter((l) => l.internal).length;\n const outboundExternal = outbound.filter((l) => !l.internal).length;\n const renderResults = page.render?.results ?? [];\n\n const issueList = pageIssues.length\n ? `<ul class=\"list\">${pageIssues\n .map((i) => `<li><span class=\"sev sev-${i.severity}\">${i.severity}</span> <code>${escapeHtml(i.type)}</code> — ${escapeHtml(i.message)}</li>`)\n .join(\"\")}</ul>`\n : `<div class=\"muted\">No issues captured for this page.</div>`;\n\n const redirectChainHtml =\n page.redirectChain.length === 0\n ? `<span class=\"muted\">—</span>`\n : `<ol class=\"list\">${page.redirectChain\n .map(\n (h) =>\n `<li><code>${escapeHtml(String(h.status))}</code> ${escapeHtml(h.url)}${h.location ? ` <span class=\"muted\">→</span> ${escapeHtml(h.location)}` : \"\"}</li>`,\n )\n .join(\"\")}</ol>`;\n\n const inboundHtml =\n inboundCount === 0\n ? `<div class=\"muted\">—</div>`\n : `<ul class=\"list\">${page.discoveredFrom\n .map((r) => {\n const a = r.anchorText ? ` <span class=\"muted\">(${escapeHtml(r.anchorText)})</span>` : \"\";\n return `<li><a href=\"${escapeHtml(r.url)}\">${escapeHtml(r.url)}</a>${a}</li>`;\n })\n .join(\"\")}</ul>`;\n\n const renderHtml =\n renderResults.length === 0\n ? `<div class=\"muted\">Not rendered.</div>`\n : `<div class=\"grid\">${renderResults\n .map((r) => {\n const consoleCount = r.consoleErrors.length;\n const reqFailCount = r.requestFailures.length;\n const meta = [\n `viewport ${r.viewport.width}×${r.viewport.height}`,\n r.fullPage ? \"full page\" : \"viewport only\",\n typeof r.pageStatus === \"number\" ? `status ${r.pageStatus}` : \"status unknown\",\n r.pageError ? \"page error\" : \"ok\",\n consoleCount ? `${consoleCount} console errors` : \"0 console errors\",\n reqFailCount ? `${reqFailCount} request failures` : \"0 request failures\",\n ].join(\" · \");\n return `\n <div class=\"card\">\n <div><strong>${escapeHtml(r.breakpoint)}</strong> <span class=\"muted\">${escapeHtml(meta)}</span></div>\n <div style=\"margin-top:8px\">\n <a href=\"${escapeHtml(r.screenshotPath)}\" target=\"_blank\" rel=\"noreferrer\">\n <img src=\"${escapeHtml(r.screenshotPath)}\" alt=\"${escapeHtml(page.finalUrl)} ${escapeHtml(r.breakpoint)}\" style=\"width:100%; border-radius:8px; border:1px solid var(--border)\" />\n </a>\n </div>\n </div>\n `.trim();\n })\n .join(\"\")}</div>`;\n\n return `\n <details class=\"card\" id=\"page-${encodeURIComponent(page.finalUrl)}\">\n <summary class=\"card-summary\">\n ${statusBadge(page)}\n <span class=\"page-title\">${escapeHtml(pageTitle(page))}</span>\n <span class=\"muted\">${escapeHtml(page.finalUrl)}</span>\n ${renderResults.length ? `<span class=\"badge badge-muted\">rendered</span>` : \"\"}\n </summary>\n <div class=\"grid\">\n <div>\n <h4>Overview</h4>\n <table class=\"table\">\n <tr><th>URL</th><td><code>${escapeHtml(page.url)}</code></td></tr>\n <tr><th>Final URL</th><td><code>${escapeHtml(page.finalUrl)}</code></td></tr>\n <tr><th>Status</th><td>${statusBadge(page)}</td></tr>\n <tr><th>Depth</th><td>${escapeHtml(String(page.depth))}</td></tr>\n <tr><th>Inbound links</th><td>${escapeHtml(String(inboundCount))}</td></tr>\n <tr><th>Outbound links</th><td>${escapeHtml(String(outbound.length))} <span class=\"muted\">(internal ${outboundInternal}, external ${outboundExternal})</span></td></tr>\n </table>\n </div>\n <div>\n <h4>SEO</h4>\n <table class=\"table\">\n <tr><th>Title</th><td>${formatMaybe(page.title)}</td></tr>\n <tr><th>Description</th><td>${formatMaybe(page.description)}</td></tr>\n <tr><th>H1</th><td>${formatMaybe(page.h1Text)} <span class=\"muted\">(count ${escapeHtml(String(page.h1Count ?? 0))})</span></td></tr>\n <tr><th>Canonical</th><td>${formatMaybe(page.canonical)}</td></tr>\n <tr><th>Robots</th><td>${formatMaybe(page.robots)}</td></tr>\n <tr><th>Lang</th><td>${formatMaybe(page.lang)}</td></tr>\n </table>\n </div>\n </div>\n\n <div class=\"grid\">\n <div>\n <h4>Redirect Chain</h4>\n ${redirectChainHtml}\n </div>\n <div>\n <h4>Referrers</h4>\n ${inboundHtml}\n </div>\n </div>\n\n <h4>Render</h4>\n ${renderHtml}\n\n <h4>Issues</h4>\n ${issueList}\n </details>\n `.trim();\n })\n .join(\"\\n\");\n\n const brokenLinksHtml =\n brokenLinks.length === 0\n ? `<div class=\"muted\">No broken links found.</div>`\n : `<div class=\"stack\">${brokenLinks\n .map(({ url, issue }) => {\n const statusRaw = issue.context?.[\"status\"];\n const status =\n typeof statusRaw === \"number\"\n ? String(statusRaw)\n : typeof statusRaw === \"string\"\n ? statusRaw\n : undefined;\n const message =\n issue.type === \"fetch_error\"\n ? safeString(issue.context?.error) ?? issue.message\n : issue.message;\n return `\n <details class=\"card\">\n <summary class=\"card-summary\">\n <span class=\"sev sev-${issue.severity}\">${issue.severity}</span>\n <span class=\"page-title\">${escapeHtml(url)}</span>\n ${status ? `<span class=\"muted\">HTTP ${escapeHtml(status)}</span>` : \"\"}\n </summary>\n <div class=\"muted\">${escapeHtml(message)}</div>\n <h4>Referrers</h4>\n ${renderReferrers(issue.context?.referrers)}\n </details>\n `.trim();\n })\n .join(\"\\n\")}</div>`;\n\n const duplicatesHtml =\n duplicates.length === 0\n ? `<div class=\"muted\">No duplicates found.</div>`\n : `<div class=\"stack\">${duplicates\n .map((issue) => {\n const value = safeString(issue.context?.value) ?? \"\";\n const pages = Array.isArray(issue.context?.pages) ? (issue.context?.pages as unknown[]) : [];\n return `\n <details class=\"card\">\n <summary class=\"card-summary\">\n <span class=\"sev sev-${issue.severity}\">${issue.severity}</span>\n <code>${escapeHtml(issue.type)}</code>\n <span class=\"muted\">${escapeHtml(String(issue.context?.count ?? pages.length))} pages</span>\n </summary>\n <div><strong>Value</strong>: ${value ? `<code>${escapeHtml(value)}</code>` : `<span class=\"muted\">—</span>`}</div>\n <h4>Pages</h4>\n <ul class=\"list\">${pages\n .map((p) => (typeof p === \"string\" ? `<li><a href=\"${escapeHtml(p)}\">${escapeHtml(p)}</a></li>` : \"\"))\n .filter(Boolean)\n .join(\"\")}</ul>\n </details>\n `.trim();\n })\n .join(\"\\n\")}</div>`;\n\n const capsHtml = `\n <ul class=\"list\">\n <li>maxPages: ${caps.maxPages ? `<span class=\"badge badge-warn\">reached</span>` : `<span class=\"badge badge-ok\">ok</span>`}</li>\n <li>maxDepth: ${caps.maxDepth ? `<span class=\"badge badge-warn\">reached</span>` : `<span class=\"badge badge-ok\">ok</span>`}</li>\n <li>maxRedirects: ${caps.maxRedirects ? `<span class=\"badge badge-warn\">reached</span>` : `<span class=\"badge badge-ok\">ok</span>`}</li>\n <li>queryVariantLimit: ${caps.queryVariantLimit ? `<span class=\"badge badge-warn\">reached</span>` : `<span class=\"badge badge-ok\">ok</span>`}</li>\n <li>paginationLimit: ${caps.paginationLimit ? `<span class=\"badge badge-warn\">reached</span>` : `<span class=\"badge badge-ok\">ok</span>`}</li>\n <li>externalLinksLimit: ${caps.externalLinksLimit ? `<span class=\"badge badge-warn\">reached</span>` : `<span class=\"badge badge-ok\">ok</span>`}</li>\n </ul>\n `.trim();\n\n const issueTypeList = byType\n .map(({ type, count }) => `<li><code>${escapeHtml(type)}</code> <span class=\"muted\">(${count})</span></li>`)\n .join(\"\");\n\n const configJson = escapeHtml(JSON.stringify(report.run.config, null, 2));\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>QA Report — ${escapeHtml(report.run.baseUrl)}</title>\n <style>\n :root { color-scheme: light; --bg:#0b0f14; --panel:#111827; --text:#e5e7eb; --muted:#9ca3af; --ok:#10b981; --warn:#f59e0b; --err:#ef4444; --info:#60a5fa; --border:#1f2937; }\n body { margin:0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; background: var(--bg); color: var(--text); }\n a { color: #93c5fd; }\n code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.95em; }\n .container { max-width: 1100px; margin: 0 auto; padding: 24px; }\n .header { display:flex; flex-wrap:wrap; gap: 12px; align-items: baseline; }\n .header h1 { margin: 0; font-size: 20px; }\n .muted { color: var(--muted); }\n .grid { display:grid; grid-template-columns: 1fr; gap: 16px; }\n @media (min-width: 900px) { .grid { grid-template-columns: 1fr 1fr; } }\n .card { background: rgba(17,24,39,0.9); border: 1px solid var(--border); border-radius: 10px; padding: 12px; }\n .card-summary { cursor:pointer; display:flex; flex-wrap:wrap; gap: 10px; align-items:center; }\n .page-title { font-weight: 600; }\n .table { width:100%; border-collapse: collapse; }\n .table th { text-align:left; width: 140px; color: var(--muted); font-weight: 500; padding: 6px 0; }\n .table td { padding: 6px 0; }\n .list { margin: 8px 0 0; padding-left: 18px; }\n .sep { color: var(--muted); }\n .stack { display:flex; flex-direction:column; gap: 12px; }\n .badge { display:inline-block; border-radius: 999px; padding: 2px 8px; font-size: 12px; border: 1px solid var(--border); }\n .badge-ok { background: rgba(16,185,129,0.15); border-color: rgba(16,185,129,0.25); }\n .badge-warn { background: rgba(245,158,11,0.15); border-color: rgba(245,158,11,0.25); }\n .badge-error { background: rgba(239,68,68,0.15); border-color: rgba(239,68,68,0.25); }\n .badge-muted { background: rgba(156,163,175,0.12); border-color: rgba(156,163,175,0.25); }\n .sev { display:inline-block; border-radius: 6px; padding: 2px 6px; font-size: 12px; text-transform: uppercase; letter-spacing: .03em; }\n .sev-error { background: rgba(239,68,68,0.15); color: var(--text); border: 1px solid rgba(239,68,68,0.25); }\n .sev-warning { background: rgba(245,158,11,0.15); color: var(--text); border: 1px solid rgba(245,158,11,0.25); }\n .sev-info { background: rgba(96,165,250,0.15); color: var(--text); border: 1px solid rgba(96,165,250,0.25); }\n details > summary { list-style: none; }\n details > summary::-webkit-details-marker { display:none; }\n .section { margin-top: 18px; }\n pre { background: #0a0f1a; border: 1px solid var(--border); border-radius: 10px; padding: 12px; overflow:auto; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>QA Report</h1>\n <span class=\"muted\">${escapeHtml(report.run.baseUrl)}</span>\n </div>\n\n <div class=\"section card\">\n <h3 style=\"margin-top:0\">Run Summary</h3>\n <div class=\"grid\">\n <div>\n <table class=\"table\">\n <tr><th>Started</th><td><code>${escapeHtml(report.run.startedAt)}</code></td></tr>\n <tr><th>Ended</th><td><code>${escapeHtml(report.run.endedAt)}</code></td></tr>\n <tr><th>Duration</th><td>${duration ? `<code>${escapeHtml(duration)}</code>` : `<span class=\"muted\">—</span>`}</td></tr>\n <tr><th>Pages</th><td><code>${escapeHtml(String(report.pages.length))}</code></td></tr>\n <tr><th>Issues</th><td><code>${escapeHtml(String(issues.length))}</code> <span class=\"muted\">(error ${bySeverity.error}, warning ${bySeverity.warning}, info ${bySeverity.info})</span></td></tr>\n </table>\n </div>\n <div>\n <div><strong>Caps reached</strong></div>\n ${capsHtml}\n ${report.run.warnings.length ? `<div style=\"margin-top:10px\"><strong>Warnings</strong><ul class=\"list\">${report.run.warnings.map((w) => `<li>${escapeHtml(w)}</li>`).join(\"\")}</ul></div>` : `<div style=\"margin-top:10px\" class=\"muted\">No run warnings.</div>`}\n </div>\n </div>\n </div>\n\n <div class=\"section card\">\n <h3 style=\"margin-top:0\">Issue Dashboard</h3>\n ${issues.length ? `<ul class=\"list\">${issueTypeList}</ul>` : `<div class=\"muted\">No issues found.</div>`}\n </div>\n\n <div class=\"section\">\n <h2>Broken Links</h2>\n ${brokenLinksHtml}\n </div>\n\n <div class=\"section\">\n <h2>Duplicates</h2>\n ${duplicatesHtml}\n </div>\n\n <div class=\"section\">\n <h2>Pages</h2>\n <div class=\"stack\">\n ${pagesHtml || `<div class=\"muted\">No pages captured.</div>`}\n </div>\n </div>\n\n <div class=\"section card\">\n <h3 style=\"margin-top:0\">Config</h3>\n <pre><code>${configJson}</code></pre>\n </div>\n </div>\n</body>\n</html>`;\n}\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport type { RunReport } from \"./schema.js\";\nimport { REPORT_SCHEMA_VERSION } from \"./constants.js\";\n\nexport function createEmptyReport(cfg: CrawlConfig, startedAt: string): RunReport {\n return {\n schemaVersion: REPORT_SCHEMA_VERSION,\n run: {\n startedAt,\n endedAt: startedAt,\n baseUrl: cfg.baseUrl,\n config: {\n mode: cfg.mode,\n reportFormat: cfg.reportFormat,\n outputDir: cfg.outputDir,\n maxPages: cfg.maxPages,\n maxDepth: cfg.maxDepth,\n maxRedirects: cfg.maxRedirects,\n concurrency: cfg.concurrency,\n timeoutMs: cfg.timeoutMs,\n sitemap: cfg.sitemap,\n respectRobots: cfg.respectRobots,\n externalLinks: cfg.externalLinks,\n ignoreHttpsErrors: cfg.ignoreHttpsErrors,\n render: {\n maxRenderPages: cfg.render.maxRenderPages,\n screenshotFullPage: cfg.render.screenshotFullPage,\n breakpoints: cfg.render.breakpoints,\n },\n crawl: {\n perPathQueryVariantLimit: cfg.crawl.perPathQueryVariantLimit,\n paginationParamNames: cfg.crawl.paginationParamNames,\n paginationLimit: cfg.crawl.paginationLimit,\n limitedPathPrefixes: cfg.crawl.limitedPathPrefixes,\n requestDelayMs: cfg.crawl.requestDelayMs,\n },\n external: {\n maxExternalLinksChecked: cfg.external.maxExternalLinksChecked,\n },\n include: cfg.include,\n exclude: cfg.exclude,\n userAgent: cfg.userAgent,\n },\n warnings: [],\n capsReached: {\n maxPages: false,\n maxDepth: false,\n maxRedirects: false,\n queryVariantLimit: false,\n paginationLimit: false,\n externalLinksLimit: false,\n },\n },\n pages: [],\n issues: [],\n };\n}\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport { discoverSeeds } from \"../crawl/seeds.js\";\nimport { normalizeUrl } from \"../url/normalize.js\";\nimport type { RunReport } from \"../report/schema.js\";\n\ntype Candidate = { url: string; group: number; rank: number };\n\nfunction isInternalToBase(baseUrl: string, candidateUrl: string): boolean {\n try {\n const base = new URL(baseUrl);\n const u = new URL(candidateUrl);\n return u.origin === base.origin;\n } catch {\n return false;\n }\n}\n\nfunction sortCandidates(candidates: Candidate[]): Candidate[] {\n return candidates.sort(\n (a, b) => (a.group - b.group) || (a.rank - b.rank) || a.url.localeCompare(b.url),\n );\n}\n\nexport async function selectRenderUrlsForFull(cfg: CrawlConfig, report: RunReport): Promise<string[]> {\n const candidates: Candidate[] = [];\n\n candidates.push({ url: cfg.baseUrl, group: 0, rank: 0 });\n\n const issueUrls = new Set<string>();\n for (const issue of report.issues ?? []) {\n const u = issue.context?.url;\n if (typeof u === \"string\" && isInternalToBase(cfg.baseUrl, u)) issueUrls.add(u);\n }\n for (const u of [...issueUrls].sort()) candidates.push({ url: u, group: 1, rank: 0 });\n\n const topInbound = report.pages\n .filter((p) => p.status === 200)\n .slice()\n .sort((a, b) => (b.discoveredFrom.length - a.discoveredFrom.length) || a.finalUrl.localeCompare(b.finalUrl))\n .slice(0, 20)\n .map((p) => p.finalUrl);\n for (const [index, u] of topInbound.entries()) candidates.push({ url: u, group: 2, rank: index });\n\n if (cfg.sitemap) {\n const seedResult = await discoverSeeds(cfg);\n for (const u of seedResult.seeds) {\n if (!isInternalToBase(cfg.baseUrl, u)) continue;\n candidates.push({ url: u, group: 3, rank: 0 });\n }\n }\n\n const used = new Set<string>();\n const out: string[] = [];\n for (const c of sortCandidates(candidates)) {\n const normalized = normalizeUrl(c.url, cfg.url).normalizedUrl;\n if (used.has(normalized)) continue;\n used.add(normalized);\n out.push(normalized);\n if (out.length >= cfg.render.maxRenderPages) break;\n }\n\n return out;\n}\n\nexport async function selectRenderUrlsForRenderOnly(cfg: CrawlConfig): Promise<string[]> {\n const seedResult = await discoverSeeds(cfg);\n const candidates: Candidate[] = seedResult.seeds\n .filter((u) => isInternalToBase(cfg.baseUrl, u))\n .map((u) => ({ url: u, group: 0, rank: 0 }));\n\n const used = new Set<string>();\n const out: string[] = [];\n for (const c of sortCandidates(candidates)) {\n const normalized = normalizeUrl(c.url, cfg.url).normalizedUrl;\n if (used.has(normalized)) continue;\n used.add(normalized);\n out.push(normalized);\n if (out.length >= cfg.render.maxRenderPages) break;\n }\n return out;\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport type ScreenshotPathInput = {\n outputDir: string;\n url: string;\n breakpoint: string;\n};\n\nfunction slugifyPath(pathname: string): string {\n if (!pathname || pathname === \"/\") return \"home\";\n return pathname\n .replace(/^\\//, \"\")\n .replace(/\\/$/, \"\")\n .split(\"/\")\n .filter(Boolean)\n .join(\"-\")\n .replace(/[^a-zA-Z0-9]+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .toLowerCase() || \"page\";\n}\n\nfunction shortHash(value: string): string {\n return createHash(\"sha1\").update(value).digest(\"hex\").slice(0, 10);\n}\n\nexport function getScreenshotRelativePath(input: ScreenshotPathInput, used: Set<string>): string {\n const u = new URL(input.url);\n const slug = slugifyPath(u.pathname);\n const queryHash = u.search ? shortHash(u.search) : \"\";\n const baseName = `${slug}${queryHash ? `__${queryHash}` : \"\"}__${input.breakpoint}.png`;\n\n let rel = `screenshots/${input.breakpoint}/${baseName}`;\n if (!used.has(rel)) {\n used.add(rel);\n return rel;\n }\n\n const disambiguator = shortHash(u.toString());\n rel = `screenshots/${input.breakpoint}/${slug}__${disambiguator}__${input.breakpoint}.png`;\n used.add(rel);\n return rel;\n}\n\nexport function ensureScreenshotDir(outputDir: string, breakpoint: string): string {\n const dir = join(outputDir, \"screenshots\", breakpoint);\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport type {\n RenderBreakpointResult,\n RenderConsoleMessage,\n RenderRequestFailure,\n RunReport,\n} from \"../report/schema.js\";\nimport { ensureScreenshotDir, getScreenshotRelativePath } from \"./screenshot-path.js\";\nimport { join } from \"node:path\";\nimport { chromium } from \"playwright\";\n\nfunction getOrCreatePage(report: RunReport, url: string): { pageIndex: number } {\n const existingIndex = report.pages.findIndex((p) => p.finalUrl === url || p.url === url);\n if (existingIndex >= 0) return { pageIndex: existingIndex };\n\n report.pages.push({\n url,\n finalUrl: url,\n redirectChain: [],\n depth: 0,\n discoveredFrom: [],\n });\n return { pageIndex: report.pages.length - 1 };\n}\n\nexport async function renderPages(cfg: CrawlConfig, report: RunReport, urls: string[]): Promise<void> {\n const usedScreenshots = new Set<string>();\n\n const breakpointEntries = Object.entries(cfg.render.breakpoints).sort((a, b) => a[0].localeCompare(b[0]));\n if (breakpointEntries.length === 0) return;\n\n for (const [breakpointName] of breakpointEntries) {\n ensureScreenshotDir(cfg.outputDir, breakpointName);\n }\n\n const browser = await chromium.launch({ headless: true });\n try {\n const contexts = await Promise.all(\n breakpointEntries.map(async ([name, viewport]) => {\n const context = await browser.newContext({\n viewport,\n ignoreHTTPSErrors: cfg.ignoreHttpsErrors,\n extraHTTPHeaders: cfg.headers,\n ...(cfg.userAgent ? { userAgent: cfg.userAgent } : {}),\n });\n return { name, viewport, context };\n }),\n );\n\n try {\n for (const url of urls) {\n const { pageIndex } = getOrCreatePage(report, url);\n const pageRecord = report.pages[pageIndex]!;\n\n const renderResults: RenderBreakpointResult[] = [];\n\n for (const { name: breakpoint, viewport, context } of contexts) {\n const consoleErrors: RenderConsoleMessage[] = [];\n const requestFailures: RenderRequestFailure[] = [];\n let pageError: string | undefined;\n let pageStatus: number | undefined;\n\n const page = await context.newPage();\n page.on(\"console\", (msg) => {\n if (msg.type() !== \"error\") return;\n const loc = msg.location();\n const location: RenderConsoleMessage[\"location\"] = {\n ...(loc.url ? { url: loc.url } : {}),\n ...(loc.lineNumber ? { lineNumber: loc.lineNumber } : {}),\n ...(loc.columnNumber ? { columnNumber: loc.columnNumber } : {}),\n };\n consoleErrors.push({\n type: msg.type(),\n text: msg.text(),\n location: Object.keys(location).length ? location : undefined,\n });\n });\n page.on(\"pageerror\", (err) => {\n pageError = err instanceof Error ? err.message : String(err);\n });\n page.on(\"requestfailed\", (req) => {\n const failure = req.failure();\n requestFailures.push({\n url: req.url(),\n method: req.method(),\n resourceType: req.resourceType(),\n failure: failure?.errorText ?? undefined,\n });\n });\n\n try {\n const response = await page.goto(url, { waitUntil: \"load\", timeout: cfg.timeoutMs });\n pageStatus = response?.status();\n } catch (error) {\n pageError = pageError ?? (error instanceof Error ? error.message : String(error));\n }\n\n const rel = getScreenshotRelativePath(\n { outputDir: cfg.outputDir, url, breakpoint },\n usedScreenshots,\n );\n const abs = join(cfg.outputDir, rel);\n const dir = join(cfg.outputDir, \"screenshots\", breakpoint);\n\n try {\n await page.screenshot({\n path: abs,\n fullPage: cfg.render.screenshotFullPage,\n });\n } catch (error) {\n pageError = pageError ?? (error instanceof Error ? error.message : String(error));\n } finally {\n await page.close();\n }\n\n // Ensure dir exists even if screenshot failed early\n ensureScreenshotDir(cfg.outputDir, breakpoint);\n void dir;\n\n renderResults.push({\n breakpoint,\n viewport,\n fullPage: cfg.render.screenshotFullPage,\n screenshotPath: rel,\n pageStatus,\n pageError,\n consoleErrors,\n requestFailures,\n });\n }\n\n pageRecord.render = { results: renderResults };\n }\n } finally {\n await Promise.all(contexts.map((c) => c.context.close()));\n }\n } finally {\n await browser.close();\n }\n}\n","import type { Issue, RunReport } from \"../report/schema.js\";\n\nfunction push(issues: Issue[], issue: Issue): void {\n issues.push(issue);\n}\n\nexport function generateRenderIssues(report: RunReport): Issue[] {\n const issues: Issue[] = [];\n\n for (const page of report.pages) {\n const render = page.render;\n if (!render) continue;\n\n for (const r of render.results) {\n const baseContext = {\n url: page.finalUrl,\n breakpoint: r.breakpoint,\n screenshotPath: r.screenshotPath,\n };\n\n if (typeof r.pageStatus === \"number\" && r.pageStatus >= 400) {\n push(issues, {\n severity: \"error\",\n type: \"render_http_error\",\n message: `Rendered page returned HTTP ${r.pageStatus}`,\n context: { ...baseContext, status: r.pageStatus },\n });\n }\n\n if (r.pageError) {\n push(issues, {\n severity: \"error\",\n type: \"render_page_error\",\n message: \"Page error while rendering\",\n context: { ...baseContext, error: r.pageError },\n });\n }\n\n if (r.consoleErrors.length > 0) {\n push(issues, {\n severity: \"error\",\n type: \"render_console_error\",\n message: `${r.consoleErrors.length} console error(s) while rendering`,\n context: { ...baseContext, count: r.consoleErrors.length, sample: r.consoleErrors.slice(0, 10) },\n });\n }\n\n if (r.requestFailures.length > 0) {\n push(issues, {\n severity: \"warning\",\n type: \"render_request_failed\",\n message: `${r.requestFailures.length} failed request(s) while rendering`,\n context: { ...baseContext, count: r.requestFailures.length, sample: r.requestFailures.slice(0, 10) },\n });\n }\n }\n }\n\n return issues;\n}\n\n","import { createHash } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction shortHash(value: string): string {\n return createHash(\"sha1\").update(value).digest(\"hex\").slice(0, 10);\n}\n\nexport function formatTimestampForDir(iso: string): string {\n // 2026-01-11T12:56:57.827Z -> 2026-01-11T12-56-57Z\n return iso.replace(/\\.\\d{3}Z$/, \"Z\").replace(/:/g, \"-\");\n}\n\nexport function slugifyUrlForDir(baseUrl: string): string {\n const u = new URL(baseUrl);\n const host = u.host; // includes port if present\n const path = u.pathname && u.pathname !== \"/\" ? u.pathname : \"\";\n const raw = `${host}${path}`;\n const slug = slugify(raw);\n if (slug.length <= 80) return slug || \"site\";\n return `${slug.slice(0, 60)}-${shortHash(raw)}`;\n}\n\nexport function createRunOutputDir(outputRootDir: string, baseUrl: string, startedAtIso: string): string {\n mkdirSync(outputRootDir, { recursive: true });\n const dirName = `${formatTimestampForDir(startedAtIso)}__${slugifyUrlForDir(baseUrl)}`;\n const outDir = join(outputRootDir, dirName);\n mkdirSync(outDir, { recursive: true });\n return outDir;\n}\n\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport type { RunReport } from \"./schema.js\";\nimport { REPORT_SCHEMA_VERSION } from \"./constants.js\";\nimport { getToolVersion } from \"../version.js\";\n\nexport type RunMetadataFile = {\n schemaVersion: string;\n toolVersion: string;\n outputDir: string;\n startedAt: string;\n endedAt: string;\n baseUrl: string;\n files: string[];\n};\n\nexport function writeRunMetadata(outputDir: string, report: RunReport, absoluteFilesWritten: string[]): string {\n mkdirSync(outputDir, { recursive: true });\n const path = join(outputDir, \"run-metadata.json\");\n const files = absoluteFilesWritten\n .map((p) => relative(outputDir, p))\n .filter((p) => !p.startsWith(\"..\"))\n .sort();\n const metadata: RunMetadataFile = {\n schemaVersion: REPORT_SCHEMA_VERSION,\n toolVersion: getToolVersion(),\n outputDir,\n startedAt: report.run.startedAt,\n endedAt: report.run.endedAt,\n baseUrl: report.run.baseUrl,\n files,\n };\n writeFileSync(path, `${JSON.stringify(metadata, null, 2)}\\n`, \"utf8\");\n return path;\n}\n\n","import { readFileSync } from \"node:fs\";\n\nexport function getToolVersion(): string {\n try {\n const raw = readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\");\n const pkg = JSON.parse(raw) as { version?: unknown };\n return typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n","export function validateRegexList(patterns: string[], label: string): void {\n for (const pattern of patterns) {\n try {\n new RegExp(pattern);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid ${label} regex: ${pattern} (${msg})`);\n }\n }\n}\n","import type { CrawlConfig } from \"../config/schema.js\";\nimport type { Issue, PageReferrer, RunReport } from \"../report/schema.js\";\nimport { fetchWithRedirects } from \"../http/fetch.js\";\nimport { HostDelayLimiter } from \"../http/host-delay.js\";\nimport { normalizeUrl } from \"../url/normalize.js\";\n\ntype ExternalReferrer = PageReferrer & { pageUrl: string };\n\ntype ExternalTarget = {\n url: string;\n referrers: ExternalReferrer[];\n};\n\nfunction isExternalToBase(cfg: CrawlConfig, url: string): boolean {\n try {\n const base = new URL(cfg.baseUrl);\n const u = new URL(url);\n return u.origin !== base.origin;\n } catch {\n return false;\n }\n}\n\nfunction addReferrer(map: Map<string, ExternalTarget>, targetUrl: string, ref: ExternalReferrer): void {\n const entry = map.get(targetUrl) ?? { url: targetUrl, referrers: [] };\n if (!entry.referrers.some((r) => r.pageUrl === ref.pageUrl && r.url === ref.url && r.anchorText === ref.anchorText)) {\n entry.referrers.push(ref);\n }\n map.set(targetUrl, entry);\n}\n\nasync function runWithConcurrency<T>(concurrency: number, tasks: Array<() => Promise<T>>): Promise<T[]> {\n const results: T[] = new Array(tasks.length);\n let index = 0;\n\n async function worker(): Promise<void> {\n while (true) {\n const current = index;\n index += 1;\n if (current >= tasks.length) return;\n const task = tasks[current];\n if (!task) return;\n results[current] = await task();\n }\n }\n\n await Promise.all(Array.from({ length: Math.max(1, concurrency) }, () => worker()));\n return results;\n}\n\nexport async function checkExternalLinks(cfg: CrawlConfig, report: RunReport): Promise<Issue[]> {\n if (!cfg.externalLinks) return [];\n if (cfg.external.maxExternalLinksChecked <= 0) return [];\n\n const urlCfg = { ...cfg.url, dropQueryParamsExceptAllowlist: false };\n const targets = new Map<string, ExternalTarget>();\n\n for (const page of report.pages) {\n const outbound = page.outboundLinks ?? [];\n for (const link of outbound) {\n if (link.internal) continue;\n const resolved = link.resolvedUrl;\n if (!isExternalToBase(cfg, resolved)) continue;\n const normalized = normalizeUrl(resolved, urlCfg).normalizedUrl;\n addReferrer(targets, normalized, {\n pageUrl: page.finalUrl,\n url: page.finalUrl,\n anchorText: link.anchorText,\n });\n }\n }\n\n const unique = [...targets.values()].sort((a, b) => a.url.localeCompare(b.url));\n const limited = unique.slice(0, cfg.external.maxExternalLinksChecked);\n if (unique.length > limited.length) {\n report.run.capsReached.externalLinksLimit = true;\n report.run.warnings.push(\n `External link cap reached (maxExternalLinksChecked=${cfg.external.maxExternalLinksChecked}); checked ${limited.length} of ${unique.length}`,\n );\n }\n\n const hostDelay = new HostDelayLimiter(cfg.crawl.requestDelayMs);\n const tasks = limited.map((t) => async () => {\n await hostDelay.waitFor(t.url);\n const res = await fetchWithRedirects(t.url, {\n timeoutMs: cfg.timeoutMs,\n maxRedirects: cfg.maxRedirects,\n headers: cfg.headers,\n userAgent: cfg.userAgent,\n ignoreHttpsErrors: cfg.ignoreHttpsErrors,\n });\n\n if (res.error) {\n const issue: Issue = {\n severity: \"warning\",\n type: \"external_link_fetch_error\",\n message: \"Failed to fetch external link\",\n context: {\n url: t.url,\n error: res.error,\n referrers: t.referrers.map((r) => ({ url: r.pageUrl, anchorText: r.anchorText })),\n },\n };\n return issue;\n }\n\n const status = res.status ?? 0;\n if (status >= 400) {\n const issue: Issue = {\n severity: \"warning\",\n type: \"external_link_http_error\",\n message: `HTTP ${status}`,\n context: {\n url: t.url,\n status,\n referrers: t.referrers.map((r) => ({ url: r.pageUrl, anchorText: r.anchorText })),\n },\n };\n return issue;\n }\n\n return null;\n });\n\n const results = await runWithConcurrency(cfg.concurrency, tasks);\n return results.filter((i): i is Issue => i !== null);\n}\n\n","import { runCli } from \"./cli/run.js\";\n\nconst exitCode = await runCli(process.argv, {\n stdout: process.stdout,\n stderr: process.stderr,\n});\n\nprocess.exitCode = exitCode;\n"],"mappings":"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","names":["CommanderError","readFileSync","base","page","mkdirSync","writeFileSync","join","mkdirSync","join","writeFileSync","mkdirSync","join","join","push","createHash","mkdirSync","join","shortHash","mkdirSync","writeFileSync","join","readFileSync","mkdirSync","join","writeFileSync","addReferrer","runWithConcurrency","exitCode","readFileSync","CommanderError"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@riverbankcms/qa",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Website QA CLI (crawl, SEO, screenshots, reports).",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"qa": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup",
|
|
15
|
+
"dev": "tsx src/cli.ts",
|
|
16
|
+
"lint": "eslint .",
|
|
17
|
+
"format": "prettier -w .",
|
|
18
|
+
"verify": "npm run lint && npm test && npm run typecheck && npm run build",
|
|
19
|
+
"prepack": "npm run build",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
23
|
+
"playwright:install": "playwright install"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/wubss/qa.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/wubss/qa/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/wubss/qa#readme",
|
|
36
|
+
"license": "UNLICENSED",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"cheerio": "^1.0.0",
|
|
42
|
+
"commander": "^14.0.0",
|
|
43
|
+
"playwright": "^1.50.1",
|
|
44
|
+
"undici": "^7.5.0",
|
|
45
|
+
"yaml": "^2.8.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@eslint/js": "^9.17.0",
|
|
49
|
+
"@types/node": "^22.10.5",
|
|
50
|
+
"eslint": "^9.17.0",
|
|
51
|
+
"prettier": "^3.4.2",
|
|
52
|
+
"tsup": "^8.3.5",
|
|
53
|
+
"tsx": "^4.19.2",
|
|
54
|
+
"typescript": "^5.7.2",
|
|
55
|
+
"typescript-eslint": "^8.19.0",
|
|
56
|
+
"vitest": "^2.1.8"
|
|
57
|
+
}
|
|
58
|
+
}
|