playwright-archaeologist 0.1.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/collectors/page-scanner.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\n\n/**\n * PageScanner collector — extracts structural information from a page.\n *\n * Receives a Playwright Page that has already navigated to the target URL.\n * Does NOT navigate, click, or scroll — it only reads the current DOM state.\n *\n * Extracts:\n * - URL, canonical URL, title\n * - Meta tags (name, property, content)\n * - Heading hierarchy (h1-h6)\n * - Landmark elements (nav, main, aside, footer, header, [role])\n * - Links (<a href>) with internal/external classification\n * - Interactive elements (buttons, inputs, selects) with CSS selectors\n * - Navigation Timing data\n * - Content hash for change detection\n * - Hash-routing detection\n */\n\nimport type { Page, Response } from 'playwright';\nimport type {\n PageScanResult,\n HeadingEntry,\n MetaTag,\n LandmarkInfo,\n LinkInfo,\n InteractiveElement,\n PageTiming,\n} from '../types/artifacts.js';\nimport { isSameOrigin, resolveUrl } from '../crawl/url-utils.js';\n\n// ---------------------------------------------------------------------------\n// Types for the raw data returned by page.evaluate\n// ---------------------------------------------------------------------------\n\ninterface RawDomData {\n canonicalHref: string | null;\n title: string;\n metaTags: MetaTag[];\n headings: HeadingEntry[];\n landmarks: LandmarkInfo[];\n links: Array<{ href: string; text: string; rel: string | null }>;\n interactiveElements: Array<{\n tagName: string;\n type: string | null;\n text: string;\n role: string | null;\n ariaLabel: string | null;\n selector: string;\n }>;\n contentHash: string;\n hashIndicators: number;\n}\n\ninterface RawTimingData {\n loadTime: number;\n domContentLoaded: number;\n firstContentfulPaint: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Scan a page and extract its structural information.\n *\n * @param page Playwright Page already navigated to the target URL.\n * @param baseUrl The crawl's starting URL, used for same-origin checks.\n * @param response The navigation response (for HTTP status code).\n * Pass null/undefined if not available; statusCode defaults to 200.\n */\nexport async function scanPage(\n page: Page,\n baseUrl: string,\n response?: Response | null,\n): Promise<PageScanResult> {\n const url = page.url();\n const statusCode = response?.status() ?? 200;\n\n // Run DOM extraction and timing extraction in parallel.\n const [domData, timingData] = await Promise.all([\n extractDomData(page),\n extractTiming(page),\n ]);\n\n // Post-process links: resolve relative URLs and classify internal/external.\n const links = processLinks(domData.links, url, baseUrl);\n\n // Post-process interactive elements: strip nulls into undefined.\n const interactiveElements = processInteractiveElements(domData.interactiveElements);\n\n // Determine hash-routing presence from link hrefs.\n const hashRoutingDetected = domData.hashIndicators >= 3;\n\n return {\n url,\n canonicalUrl: domData.canonicalHref ?? undefined,\n statusCode,\n title: domData.title,\n metaTags: domData.metaTags,\n headings: domData.headings,\n landmarks: domData.landmarks,\n links,\n interactiveElements,\n timing: timingData,\n contentHash: domData.contentHash,\n hashRoutingDetected,\n };\n}\n\n// ---------------------------------------------------------------------------\n// DOM extraction (single page.evaluate call)\n// ---------------------------------------------------------------------------\n\nasync function extractDomData(page: Page): Promise<RawDomData> {\n try {\n return await page.evaluate((): RawDomData => {\n // ------ Canonical ------\n const canonicalEl = document.querySelector<HTMLLinkElement>('link[rel=\"canonical\"]');\n const canonicalHref = canonicalEl?.href ?? null;\n\n // ------ Title ------\n const title = document.title ?? '';\n\n // ------ Meta tags ------\n const metaEls = document.querySelectorAll<HTMLMetaElement>('meta[name], meta[property], meta[content]');\n const metaTags: MetaTag[] = [];\n metaEls.forEach((el) => {\n const content = el.getAttribute('content');\n if (!content) return;\n const entry: MetaTag = { content };\n const name = el.getAttribute('name');\n const property = el.getAttribute('property');\n if (name) entry.name = name;\n if (property) entry.property = property;\n // Only include if at least one of name/property is present\n if (name || property) {\n metaTags.push(entry);\n }\n });\n\n // ------ Headings ------\n const headingEls = document.querySelectorAll<HTMLHeadingElement>('h1, h2, h3, h4, h5, h6');\n const headings: HeadingEntry[] = [];\n headingEls.forEach((el) => {\n const text = (el.textContent ?? '').trim();\n if (!text) return;\n const level = parseInt(el.tagName[1], 10) as 1 | 2 | 3 | 4 | 5 | 6;\n headings.push({ level, text });\n });\n\n // ------ Landmarks ------\n const landmarkSelectors = 'nav, main, aside, footer, header, [role]';\n const landmarkEls = document.querySelectorAll(landmarkSelectors);\n const landmarks: LandmarkInfo[] = [];\n const seenLandmarks = new Set<string>();\n\n landmarkEls.forEach((el) => {\n const tagName = el.tagName.toLowerCase();\n const explicitRole = el.getAttribute('role');\n\n // Determine the effective role\n let role: string;\n if (explicitRole) {\n role = explicitRole;\n } else {\n // Map tag names to implicit ARIA roles\n const implicitRoles: Record<string, string> = {\n nav: 'navigation',\n main: 'main',\n aside: 'complementary',\n footer: 'contentinfo',\n header: 'banner',\n };\n role = implicitRoles[tagName] ?? tagName;\n }\n\n const label =\n el.getAttribute('aria-label') ??\n el.getAttribute('aria-labelledby') ??\n undefined;\n\n // Dedup by role+tagName+label\n const key = `${role}|${tagName}|${label ?? ''}`;\n if (seenLandmarks.has(key)) return;\n seenLandmarks.add(key);\n\n const entry: LandmarkInfo = { role, tagName };\n if (label) entry.label = label;\n landmarks.push(entry);\n });\n\n // ------ Links ------\n const linkEls = document.querySelectorAll<HTMLAnchorElement>('a[href]');\n const links: Array<{ href: string; text: string; rel: string | null }> = [];\n const seenHrefs = new Set<string>();\n\n linkEls.forEach((el) => {\n const href = el.getAttribute('href');\n if (!href || href === '#' || href.startsWith('javascript:')) return;\n\n // Use the resolved href from the anchor element\n const resolvedHref = el.href;\n if (seenHrefs.has(resolvedHref)) return;\n seenHrefs.add(resolvedHref);\n\n const text = (el.textContent ?? '').trim();\n const rel = el.getAttribute('rel');\n links.push({ href: resolvedHref, text, rel });\n });\n\n // ------ Interactive elements (not links) ------\n const interactiveSelectors =\n 'button, input, select, textarea, [role=\"button\"], [role=\"tab\"], [role=\"combobox\"], [role=\"listbox\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"switch\"]';\n const interactiveEls = document.querySelectorAll(interactiveSelectors);\n const interactiveElements: RawDomData['interactiveElements'] = [];\n\n interactiveEls.forEach((el, index) => {\n const tagName = el.tagName.toLowerCase();\n const type = el.getAttribute('type');\n const role = el.getAttribute('role');\n const ariaLabel = el.getAttribute('aria-label');\n\n // Skip hidden inputs — they are not user-interactive\n if (tagName === 'input' && type === 'hidden') return;\n\n // Extract visible text\n let text = '';\n if (tagName === 'input' || tagName === 'textarea') {\n text =\n (el as HTMLInputElement).placeholder ??\n (el as HTMLInputElement).value ??\n '';\n } else {\n text = (el.textContent ?? '').trim();\n }\n\n // Generate a CSS selector\n let selector: string;\n const id = el.getAttribute('id');\n if (id) {\n selector = `${tagName}#${CSS.escape(id)}`;\n } else {\n const name = el.getAttribute('name');\n if (name) {\n selector = `${tagName}[name=\"${CSS.escape(name)}\"]`;\n } else {\n // Fall back to nth-of-type within parent\n const parent = el.parentElement;\n if (parent) {\n const siblings = Array.from(parent.querySelectorAll(`:scope > ${tagName}`));\n const nth = siblings.indexOf(el) + 1;\n // Try to produce a more specific selector using parent's id\n const parentId = parent.getAttribute('id');\n if (parentId) {\n selector = `#${CSS.escape(parentId)} > ${tagName}:nth-of-type(${nth})`;\n } else {\n selector = `${tagName}:nth-of-type(${nth})`;\n }\n } else {\n selector = `${tagName}[data-arch-index=\"${index}\"]`;\n }\n }\n }\n\n interactiveElements.push({\n tagName,\n type,\n text: text.slice(0, 200), // cap text length\n role,\n ariaLabel,\n selector,\n });\n });\n\n // ------ Content hash (SHA-256 via SubtleCrypto) ------\n // SubtleCrypto is async, so we compute a simple djb2 hash synchronously\n // for deterministic, fast change detection.\n const bodyText = (document.body?.innerText ?? '').trim();\n let hash = 5381;\n for (let i = 0; i < bodyText.length; i++) {\n hash = ((hash << 5) + hash + bodyText.charCodeAt(i)) | 0;\n }\n const contentHash = (hash >>> 0).toString(16).padStart(8, '0');\n\n // ------ Hash routing indicators ------\n let hashIndicators = 0;\n linkEls.forEach((el) => {\n const h = el.getAttribute('href');\n if (h && (h.startsWith('#/') || h.startsWith('#!/'))) {\n hashIndicators++;\n }\n });\n\n return {\n canonicalHref,\n title,\n metaTags,\n headings,\n landmarks,\n links,\n interactiveElements,\n contentHash,\n hashIndicators,\n };\n });\n } catch {\n // If DOM extraction fails entirely, return safe defaults\n return {\n canonicalHref: null,\n title: '',\n metaTags: [],\n headings: [],\n landmarks: [],\n links: [],\n interactiveElements: [],\n contentHash: '00000000',\n hashIndicators: 0,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Timing extraction\n// ---------------------------------------------------------------------------\n\nasync function extractTiming(page: Page): Promise<PageTiming> {\n try {\n const raw = await page.evaluate((): RawTimingData => {\n const entries = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[];\n if (entries.length === 0) {\n return { loadTime: 0, domContentLoaded: 0, firstContentfulPaint: null };\n }\n\n const nav = entries[0];\n const loadTime = nav.loadEventEnd > 0\n ? Math.round(nav.loadEventEnd - nav.startTime)\n : 0;\n const domContentLoaded = nav.domContentLoadedEventEnd > 0\n ? Math.round(nav.domContentLoadedEventEnd - nav.startTime)\n : 0;\n\n // First Contentful Paint from paint entries\n let firstContentfulPaint: number | null = null;\n const paintEntries = performance.getEntriesByType('paint');\n for (const entry of paintEntries) {\n if (entry.name === 'first-contentful-paint') {\n firstContentfulPaint = Math.round(entry.startTime);\n break;\n }\n }\n\n return { loadTime, domContentLoaded, firstContentfulPaint };\n });\n\n const timing: PageTiming = {\n loadTime: raw.loadTime,\n domContentLoaded: raw.domContentLoaded,\n };\n if (raw.firstContentfulPaint != null) {\n timing.firstContentfulPaint = raw.firstContentfulPaint;\n }\n return timing;\n } catch {\n return { loadTime: 0, domContentLoaded: 0 };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Post-processing helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve relative hrefs against the page URL and classify as internal/external.\n */\nfunction processLinks(\n rawLinks: Array<{ href: string; text: string; rel: string | null }>,\n pageUrl: string,\n baseUrl: string,\n): LinkInfo[] {\n const results: LinkInfo[] = [];\n\n for (const raw of rawLinks) {\n const resolved = resolveUrl(raw.href, pageUrl);\n const isExternal = !isSameOrigin(resolved, baseUrl);\n\n const link: LinkInfo = {\n href: resolved,\n text: raw.text,\n isExternal,\n };\n if (raw.rel) {\n link.rel = raw.rel;\n }\n results.push(link);\n }\n\n return results;\n}\n\n/**\n * Convert nullable fields to undefined for the InteractiveElement interface.\n */\nfunction processInteractiveElements(\n rawElements: RawDomData['interactiveElements'],\n): InteractiveElement[] {\n return rawElements.map((raw) => {\n const el: InteractiveElement = {\n tagName: raw.tagName,\n text: raw.text,\n selector: raw.selector,\n };\n if (raw.type) el.type = raw.type;\n if (raw.role) el.role = raw.role;\n if (raw.ariaLabel) el.ariaLabel = raw.ariaLabel;\n return el;\n });\n}\n"],"mappings":"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","names":[]}