@whenessel/seql-js 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/seql-js.d.ts +2 -0
- package/dist/seql-js.js +731 -613
- package/dist/seql-js.js.map +1 -1
- package/dist/seql-js.umd.cjs +1 -1
- package/dist/seql-js.umd.cjs.map +1 -1
- package/package.json +2 -1
package/dist/seql-js.umd.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seql-js.umd.cjs","sources":["../src/utils/constants.ts","../src/utils/id-validator.ts","../src/generator/anchor-finder.ts","../src/utils/class-classifier.ts","../src/utils/class-filter.ts","../src/generator/path-builder.ts","../src/utils/text-normalizer.ts","../src/utils/attribute-cleaner.ts","../src/generator/semantic-extractor.ts","../src/generator/svg-fingerprinter.ts","../src/utils/scorer.ts","../src/utils/eid-cache.ts","../src/generator/generator.ts","../src/resolver/css-generator.ts","../src/resolver/semantics-matcher.ts","../src/resolver/constraints-evaluator.ts","../src/resolver/fallback-handler.ts","../src/resolver/resolver.ts","../src/utils/validator.ts","../src/utils/seql-parser.ts","../src/utils/batch-generator.ts"],"sourcesContent":["import type { EIDVersion, GeneratorOptions, ResolverOptions } from '../types';\n\n/**\n * EID specification version\n */\nexport const EID_VERSION: EIDVersion = '1.0';\n\n/**\n * Maximum path depth for traversal\n * Following SPECIFICATION.md §8\n */\nexport const MAX_PATH_DEPTH = 10;\n\n/**\n * Confidence calculation weights\n * Following SPECIFICATION.md §13\n */\nexport const CONFIDENCE_WEIGHTS = {\n ANCHOR: 0.4,\n PATH: 0.3,\n TARGET: 0.2,\n UNIQUENESS: 0.1,\n} as const;\n\n/**\n * Anchor scoring weights\n * Following SPECIFICATION.md §7\n */\nexport const ANCHOR_SCORE = {\n SEMANTIC_TAG: 0.5,\n ROLE: 0.3,\n ARIA_LABEL: 0.1,\n STABLE_ID: 0.1,\n TEST_MARKER: 0.05,\n DEPTH_PENALTY_THRESHOLD: 5,\n DEPTH_PENALTY_FACTOR: 0.05,\n DEGRADED_SCORE: 0.3,\n} as const;\n\n/**\n * Path building constants\n */\nexport const PATH_SCORE = {\n MIN_CONFIDENCE_FOR_SKIP: 0.7,\n} as const;\n\n/**\n * Tier A semantic anchor tags - highest priority\n * Following SPECIFICATION.md §7\n */\nexport const SEMANTIC_ANCHOR_TAGS = [\n 'form',\n 'main',\n 'nav',\n 'section',\n 'article',\n 'footer',\n 'header',\n];\n\n/**\n * Tier B role values for anchor detection\n * Following SPECIFICATION.md §7\n */\nexport const ROLE_ANCHOR_VALUES = [\n 'form',\n 'navigation',\n 'main',\n 'region',\n 'contentinfo',\n 'complementary',\n 'banner',\n 'search',\n];\n\n/**\n * All semantic tags to include in path\n * Following SPECIFICATION.md §8\n */\nexport const SEMANTIC_TAGS = [\n // HTML5 Semantic\n 'article',\n 'aside',\n 'details',\n 'figcaption',\n 'figure',\n 'footer',\n 'header',\n 'main',\n 'mark',\n 'nav',\n 'section',\n 'summary',\n 'time',\n // Form elements\n 'button',\n 'datalist',\n 'fieldset',\n 'form',\n 'input',\n 'label',\n 'legend',\n 'meter',\n 'optgroup',\n 'option',\n 'output',\n 'progress',\n 'select',\n 'textarea',\n // Interactive\n 'a',\n 'audio',\n 'video',\n 'canvas',\n 'dialog',\n 'menu',\n // Text content\n 'blockquote',\n 'dd',\n 'dl',\n 'dt',\n 'hr',\n 'li',\n 'ol',\n 'ul',\n 'p',\n 'pre',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n // Table\n 'caption',\n 'col',\n 'colgroup',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'tr',\n // SVG\n 'svg',\n 'path',\n 'circle',\n 'rect',\n 'line',\n 'polyline',\n 'polygon',\n 'ellipse',\n 'g',\n 'text',\n 'use',\n];\n\n/**\n * SVG child elements that are direct children of svg element\n * These require child combinator for accurate selection\n */\nexport const SVG_CHILD_ELEMENTS = [\n 'rect',\n 'path',\n 'circle',\n 'line',\n 'polyline',\n 'polygon',\n 'ellipse',\n 'g',\n 'text',\n 'use',\n 'defs',\n 'clipPath',\n 'mask',\n] as const;\n\n/**\n * Semantic attributes to extract\n */\nexport const SEMANTIC_ATTRIBUTES = [\n 'aria-label',\n 'aria-labelledby',\n 'aria-describedby',\n 'name',\n 'type',\n 'data-testid',\n 'data-qa',\n 'data-test',\n 'href',\n 'title',\n 'placeholder',\n 'alt',\n];\n\n/**\n * Attribute priorities for CSS selector generation (higher = more priority)\n * Following the priority order for selector building\n */\nexport const ATTRIBUTE_PRIORITY: Record<string, number> = {\n // Test attributes (highest priority)\n 'data-testid': 100,\n 'data-qa': 99,\n 'data-cy': 98,\n 'data-test': 97,\n 'data-test-id': 96,\n\n // ARIA (accessibility semantics)\n 'aria-label': 90,\n 'aria-labelledby': 85,\n 'aria-describedby': 80,\n\n // Semantic HTML attributes\n 'name': 75,\n 'href': 70, // for <a>\n 'src': 70, // for <img>, <script>, etc.\n 'type': 65,\n 'role': 60,\n 'alt': 55,\n 'title': 50,\n 'for': 45,\n 'placeholder': 40,\n\n // Any data-* attribute (if not above)\n 'data-*': 30,\n\n // Any aria-* attribute (if not above)\n 'aria-*': 25,\n};\n\n/**\n * Attributes to ignore in selector generation\n */\nexport const IGNORED_ATTRIBUTES = new Set([\n 'id', // handled separately\n 'class', // handled separately\n 'style', // unstable\n 'xmlns', // service attribute for SVG\n 'tabindex', // can change\n 'contenteditable',\n]);\n\n/**\n * Default generator options\n * Note: cache is optional and not included in defaults\n */\nexport const DEFAULT_GENERATOR_OPTIONS: Omit<Required<GeneratorOptions>, 'cache'> & Pick<GeneratorOptions, 'cache'> = {\n maxPathDepth: MAX_PATH_DEPTH,\n enableSvgFingerprint: true,\n confidenceThreshold: 0.1,\n fallbackToBody: true,\n includeUtilityClasses: false,\n source: 'dom-dsl',\n};\n\n/**\n * Default resolver options\n */\nexport const DEFAULT_RESOLVER_OPTIONS: Required<ResolverOptions> = {\n strictMode: false,\n enableFallback: true,\n maxCandidates: 20,\n};\n","/**\n * Checks if an ID appears to be dynamically generated\n * Dynamic IDs should not be used in DSL as they change between sessions\n *\n * @param id - ID string to check\n * @returns True if ID appears to be dynamic/generated\n */\nexport function isDynamicId(id: string): boolean {\n // Pattern: prefix-number (e.g., \"input-123\", \"react-select-2\")\n if (/^[a-z]+-\\d+$/i.test(id)) return true;\n\n // Pattern: multi-word-prefix-number (e.g., \"react-day-picker-1\", \"mui-date-input-42\")\n if (/^[a-z]+(-[a-z]+)+-\\d+$/i.test(id)) return true;\n\n // Pattern: prefix_number (underscore variant, e.g., \"radix_dialog_1\")\n if (/^[a-z]+(_[a-z]+)*_\\d+$/i.test(id)) return true;\n\n // Pattern: just numbers (e.g., \"123\")\n if (/^\\d+$/.test(id)) return true;\n\n // Pattern: React-style IDs (e.g., \":r0:\", \":R1:\")\n if (/^:[a-z0-9]+:$/i.test(id)) return true;\n\n // Pattern: UUID-like (e.g., \"550e8400-e29b-41d4-a716-446655440000\")\n if (\n /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(id)\n ) {\n return true;\n }\n\n // Pattern: hash-like (long random strings with mixed case/numbers)\n // e.g., \"css-1a2b3c4d\", \"sc-bdVaJa\"\n if (\n /^[a-z]{1,3}[A-Za-z0-9]{8,}$/.test(id) &&\n (/\\d/.test(id) || /[A-Z]/.test(id))\n ) {\n return true;\n }\n\n // Pattern: Radix UI style (e.g., \"radix-:r0:\")\n if (/^radix-/.test(id)) return true;\n\n // Pattern: MUI style (e.g., \"mui-123456\")\n if (/^mui-\\d+$/.test(id)) return true;\n\n return false;\n}\n\n/**\n * Attributes whose values are references to IDs of other elements.\n * If the value contains a dynamic ID, the attribute should be ignored.\n */\nexport const ID_REFERENCE_ATTRIBUTES = new Set([\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-controls',\n 'aria-owns',\n 'aria-activedescendant',\n 'for',\n 'form',\n 'list',\n 'headers',\n 'aria-details',\n 'aria-errormessage',\n 'aria-flowto',\n]);\n\n/**\n * Checks if an attribute value contains a dynamic ID reference.\n * Used for filtering attributes like aria-labelledby.\n * @param value - Attribute value (may contain space-separated IDs)\n * @returns True if value contains at least one dynamic ID\n */\nexport function hasDynamicIdReference(value: string): boolean {\n // Value may contain a list of IDs separated by spaces\n const ids = value.trim().split(/\\s+/);\n return ids.some(id => isDynamicId(id));\n}\n\n/**\n * Validates if an ID is stable and suitable for DSL\n * @param id - ID string to validate\n * @returns True if ID is stable and can be used in DSL\n */\nexport function isStableId(id: string | null | undefined): boolean {\n if (!id) return false;\n return !isDynamicId(id);\n}\n","import type { GeneratorOptions } from '../types';\nimport {\n SEMANTIC_ANCHOR_TAGS,\n ROLE_ANCHOR_VALUES,\n MAX_PATH_DEPTH,\n ANCHOR_SCORE,\n} from '../utils/constants';\nimport { isDynamicId } from '../utils/id-validator';\nimport type { EIDCache } from '../utils/eid-cache';\n\n/**\n * Result of anchor finding\n */\nexport interface AnchorResult {\n element: Element;\n score: number;\n tier: 'A' | 'B' | 'C';\n depth: number;\n}\n\n/**\n * Finds semantic anchor for element identification\n * Traverses up from target to find semantic root\n * Following SPECIFICATION.md §7\n */\nexport class AnchorFinder {\n private maxDepth: number;\n private cache?: EIDCache;\n\n constructor(options: GeneratorOptions, cache?: EIDCache) {\n this.maxDepth = options.maxPathDepth ?? MAX_PATH_DEPTH;\n this.cache = cache;\n }\n\n /**\n * Finds the best anchor element for the target\n * @param target - Target element to find anchor for\n * @returns Anchor result or null if not found\n */\n findAnchor(target: Element): AnchorResult | null {\n // Check cache first\n if (this.cache) {\n const cached = this.cache.getAnchor(target);\n if (cached !== undefined) {\n return cached;\n }\n }\n\n let current: Element | null = target.parentElement;\n let depth = 0;\n let bestCandidate: AnchorResult | null = null;\n\n while (current && depth < this.maxDepth) {\n // Check for body - stop here if reached (SPECIFICATION.md §7)\n if (current.tagName.toLowerCase() === 'body') {\n // Return best candidate if found, otherwise return body as degraded anchor\n if (bestCandidate) {\n return bestCandidate;\n }\n return {\n element: current,\n score: ANCHOR_SCORE.DEGRADED_SCORE,\n tier: 'C',\n depth,\n };\n }\n\n const rawScore = this.scoreAnchor(current);\n\n if (rawScore > 0) {\n // Apply depth penalty (SPECIFICATION.md §7)\n const score = this.applyDepthPenalty(rawScore, depth);\n const tier = this.getTier(current);\n const candidate: AnchorResult = { element: current, score, tier, depth };\n\n // Tier A is always best, stop immediately\n if (tier === 'A') {\n return candidate;\n }\n\n // Keep best candidate\n if (!bestCandidate || score > bestCandidate.score) {\n bestCandidate = candidate;\n }\n }\n\n current = current.parentElement;\n depth++;\n }\n\n const result = bestCandidate;\n\n // Cache the result\n if (this.cache) {\n this.cache.setAnchor(target, result);\n }\n\n return result;\n }\n\n /**\n * Scores an element as anchor candidate (without depth penalty)\n * @param element - Element to score\n * @returns Raw score from 0 to 1\n */\n scoreAnchor(element: Element): number {\n let score = 0;\n const tag = element.tagName.toLowerCase();\n\n // Tier A: Native semantic tags (highest weight)\n if (SEMANTIC_ANCHOR_TAGS.includes(tag)) {\n score += ANCHOR_SCORE.SEMANTIC_TAG;\n }\n\n // Tier B: Role-based semantics\n const role = element.getAttribute('role');\n if (role && ROLE_ANCHOR_VALUES.includes(role)) {\n score += ANCHOR_SCORE.ROLE;\n }\n\n // ARIA attributes bonus\n if (\n element.hasAttribute('aria-label') ||\n element.hasAttribute('aria-labelledby')\n ) {\n score += ANCHOR_SCORE.ARIA_LABEL;\n }\n\n // Stable ID bonus\n const id = element.id;\n if (id && !isDynamicId(id)) {\n score += ANCHOR_SCORE.STABLE_ID;\n }\n\n // Tier C: Test markers (lower score)\n if (\n element.hasAttribute('data-testid') ||\n element.hasAttribute('data-qa') ||\n element.hasAttribute('data-test')\n ) {\n score += ANCHOR_SCORE.TEST_MARKER;\n }\n\n return Math.min(score, 1.0);\n }\n\n /**\n * Applies depth penalty to score\n * Following SPECIFICATION.md §7: depthPenalty = (depth - threshold) * factor\n */\n private applyDepthPenalty(score: number, depth: number): number {\n if (depth <= ANCHOR_SCORE.DEPTH_PENALTY_THRESHOLD) {\n return score;\n }\n\n const penalty =\n (depth - ANCHOR_SCORE.DEPTH_PENALTY_THRESHOLD) *\n ANCHOR_SCORE.DEPTH_PENALTY_FACTOR;\n\n return Math.max(0, score - penalty);\n }\n\n /**\n * Determines the tier of an anchor element\n */\n private getTier(element: Element): 'A' | 'B' | 'C' {\n const tag = element.tagName.toLowerCase();\n\n if (SEMANTIC_ANCHOR_TAGS.includes(tag)) {\n return 'A';\n }\n\n const role = element.getAttribute('role');\n if (role && ROLE_ANCHOR_VALUES.includes(role)) {\n return 'B';\n }\n\n return 'C';\n }\n}\n","/**\n * Class classification for CSS selector generation\n * Categorizes classes into dynamic, utility, and semantic classes\n */\n\n/**\n * Classification result for a CSS class\n */\nexport interface ClassClassification {\n /** Whether class is dynamically generated (CSS-in-JS, hashes) */\n isDynamic: boolean;\n /** Whether class is a utility class (Tailwind, Bootstrap, animations) */\n isUtility: boolean;\n /** Whether class has semantic meaning */\n isSemantic: boolean;\n /** Whether class is stable (not dynamic and not utility) */\n isStable: boolean;\n}\n\n/**\n * Patterns for detecting dynamically generated classes\n * These classes should be IGNORED in selectors\n */\nconst DYNAMIC_CLASS_PATTERNS = [\n // CSS-in-JS\n /^css-[a-z0-9]+$/i,\n /^sc-[a-z0-9]+-\\d+$/i,\n /^[a-z]{5,8}$/i, // Short generated classes (abcdef)\n\n // Material-UI / MUI\n /^Mui[A-Z]\\w+-\\w+-\\w+/,\n /^makeStyles-\\w+-\\d+$/,\n\n // JSS\n /^jss\\d+$/,\n\n // Emotion / Linaria\n /^(emotion|linaria)-[a-z0-9]+/i,\n\n // Component libraries with hashes\n /^(chakra|tw-|ant-)[a-z0-9]+-\\w+/i,\n\n // Hash-based (hashes in classes)\n /-[a-f0-9]{6,}$/i,\n /^_[a-z0-9]{5,}$/i,\n /\\d{5,}/, // 5+ digits in a row\n];\n\n/**\n * Patterns for detecting utility classes\n * These classes should be IGNORED in selectors\n */\nconst UTILITY_CLASS_PATTERNS = [\n // === FIX 4: Tailwind arbitrary values and variants (highest priority) ===\n /^\\[/, // Any arbitrary value or variant starting with [ (e.g., [&_svg]:..., [mask-type:luminance])\n\n // === FIX 4: Pseudo-class variants (must be before specific patterns) ===\n /^(first|last|odd|even|only|first-of-type|last-of-type|only-of-type):/, // first:, last:, etc.\n /^(hover|focus|active|disabled|enabled|checked|indeterminate|default|required|valid|invalid|in-range|out-of-range|placeholder-shown|autofill|read-only):/, // State pseudo-classes\n /^(focus-within|focus-visible|visited|target|open):/, // Advanced pseudo-classes\n\n // === FIX 4: Responsive variants (must be before specific patterns) ===\n /^(sm|md|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl):/,\n\n // === FIX 4: Dark mode and directional variants ===\n /^dark:/,\n /^(rtl|ltr):/,\n\n // === FIX 4: Group and peer variants ===\n /^(group|peer)(-hover|-focus|-active)?:/,\n\n // === FIX 4: Tailwind utilities with fraction values ===\n /\\/([\\d.]+|full|auto|screen)$/, // /50, /100, /full, /auto, /screen\n\n // === FIX 4: Positioning utilities ===\n /^(inset|top|right|bottom|left)(-|$)/, // inset-0, top-0, left-0\n\n // === Layout & Display ===\n /^(flex|inline-flex|grid|block|inline|inline-block|hidden|visible)$/,\n /^(absolute|relative|fixed|sticky|static)$/,\n\n // === Flexbox & Grid ===\n /^(items|justify|content|self|place)-/,\n /^flex-(row|col|wrap|nowrap|1|auto|initial|none)/,\n /^grid-(cols|rows|flow)/,\n\n // === Spacing (Tailwind) ===\n /^(gap|space)-/,\n /^[mp][trblxy]?-(\\d+|auto|px)$/,\n\n // === Sizing ===\n /^(w|h|min-w|min-h|max-w|max-h|size)-/,\n\n // === Colors & Styling ===\n // Note: text-* can be semantic (text-muted, text-primary) or utility (text-center, text-lg)\n // More specific patterns for utility text-* classes\n /^text-(center|left|right|justify|start|end|xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/,\n /^text-(uppercase|lowercase|capitalize|normal-case|underline|line-through|no-underline)$/,\n /^text-(truncate|ellipsis|clip)$/,\n /^(bg|border|ring|shadow|outline)-/,\n /^rounded(-|$)/,\n\n // === Typography ===\n /^(font|leading|tracking|whitespace|break|truncate)-/,\n /^(uppercase|lowercase|capitalize|normal-case)$/,\n\n // === Transform & Animation (IMPORTANT!) ===\n /^(transform|transition|duration|delay|ease|animate)-/,\n /^(scale|rotate|translate|skew)-/,\n /^transform$/,\n /^backdrop-blur-/,\n /^motion-/, // Framer Motion\n /^(fade|slide|zoom|bounce|pulse|spin|ping)-/, // animations\n\n // === Overflow & Scrolling ===\n /^(overflow|overscroll|scroll)-/,\n\n // === Interactivity ===\n /^(cursor|pointer-events|select|resize)-/,\n\n // === Visibility & Opacity ===\n /^(opacity|z)-/,\n /^(visible|invisible|collapse)$/,\n\n // === Bootstrap utilities ===\n /^d-(none|inline|inline-block|block|grid|table|flex)$/,\n /^(float|clearfix|text)-(left|right|center|justify|start|end)$/,\n /^(m|p)[trblxy]?-[0-5]$/,\n /^(w|h)-(25|50|75|100|auto)$/,\n // Note: btn-* classes are semantic (component classes), not utility\n // /^btn-(primary|secondary|success|danger|warning|info|light|dark|link)$/,\n /^btn-(sm|lg|block)$/, // Only size modifiers are utility\n /^text-(muted|primary|success|danger|warning|info|light|dark|white)$/,\n /^bg-(primary|secondary|success|danger|warning|info|light|dark|white|transparent)$/,\n /^border(-top|-bottom|-left|-right)?(-0)?$/,\n /^rounded(-top|-bottom|-left|-right|-circle|-pill|-0)?$/,\n /^shadow(-sm|-lg|-none)?$/,\n /^(align|justify|order|flex)-(start|end|center|between|around|fill|grow|shrink)$/,\n /^col(-sm|-md|-lg|-xl)?(-\\d+|-auto)?$/,\n /^row(-cols)?(-\\d+)?$/,\n /^g[xy]?-[0-5]$/,\n /^(show|hide|invisible|visible)$/,\n /^(position|top|bottom|start|end)-(static|relative|absolute|fixed|sticky|-\\d+)$/,\n\n // === Common utility patterns ===\n /^(row|col)$/,\n /^clearfix$/,\n /^pull-(left|right)$/,\n /^float-(left|right|none)$/,\n];\n\n/**\n * Patterns for detecting semantic classes\n * These classes should be USED in selectors\n */\nconst SEMANTIC_CLASS_PATTERNS = [\n // === Navigation ===\n /^(nav|menu|header|footer|sidebar|topbar|navbar|breadcrumb)/,\n /(navigation|dropdown|megamenu)$/,\n\n // === Components ===\n /^(btn|button|link|card|modal|dialog|popup|tooltip|alert|badge|chip)/,\n /^(form|input|select|checkbox|radio|textarea|label|fieldset)/,\n /^(table|list|item|row|cell|column)/,\n /^(accordion|tab|carousel|slider|gallery)/,\n\n // === Content ===\n /^(content|main|article|post|comment|title|subtitle|description|caption)/,\n /^(hero|banner|jumbotron|section|wrapper|box)/,\n\n // === User/Data ===\n /^(user|profile|avatar|account|auth)/,\n /^(product|item|price|cart|checkout|order)/,\n\n // === Layout sections ===\n /^(page|layout|panel|widget|block)/,\n\n // === States (semantic naming) ===\n /-(primary|secondary|tertiary|success|error|warning|info|danger)$/,\n /-(active|inactive|disabled|enabled|selected|highlighted|focused)$/,\n /-(open|closed|expanded|collapsed|visible|hidden)$/,\n /-(large|medium|small|tiny|xs|sm|md|lg|xl)$/,\n\n // === Action buttons ===\n /^(submit|cancel|close|delete|edit|save|back|next|prev|search)/,\n\n // === Status ===\n /^(loading|pending|complete|failed|draft|published)/,\n];\n\n/**\n * Classifies a CSS class into categories\n * @param className - Class name to classify\n * @returns Classification result\n */\nexport function classifyClass(className: string): ClassClassification {\n const isDynamic = isDynamicClass(className);\n const isUtility = isUtilityClass(className);\n const isSemantic = isSemanticClass(className);\n const isStable = !isDynamic && !isUtility;\n\n return {\n isDynamic,\n isUtility,\n isSemantic,\n isStable,\n };\n}\n\n/**\n * Checks if class is dynamically generated (CSS-in-JS, hashes)\n * @param className - Class name to check\n * @returns True if dynamic class\n */\nexport function isDynamicClass(className: string): boolean {\n return DYNAMIC_CLASS_PATTERNS.some((pattern) => pattern.test(className));\n}\n\n/**\n * Checks if class is a utility class (Tailwind, Bootstrap, animations)\n * @param className - Class name to check\n * @returns True if utility class\n */\nexport function isUtilityClass(className: string): boolean {\n // Very short classes are often utility\n if (className.length <= 2) return true;\n\n // Classes that are just numbers or start with numbers\n if (/^\\d/.test(className)) return true;\n\n // Check utility patterns\n return UTILITY_CLASS_PATTERNS.some((pattern) => pattern.test(className));\n}\n\n/**\n * Checks if class has semantic meaning\n * @param className - Class name to check\n * @returns True if semantic class\n */\nexport function isSemanticClass(className: string): boolean {\n // Must not be dynamic or utility\n if (isDynamicClass(className) || isUtilityClass(className)) {\n return false;\n }\n\n // Check semantic patterns\n return SEMANTIC_CLASS_PATTERNS.some((pattern) => pattern.test(className));\n}\n\n/**\n * Checks if class is stable (not dynamic and not utility)\n * @param className - Class name to check\n * @returns True if stable class\n */\nexport function isStableClass(className: string): boolean {\n return !isDynamicClass(className) && !isUtilityClass(className);\n}\n\n/**\n * Filters classes to keep only stable ones (not dynamic and not utility)\n * @param classes - Array of class names\n * @returns Array of stable class names\n */\nexport function filterStableClasses(classes: string[]): string[] {\n return classes.filter((cls) => isStableClass(cls));\n}\n\n/**\n * Filters classes to keep only semantic ones\n * @param classes - Array of class names\n * @returns Array of semantic class names\n */\nexport function filterSemanticClasses(classes: string[]): string[] {\n return classes.filter((cls) => isSemanticClass(cls));\n}\n\n/**\n * Scores a class for semantic value (0-1)\n * Higher score = more semantic value\n * @param className - Class name to score\n * @returns Score from 0 to 1\n */\nexport function scoreClass(className: string): number {\n // Dynamic or utility classes get 0\n if (isDynamicClass(className) || isUtilityClass(className)) {\n return 0;\n }\n\n let score = 0.5; // Base score for stable classes\n\n // Semantic classes get higher score\n if (isSemanticClass(className)) {\n score = 0.8;\n }\n\n // Short classes are less semantic\n if (className.length < 3) {\n score *= 0.3;\n } else if (className.length < 5) {\n score *= 0.6;\n }\n\n // Numeric parts reduce score (but not to 0 if already stable)\n if (/\\d/.test(className)) {\n score *= 0.7;\n }\n\n return Math.min(score, 1.0);\n}\n","import {\n isUtilityClass as isUtilityClassFromClassifier,\n isDynamicClass,\n scoreClass as scoreClassFromClassifier,\n} from './class-classifier';\n\n/**\n * Filters classes into semantic and utility\n * Uses class-classifier for improved detection\n * @param classes - Array of class names\n * @returns Object with semantic and utility arrays\n */\nexport function filterClasses(classes: string[]): {\n semantic: string[];\n utility: string[];\n} {\n const semantic: string[] = [];\n const utility: string[] = [];\n\n for (const cls of classes) {\n // Use new classifier logic\n if (isUtilityClassFromClassifier(cls) || isDynamicClass(cls)) {\n utility.push(cls);\n } else {\n semantic.push(cls);\n }\n }\n\n return { semantic, utility };\n}\n\n/**\n * Checks if class is a utility/atomic CSS class\n * Uses class-classifier for improved detection\n * @param className - Class name to check\n * @returns True if utility class\n */\nexport function isUtilityClass(className: string): boolean {\n // Use new classifier logic\n return isUtilityClassFromClassifier(className) || isDynamicClass(className);\n}\n\n/**\n * Scores a class for semantic value\n * Uses class-classifier for improved scoring\n * @param className - Class name to score\n * @returns Score from 0 to 1\n */\nexport function getClassScore(className: string): number {\n // Use new classifier scoring\n return scoreClassFromClassifier(className);\n}\n","import type { PathNode, GeneratorOptions } from '../types';\nimport type { SemanticExtractor } from './semantic-extractor';\nimport { SEMANTIC_TAGS, MAX_PATH_DEPTH, PATH_SCORE } from '../utils/constants';\nimport { isUtilityClass } from '../utils/class-filter';\nimport { isDynamicId } from '../utils/id-validator';\nimport type { EIDCache } from '../utils/eid-cache';\n\n// Fallback CSS escape function if not available\nconst cssEscape = (str: string) => {\n // Simplified CSS escape for special characters\n return str.replace(/([#:.[\\]@])/g, '\\\\$1');\n};\n\n/**\n * Result of path building including degradation info\n */\nexport interface PathBuildResult {\n path: PathNode[];\n degraded: boolean;\n degradationReason?: string;\n}\n\n/**\n * Builds semantic path from anchor to target\n * Following SPECIFICATION.md §8\n */\nexport class PathBuilder {\n private maxDepth: number;\n private cache?: EIDCache;\n\n constructor(options: GeneratorOptions, cache?: EIDCache) {\n this.maxDepth = options.maxPathDepth ?? MAX_PATH_DEPTH;\n this.cache = cache;\n }\n\n /**\n * Builds path from anchor to target (excluding both)\n * @param anchor - Anchor element (start)\n * @param target - Target element (end)\n * @param extractor - Semantic extractor instance\n * @returns Path build result with nodes and degradation info\n */\n buildPath(\n anchor: Element,\n target: Element,\n extractor: SemanticExtractor,\n ): PathBuildResult {\n const rawPath: Element[] = [];\n let current: Element | null = target.parentElement;\n\n // Traverse up from target to anchor\n while (current && current !== anchor && rawPath.length < this.maxDepth) {\n rawPath.unshift(current); // Add to beginning (we're going backwards)\n current = current.parentElement;\n }\n\n // Check for depth overflow (SPECIFICATION.md §8)\n const depthOverflow = rawPath.length >= this.maxDepth && current !== anchor;\n\n // Filter noise elements, keeping semantic ones\n let filteredPath = this.filterNoise(rawPath);\n\n // Check uniqueness and add disambiguation nodes if needed (SPECIFICATION.md §8)\n filteredPath = this.ensureUniqueness(\n rawPath,\n filteredPath,\n anchor,\n target,\n extractor,\n );\n\n // Convert to PathNodes\n const pathNodes = filteredPath.map((el) => {\n // Calculate nth-child position (1-based index)\n const parent = el.parentElement;\n let nthChild: number | undefined;\n\n if (parent) {\n const siblings = Array.from(parent.children);\n const index = siblings.indexOf(el);\n if (index !== -1) {\n nthChild = index + 1; // 1-based for CSS nth-child()\n }\n }\n\n return {\n tag: el.tagName.toLowerCase(),\n semantics: extractor.extract(el),\n score: extractor.scoreElement(el),\n nthChild,\n };\n });\n\n return {\n path: pathNodes,\n degraded: depthOverflow,\n degradationReason: depthOverflow ? 'path-depth-overflow' : undefined,\n };\n }\n\n /**\n * Legacy method for backward compatibility\n */\n buildPathNodes(\n anchor: Element,\n target: Element,\n extractor: SemanticExtractor,\n ): PathNode[] {\n return this.buildPath(anchor, target, extractor).path;\n }\n\n /**\n * Ensures path uniqueness by adding nodes if needed\n * Following SPECIFICATION.md §8 Disambiguation Algorithm\n */\n private ensureUniqueness(\n rawPath: Element[],\n filteredPath: Element[],\n anchor: Element,\n target: Element,\n extractor: SemanticExtractor,\n ): Element[] {\n // Build selector from current path and check uniqueness\n const selector = this.buildTestSelector(anchor, filteredPath, target);\n\n try {\n const doc = target.ownerDocument;\n if (!doc) return filteredPath;\n\n // Check cache first\n let matches: NodeListOf<Element> | Element[];\n if (this.cache) {\n const cached = this.cache.getSelectorResults(selector);\n if (cached !== undefined) {\n matches = cached;\n } else {\n matches = Array.from(doc.querySelectorAll(selector));\n this.cache.setSelectorResults(selector, matches);\n }\n } else {\n matches = doc.querySelectorAll(selector);\n }\n\n // If unique, return as-is\n if (matches.length <= 1) {\n return filteredPath;\n }\n\n // Not unique - try adding skipped nodes one by one\n const skippedNodes = rawPath.filter((el) => !filteredPath.includes(el));\n\n for (const node of skippedNodes) {\n // Only add if it has good semantic score\n const score = extractor.scoreElement(node);\n if (score < PATH_SCORE.MIN_CONFIDENCE_FOR_SKIP) {\n continue; // Skip low-value nodes\n }\n\n // Try adding this node\n const testPath = this.insertNodeInOrder(filteredPath, node, rawPath);\n const testSelector = this.buildTestSelector(anchor, testPath, target);\n\n try {\n // Check cache for test selector\n let testMatches: NodeListOf<Element> | Element[];\n if (this.cache) {\n const cached = this.cache.getSelectorResults(testSelector);\n if (cached !== undefined) {\n testMatches = cached;\n } else {\n testMatches = Array.from(doc.querySelectorAll(testSelector));\n this.cache.setSelectorResults(testSelector, testMatches);\n }\n } else {\n testMatches = doc.querySelectorAll(testSelector);\n }\n if (testMatches.length === 1) {\n return testPath; // Found unique path\n }\n if (testMatches.length < matches.length) {\n // Improved, continue with this path\n filteredPath = testPath;\n }\n } catch {\n // Invalid selector, skip this node\n }\n }\n\n return filteredPath;\n } catch {\n // querySelectorAll failed, return original\n return filteredPath;\n }\n }\n\n /**\n * Inserts node into path maintaining original order\n */\n private insertNodeInOrder(\n path: Element[],\n node: Element,\n rawPath: Element[],\n ): Element[] {\n const nodeIndex = rawPath.indexOf(node);\n const result = [...path];\n\n // Find insertion point\n let insertIndex = 0;\n for (let i = 0; i < result.length; i++) {\n const pathNodeIndex = rawPath.indexOf(result[i]);\n if (pathNodeIndex > nodeIndex) {\n break;\n }\n insertIndex = i + 1;\n }\n\n result.splice(insertIndex, 0, node);\n return result;\n }\n\n /**\n * Builds a test CSS selector from path\n */\n private buildTestSelector(\n anchor: Element,\n path: Element[],\n target: Element,\n ): string {\n const parts: string[] = [];\n\n // Anchor\n parts.push(this.elementToSelector(anchor));\n\n // Path\n for (const el of path) {\n parts.push(this.elementToSelector(el));\n }\n\n // Target\n parts.push(this.elementToSelector(target));\n\n return parts.join(' ');\n }\n\n /**\n * Converts element to basic CSS selector\n */\n private elementToSelector(element: Element): string {\n let selector = element.tagName.toLowerCase();\n\n if (element.id && !isDynamicId(element.id)) {\n selector += `#${cssEscape(element.id)}`;\n }\n\n for (const cls of Array.from(element.classList)) {\n if (!isUtilityClass(cls)) {\n selector += `.${cssEscape(cls)}`;\n }\n }\n\n return selector;\n }\n\n /**\n * Filters out noise/layout elements\n */\n private filterNoise(elements: Element[]): Element[] {\n return elements.filter((el) => this.shouldInclude(el));\n }\n\n /**\n * Determines if element should be included in path\n */\n shouldInclude(element: Element): boolean {\n const tag = element.tagName.toLowerCase();\n\n // Always include semantic HTML tags\n if (SEMANTIC_TAGS.includes(tag)) {\n return true;\n }\n\n // div/span need additional semantic features to be included\n if (tag === 'div' || tag === 'span') {\n return this.hasSemanticFeatures(element);\n }\n\n return false;\n }\n\n /**\n * Checks if element has meaningful semantic features\n */\n private hasSemanticFeatures(element: Element): boolean {\n // Has role attribute\n if (element.hasAttribute('role')) return true;\n\n // Has ARIA attributes\n for (const attr of Array.from(element.attributes)) {\n if (attr.name.startsWith('aria-')) return true;\n }\n\n // Has semantic classes (not just utility)\n if (element.classList.length > 0) {\n for (const cls of Array.from(element.classList)) {\n if (!isUtilityClass(cls)) return true;\n }\n }\n\n // Has data-testid or similar\n if (\n element.hasAttribute('data-testid') ||\n element.hasAttribute('data-qa') ||\n element.hasAttribute('data-test')\n ) {\n return true;\n }\n\n // Has stable ID\n const id = element.id;\n if (id && !isDynamicId(id)) {\n return true;\n }\n\n return false;\n }\n}\n","/**\n * Normalizes text for comparison\n * Following SPECIFICATION.md §11\n *\n * - Trims whitespace\n * - Collapses multiple spaces\n * - Replaces newlines/tabs with spaces\n */\nexport function normalizeText(text: string | null | undefined): string {\n if (!text) return '';\n\n return text.trim().replace(/[\\n\\t\\r]/g, ' ').replace(/\\s+/g, ' ');\n}\n","/**\n * Attribute value cleaning for stable CSS selector generation\n * Cleans href/src attributes by removing dynamic query parameters and hashes\n */\n\n/**\n * Options for cleaning attribute values\n */\nexport interface CleanAttributeOptions {\n /** Preserve query parameters for absolute URLs */\n preserveQueryForAbsolute?: boolean; // default: true\n /** Remove dynamic hashes */\n removeDynamicHashes?: boolean; // default: true\n}\n\n/**\n * Default options for cleaning\n */\nconst DEFAULT_OPTIONS: Required<CleanAttributeOptions> = {\n preserveQueryForAbsolute: true,\n removeDynamicHashes: true,\n};\n\n/**\n * Checks if a hash value is dynamic (should be removed)\n * @param hash - Hash string to check (without #)\n * @returns True if hash is dynamic\n */\nfunction isDynamicHash(hash: string): boolean {\n if (!hash) return false;\n\n const dynamicPatterns = [\n /\\d{5,}/, // 5+ digits\n /[a-f0-9]{8,}/i, // hex hash 8+ characters\n /(session|token|temp|random|timestamp|nonce|cache)/i, // dynamic words\n /^\\d+$/, // only digits\n /^[a-f0-9-]{32,}$/i, // UUID-like\n ];\n\n return dynamicPatterns.some((p) => p.test(hash));\n}\n\n/**\n * Cleans URL value (href/src) by removing dynamic parts\n * @param value - URL value to clean\n * @param options - Cleaning options\n * @returns Cleaned URL value\n */\nfunction cleanUrlValue(\n value: string,\n options: Required<CleanAttributeOptions>,\n): string {\n if (!value) return value;\n\n const isAbsolute = value.startsWith('http://') || value.startsWith('https://');\n\n // Split into parts: base, query, hash\n let [baseWithQuery, hash] = value.split('#');\n const [base, query] = baseWithQuery.split('?');\n\n let cleaned = base;\n\n // Handle query parameters\n if (isAbsolute) {\n // For absolute URLs, preserve query if preserveQueryForAbsolute is true\n if (options.preserveQueryForAbsolute && query) {\n cleaned += `?${query}`;\n }\n }\n // For relative URLs, query is always removed (already handled by base)\n\n // Handle hash\n if (hash) {\n if (options.removeDynamicHashes && isDynamicHash(hash)) {\n // Remove dynamic hash\n } else {\n // Preserve non-dynamic hash\n cleaned += `#${hash}`;\n }\n }\n\n return cleaned;\n}\n\n/**\n * Cleans attribute value based on attribute name\n * Currently handles href and src attributes\n * @param attrName - Attribute name\n * @param value - Attribute value\n * @param options - Cleaning options\n * @returns Cleaned attribute value\n */\nexport function cleanAttributeValue(\n attrName: string,\n value: string,\n options: CleanAttributeOptions = {},\n): string {\n if (!value) return value;\n\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Only clean href and src attributes\n if (attrName === 'href' || attrName === 'src') {\n return cleanUrlValue(value, opts);\n }\n\n // For other attributes, return as-is\n return value;\n}\n","import type { ElementSemantics, TextContent, GeneratorOptions } from '../types';\nimport { normalizeText } from '../utils/text-normalizer';\nimport { filterClasses } from '../utils/class-filter';\nimport { isDynamicId, ID_REFERENCE_ATTRIBUTES, hasDynamicIdReference } from '../utils/id-validator';\nimport { ATTRIBUTE_PRIORITY, IGNORED_ATTRIBUTES } from '../utils/constants';\nimport { cleanAttributeValue } from '../utils/attribute-cleaner';\nimport type { EIDCache } from '../utils/eid-cache';\n\n/**\n * Extracts semantic features from DOM elements\n * Following SPECIFICATION.md §10\n */\nexport class SemanticExtractor {\n private includeUtilityClasses: boolean;\n private cache?: EIDCache;\n\n constructor(options: GeneratorOptions, cache?: EIDCache) {\n this.includeUtilityClasses = options.includeUtilityClasses ?? false;\n this.cache = cache;\n }\n\n /**\n * Extracts semantic features from element\n * @param element - DOM element to extract from\n * @returns Semantic features object\n */\n extract(element: Element): ElementSemantics {\n // Check cache first\n if (this.cache) {\n const cached = this.cache.getSemantics(element);\n if (cached !== undefined) {\n return cached;\n }\n }\n\n const semantics: ElementSemantics = {};\n\n // ID (if stable)\n const id = element.id;\n if (id && !isDynamicId(id)) {\n semantics.id = id;\n }\n\n // Classes (filtered)\n if (element.classList.length > 0) {\n const classes = Array.from(element.classList);\n if (this.includeUtilityClasses) {\n semantics.classes = classes;\n } else {\n const { semantic } = filterClasses(classes);\n if (semantic.length > 0) {\n semantics.classes = semantic;\n }\n }\n }\n\n // Semantic attributes\n const attrs = this.extractAttributes(element);\n if (Object.keys(attrs).length > 0) {\n semantics.attributes = attrs;\n }\n\n // Role\n const role = element.getAttribute('role');\n if (role) {\n semantics.role = role;\n }\n\n // Text content (for certain elements)\n if (this.shouldExtractText(element)) {\n const text = this.extractText(element);\n if (text) {\n semantics.text = text;\n }\n }\n\n // Cache the result\n if (this.cache) {\n this.cache.setSemantics(element, semantics);\n }\n\n return semantics;\n }\n\n /**\n * Scores element based on semantic richness\n * @param element - Element to score\n * @returns Score from 0 to 1\n */\n scoreElement(element: Element): number {\n let score = 0.5; // Base score\n\n const semantics = this.extract(element);\n\n if (semantics.id) score += 0.15;\n if (semantics.classes && semantics.classes.length > 0) score += 0.1;\n if (semantics.attributes && Object.keys(semantics.attributes).length > 0) {\n score += 0.1;\n }\n if (semantics.role) score += 0.1;\n if (semantics.text) score += 0.05;\n\n return Math.min(score, 1.0);\n }\n\n /**\n * Checks if attribute should be ignored\n * @param attrName - Attribute name\n * @returns True if should be ignored\n */\n private shouldIgnoreAttribute(attrName: string): boolean {\n // Ignored attributes\n if (IGNORED_ATTRIBUTES.has(attrName)) return true;\n\n // Inline event handlers\n if (attrName.startsWith('on')) return true;\n\n // Angular/React service attributes\n if (attrName.startsWith('ng-') || attrName.startsWith('_ng')) return true;\n if (attrName.startsWith('data-reactid') || attrName.startsWith('data-react'))\n return true;\n if (attrName.startsWith('data-v-')) return true; // Vue scoped styles\n\n return false;\n }\n\n /**\n * Gets attribute priority\n * @param attrName - Attribute name\n * @returns Priority number (higher = more priority)\n */\n private getAttributePriority(attrName: string): number {\n // Exact match\n if (ATTRIBUTE_PRIORITY[attrName] !== undefined) {\n return ATTRIBUTE_PRIORITY[attrName];\n }\n\n // data-* wildcard\n if (attrName.startsWith('data-')) {\n return ATTRIBUTE_PRIORITY['data-*'];\n }\n\n // aria-* wildcard\n if (attrName.startsWith('aria-')) {\n return ATTRIBUTE_PRIORITY['aria-*'];\n }\n\n return 0; // Don't use in selector\n }\n\n /**\n * Checks if attribute value is dynamic (should be ignored)\n * @param value - Attribute value\n * @returns True if value is dynamic\n */\n private isDynamicValue(value: string): boolean {\n const dynamicPatterns = [\n /^[a-f0-9]{32,}$/i, // Long hashes\n /^\\d{10,}$/, // Timestamp\n /^(undefined|null|\\[object)/, // JS artifacts\n /^{{.*}}$/, // Template literals\n ];\n\n return dynamicPatterns.some((p) => p.test(value));\n }\n\n /**\n * Extracts relevant semantic attributes from element\n * Iterates through all attributes and filters by priority\n */\n private extractAttributes(element: Element): Record<string, string> {\n const attrs: Record<string, string> = {};\n\n // Iterate through all attributes (not just SEMANTIC_ATTRIBUTES)\n for (const attr of Array.from(element.attributes)) {\n const name = attr.name;\n\n // Skip ignored attributes\n if (this.shouldIgnoreAttribute(name)) continue;\n\n // Skip ID-reference attributes with dynamic IDs\n if (ID_REFERENCE_ATTRIBUTES.has(name) && hasDynamicIdReference(attr.value)) {\n continue;\n }\n\n // Get priority\n const priority = this.getAttributePriority(name);\n if (priority === 0) continue;\n\n // Clean value for href/src\n const value =\n name === 'href' || name === 'src'\n ? cleanAttributeValue(name, attr.value)\n : attr.value;\n\n // Skip empty values\n if (!value || value.trim() === '') continue;\n\n // Skip dynamic values\n if (this.isDynamicValue(value)) continue;\n\n attrs[name] = value;\n }\n\n return attrs;\n }\n\n /**\n * Extracts and normalizes text content\n */\n private extractText(element: Element): TextContent | null {\n // Get direct text content, not from children\n const rawText = this.getDirectTextContent(element);\n if (!rawText) return null;\n\n const normalized = normalizeText(rawText);\n if (!normalized) return null;\n\n // Limit text length for performance\n const maxLength = 100;\n const truncatedRaw =\n rawText.length > maxLength ? rawText.slice(0, maxLength) + '...' : rawText;\n const truncatedNorm =\n normalized.length > maxLength\n ? normalized.slice(0, maxLength) + '...'\n : normalized;\n\n return {\n raw: truncatedRaw,\n normalized: truncatedNorm,\n };\n }\n\n /**\n * Gets direct text content excluding child elements\n */\n private getDirectTextContent(element: Element): string | null {\n const texts: string[] = [];\n\n for (const node of Array.from(element.childNodes)) {\n if (node.nodeType === Node.TEXT_NODE && node.textContent) {\n const trimmed = node.textContent.trim();\n if (trimmed) {\n texts.push(trimmed);\n }\n }\n }\n\n // Return direct text if found, otherwise fallback to textContent (with null safety)\n return texts.length > 0 ? texts.join(' ') : (element.textContent ?? null);\n }\n\n /**\n * Determines if text should be extracted for this element\n */\n private shouldExtractText(element: Element): boolean {\n const tag = element.tagName.toLowerCase();\n // Extract text for interactive and semantic text elements\n return [\n 'button',\n 'a',\n 'label',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'p',\n 'span',\n 'li',\n 'th',\n 'td',\n 'dt',\n 'dd',\n 'legend',\n 'figcaption',\n 'summary',\n ].includes(tag);\n }\n\n}\n","import type { SvgFingerprint } from '../types';\n\n/**\n * Generates stable fingerprints for SVG elements\n * Following SPECIFICATION.md §9\n */\nexport class SvgFingerprinter {\n /**\n * Generates fingerprint for SVG element\n * @param element - SVG element to fingerprint\n * @returns SVG fingerprint object\n */\n fingerprint(element: SVGElement): SvgFingerprint {\n const tag = element.tagName.toLowerCase();\n const shape = this.getShape(tag);\n\n const fingerprint: SvgFingerprint = {\n shape,\n hasAnimation: this.hasAnimation(element),\n };\n\n // Path-specific hash\n if (shape === 'path') {\n const d = element.getAttribute('d');\n if (d) {\n fingerprint.dHash = this.computePathHash(d);\n }\n } else if (['circle', 'rect', 'ellipse', 'line'].includes(shape)) {\n fingerprint.geomHash = this.computeGeomHash(element, shape);\n }\n\n // ARIA role\n const role = element.getAttribute('role');\n if (role) {\n fingerprint.role = role;\n }\n\n // Title text (accessibility)\n const title = element.querySelector('title');\n if (title?.textContent) {\n fingerprint.titleText = title.textContent.trim();\n }\n\n return fingerprint;\n }\n\n /**\n * Computes hash from path data (first N commands)\n * @param d - SVG path d attribute value\n * @returns Hash string\n */\n computePathHash(d: string): string {\n const normalized = this.normalizePathData(d);\n return this.simpleHash(normalized);\n }\n\n /**\n * Gets the shape type from tag name\n */\n private getShape(tag: string): SvgFingerprint['shape'] {\n const shapes: SvgFingerprint['shape'][] = [\n 'path',\n 'circle',\n 'rect',\n 'line',\n 'polyline',\n 'polygon',\n 'ellipse',\n 'g',\n 'text',\n 'use',\n 'svg',\n ];\n return shapes.find((s) => s === tag) ?? 'path';\n }\n\n /**\n * Normalizes path data for consistent hashing\n */\n private normalizePathData(d: string): string {\n // Split into commands, take first 5 for fingerprint\n const commands = d.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/gi) ?? [];\n const firstN = commands.slice(0, 5);\n\n // Normalize: trim, round numbers to 1 decimal\n return firstN\n .map((cmd) => {\n return cmd.trim().replace(/(-?\\d+\\.?\\d*)/g, (match) => {\n return parseFloat(match).toFixed(1);\n });\n })\n .join(' ');\n }\n\n /**\n * Computes geometry hash for non-path shapes\n */\n private computeGeomHash(element: SVGElement, shape: string): string {\n const attrs: string[] = [];\n\n switch (shape) {\n case 'circle':\n // Use radius ratio for scale independence\n attrs.push(`r=${element.getAttribute('r') ?? '0'}`);\n break;\n\n case 'rect':\n // Use aspect ratio for scale independence\n {\n const w = parseFloat(element.getAttribute('width') ?? '0');\n const h = parseFloat(element.getAttribute('height') ?? '0');\n if (w > 0 && h > 0) {\n attrs.push(`ratio=${(w / h).toFixed(2)}`);\n }\n }\n break;\n\n case 'ellipse':\n // Use radii ratio\n {\n const rx = parseFloat(element.getAttribute('rx') ?? '0');\n const ry = parseFloat(element.getAttribute('ry') ?? '0');\n if (rx > 0 && ry > 0) {\n attrs.push(`ratio=${(rx / ry).toFixed(2)}`);\n }\n }\n break;\n\n case 'line':\n // Use angle/direction\n {\n const x1 = parseFloat(element.getAttribute('x1') ?? '0');\n const y1 = parseFloat(element.getAttribute('y1') ?? '0');\n const x2 = parseFloat(element.getAttribute('x2') ?? '0');\n const y2 = parseFloat(element.getAttribute('y2') ?? '0');\n const angle = Math.atan2(y2 - y1, x2 - x1);\n attrs.push(`angle=${angle.toFixed(2)}`);\n }\n break;\n }\n\n return this.simpleHash(attrs.join(';'));\n }\n\n /**\n * Detects animations on SVG element\n */\n hasAnimation(element: SVGElement): boolean {\n // Check for SMIL animation elements\n if (element.querySelector('animate, animateTransform, animateMotion')) {\n return true;\n }\n\n // Check for CSS animations\n const doc = element.ownerDocument;\n if (doc?.defaultView) {\n try {\n const style = doc.defaultView.getComputedStyle(element);\n if (\n style.animationName !== 'none' ||\n (style.transitionProperty !== 'all' &&\n style.transitionProperty !== 'none')\n ) {\n return true;\n }\n } catch {\n // getComputedStyle may fail in some contexts\n }\n }\n\n return false;\n }\n\n /**\n * Simple hash function for fingerprinting (not cryptographic)\n */\n private simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n }\n}\n","import type { ElementIdentity } from '../types';\nimport { CONFIDENCE_WEIGHTS } from './constants';\n\n/**\n * Calculates overall confidence score for Element Identity\n * Following SPECIFICATION.md §13 confidence formula\n *\n * @param eid - The Element Identity to score\n * @param uniquenessBonus - Bonus for unique resolution (0-1), determined during resolution\n * @returns Confidence score from 0 to 1\n */\nexport function calculateConfidence(\n eid: ElementIdentity,\n uniquenessBonus: number = 0,\n): number {\n const anchorScore = eid.anchor.score;\n\n const avgPathScore =\n eid.path.length > 0\n ? eid.path.reduce((sum, n) => sum + n.score, 0) / eid.path.length\n : 0.5;\n\n const targetScore = eid.target.score;\n\n // Weighted sum formula using constants\n const confidence =\n anchorScore * CONFIDENCE_WEIGHTS.ANCHOR +\n avgPathScore * CONFIDENCE_WEIGHTS.PATH +\n targetScore * CONFIDENCE_WEIGHTS.TARGET +\n uniquenessBonus * CONFIDENCE_WEIGHTS.UNIQUENESS;\n\n // Degradation penalty\n const degradationPenalty = eid.anchor.degraded ? 0.2 : 0;\n\n return Math.max(0, Math.min(1, confidence - degradationPenalty));\n}\n\n/**\n * Calculates score for a single element based on its semantic features\n * @param semanticsCount - Number of semantic features present\n * @param hasId - Whether element has stable ID\n * @param hasRole - Whether element has ARIA role\n * @returns Score from 0 to 1\n */\nexport function calculateElementScore(\n semanticsCount: number,\n hasId: boolean,\n hasRole: boolean,\n): number {\n let score = 0.5; // Base score\n\n if (hasId) score += 0.2;\n if (hasRole) score += 0.15;\n\n // Each semantic feature adds to score\n score += Math.min(semanticsCount * 0.05, 0.15);\n\n return Math.min(score, 1.0);\n}\n","import type { ElementIdentity, ElementSemantics } from '../types';\nimport type { AnchorResult } from '../generator/anchor-finder';\n\n/**\n * LRU Cache implementation for selector results\n */\nclass LRUCache<K, V> {\n private cache: Map<K, V>;\n private maxSize: number;\n\n constructor(maxSize: number) {\n this.cache = new Map();\n this.maxSize = maxSize;\n }\n\n get(key: K): V | undefined {\n if (!this.cache.has(key)) return undefined;\n // Move to end (most recently used)\n const value = this.cache.get(key)!;\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n // Remove oldest (first) entry\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n delete(key: K): void {\n this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n/**\n * Cache statistics\n */\nexport interface CacheStats {\n /** Total number of EID cache hits */\n eidHits: number;\n /** Total number of EID cache misses */\n eidMisses: number;\n /** Total number of selector cache hits */\n selectorHits: number;\n /** Total number of selector cache misses */\n selectorMisses: number;\n /** Total number of anchor cache hits */\n anchorHits: number;\n /** Total number of anchor cache misses */\n anchorMisses: number;\n /** Total number of semantics cache hits */\n semanticsHits: number;\n /** Total number of semantics cache misses */\n semanticsMisses: number;\n /** Current selector cache size */\n selectorCacheSize: number;\n /** Maximum selector cache size */\n maxSelectorCacheSize: number;\n}\n\n/**\n * Options for creating an EID cache\n */\nexport interface EIDCacheOptions {\n /** Maximum size for selector cache (default: 1000) */\n maxSelectorCacheSize?: number;\n}\n\n/**\n * EID Cache for multi-level caching\n * Provides caching for:\n * - Complete EID identities (WeakMap)\n * - Selector query results (LRU Map)\n * - Anchor finder results (WeakMap)\n * - Semantic extraction results (WeakMap)\n */\nexport class EIDCache {\n private eidCache: WeakMap<Element, ElementIdentity>;\n private selectorResultCache: LRUCache<string, Element[]>;\n private anchorCache: WeakMap<Element, AnchorResult | null>;\n private semanticsCache: WeakMap<Element, ElementSemantics>;\n private stats: CacheStats;\n\n constructor(options: EIDCacheOptions = {}) {\n this.eidCache = new WeakMap();\n this.selectorResultCache = new LRUCache<string, Element[]>(\n options.maxSelectorCacheSize ?? 1000,\n );\n this.anchorCache = new WeakMap();\n this.semanticsCache = new WeakMap();\n this.stats = {\n eidHits: 0,\n eidMisses: 0,\n selectorHits: 0,\n selectorMisses: 0,\n anchorHits: 0,\n anchorMisses: 0,\n semanticsHits: 0,\n semanticsMisses: 0,\n selectorCacheSize: 0,\n maxSelectorCacheSize: options.maxSelectorCacheSize ?? 1000,\n };\n }\n\n /**\n * Get cached EID for element\n */\n getEID(element: Element): ElementIdentity | undefined {\n const cached = this.eidCache.get(element);\n if (cached !== undefined) {\n this.stats.eidHits++;\n return cached;\n }\n this.stats.eidMisses++;\n return undefined;\n }\n\n /**\n * Cache EID for element\n */\n setEID(element: Element, eid: ElementIdentity): void {\n this.eidCache.set(element, eid);\n }\n\n /**\n * Get cached selector results\n */\n getSelectorResults(selector: string): Element[] | undefined {\n const cached = this.selectorResultCache.get(selector);\n if (cached !== undefined) {\n this.stats.selectorHits++;\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n return cached;\n }\n this.stats.selectorMisses++;\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n return undefined;\n }\n\n /**\n * Cache selector results\n */\n setSelectorResults(selector: string, results: Element[]): void {\n this.selectorResultCache.set(selector, results);\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n }\n\n /**\n * Get cached anchor result\n */\n getAnchor(element: Element): AnchorResult | null | undefined {\n if (this.anchorCache.has(element)) {\n this.stats.anchorHits++;\n return this.anchorCache.get(element);\n }\n this.stats.anchorMisses++;\n return undefined;\n }\n\n /**\n * Cache anchor result\n */\n setAnchor(element: Element, result: AnchorResult | null): void {\n this.anchorCache.set(element, result);\n }\n\n /**\n * Get cached semantics\n */\n getSemantics(element: Element): ElementSemantics | undefined {\n const cached = this.semanticsCache.get(element);\n if (cached !== undefined) {\n this.stats.semanticsHits++;\n return cached;\n }\n this.stats.semanticsMisses++;\n return undefined;\n }\n\n /**\n * Cache semantics\n */\n setSemantics(element: Element, semantics: ElementSemantics): void {\n this.semanticsCache.set(element, semantics);\n }\n\n /**\n * Clear all caches\n */\n clear(): void {\n this.selectorResultCache.clear();\n this.stats.selectorCacheSize = 0;\n // WeakMaps are automatically cleared when elements are GC'd\n // Reset stats\n this.stats = {\n eidHits: 0,\n eidMisses: 0,\n selectorHits: 0,\n selectorMisses: 0,\n anchorHits: 0,\n anchorMisses: 0,\n semanticsHits: 0,\n semanticsMisses: 0,\n selectorCacheSize: 0,\n maxSelectorCacheSize: this.stats.maxSelectorCacheSize,\n };\n }\n\n /**\n * Invalidate cache for a specific element\n * Note: WeakMaps don't support deletion, but we can clear selector cache\n * if needed. This method is mainly for future extensibility.\n */\n invalidateElement(_element: Element): void {\n // WeakMaps don't support explicit deletion\n // Elements will be automatically removed when GC'd\n // We can only clear selector cache entries that might reference this element\n // For now, this is a no-op, but kept for API consistency\n }\n\n /**\n * Invalidate a specific selector from cache\n */\n invalidateSelector(selector: string): void {\n this.selectorResultCache.delete(selector);\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n }\n\n /**\n * Get cache statistics\n */\n getStats(): CacheStats {\n return {\n ...this.stats,\n selectorCacheSize: this.selectorResultCache.size,\n };\n }\n\n /**\n * Get cache hit rate for EID cache\n */\n getEIDHitRate(): number {\n const total = this.stats.eidHits + this.stats.eidMisses;\n return total > 0 ? this.stats.eidHits / total : 0;\n }\n\n /**\n * Get cache hit rate for selector cache\n */\n getSelectorHitRate(): number {\n const total = this.stats.selectorHits + this.stats.selectorMisses;\n return total > 0 ? this.stats.selectorHits / total : 0;\n }\n\n /**\n * Get cache hit rate for anchor cache\n */\n getAnchorHitRate(): number {\n const total = this.stats.anchorHits + this.stats.anchorMisses;\n return total > 0 ? this.stats.anchorHits / total : 0;\n }\n\n /**\n * Get cache hit rate for semantics cache\n */\n getSemanticsHitRate(): number {\n const total = this.stats.semanticsHits + this.stats.semanticsMisses;\n return total > 0 ? this.stats.semanticsHits / total : 0;\n }\n\n /**\n * Get overall cache hit rate\n */\n getOverallHitRate(): number {\n const totalHits =\n this.stats.eidHits +\n this.stats.selectorHits +\n this.stats.anchorHits +\n this.stats.semanticsHits;\n const totalMisses =\n this.stats.eidMisses +\n this.stats.selectorMisses +\n this.stats.anchorMisses +\n this.stats.semanticsMisses;\n const total = totalHits + totalMisses;\n return total > 0 ? totalHits / total : 0;\n }\n}\n\n/**\n * Create a new EID cache instance\n */\nexport function createEIDCache(options?: EIDCacheOptions): EIDCache {\n return new EIDCache(options);\n}\n\n/**\n * Global default cache instance\n * Used automatically if no cache is provided to generateEID\n */\nlet globalCache: EIDCache | null = null;\n\n/**\n * Get or create the global default cache\n */\nexport function getGlobalCache(): EIDCache {\n if (!globalCache) {\n globalCache = createEIDCache();\n }\n return globalCache;\n}\n\n/**\n * Reset the global cache\n */\nexport function resetGlobalCache(): void {\n globalCache = null;\n}\n","import type {\n ElementIdentity,\n GeneratorOptions,\n Constraint,\n FallbackRules,\n} from '../types';\nimport { AnchorFinder } from './anchor-finder';\nimport { PathBuilder } from './path-builder';\nimport { SemanticExtractor } from './semantic-extractor';\nimport { SvgFingerprinter } from './svg-fingerprinter';\nimport { calculateConfidence } from '../utils/scorer';\nimport { EID_VERSION, DEFAULT_GENERATOR_OPTIONS, ANCHOR_SCORE } from '../utils/constants';\nimport { getGlobalCache } from '../utils/eid-cache';\n\n/**\n * Generates EID (Element Identity) for a DOM element\n * Following SPECIFICATION.md §7-11\n *\n * @param target - Target DOM element\n * @param options - Generation options\n * @returns Element identity or null if generation failed\n */\nexport function generateEID(\n target: Element,\n options: GeneratorOptions = {},\n): ElementIdentity | null {\n // Input validation\n if (!target || !target.ownerDocument) {\n return null;\n }\n\n // Ensure target is part of a document\n if (!target.isConnected) {\n return null;\n }\n\n const opts = { ...DEFAULT_GENERATOR_OPTIONS, ...options };\n\n // Get cache instance (use provided or global default)\n const cache = opts.cache ?? getGlobalCache();\n\n // Check cache for complete EID first\n const cachedEID = cache.getEID(target);\n if (cachedEID !== undefined) {\n return cachedEID;\n }\n\n const anchorFinder = new AnchorFinder(opts, cache);\n const pathBuilder = new PathBuilder(opts, cache);\n const semanticExtractor = new SemanticExtractor(opts, cache);\n const svgFingerprinter = new SvgFingerprinter();\n\n // 1. Find anchor\n const anchorResult = anchorFinder.findAnchor(target);\n if (!anchorResult && !opts.fallbackToBody) {\n return null;\n }\n\n // Use found anchor or fallback to body\n const anchorElement =\n anchorResult?.element ?? target.ownerDocument?.body ?? null;\n if (!anchorElement) return null;\n\n // Determine if anchor is degraded\n const anchorDegraded = !anchorResult || anchorResult.tier === 'C';\n\n // 2. Build anchor node\n const anchorSemantics = semanticExtractor.extract(anchorElement);\n const anchorNode = {\n tag: anchorElement.tagName.toLowerCase(),\n semantics: anchorSemantics,\n score: anchorResult?.score ?? ANCHOR_SCORE.DEGRADED_SCORE,\n degraded: anchorDegraded,\n };\n\n // 3. Build path (now returns PathBuildResult with degradation info)\n const pathResult = pathBuilder.buildPath(\n anchorElement,\n target,\n semanticExtractor,\n );\n\n // 4. Build target node\n const targetSemantics = semanticExtractor.extract(target);\n\n // Add SVG fingerprint if applicable\n if (opts.enableSvgFingerprint && isSvgElement(target)) {\n targetSemantics.svg = svgFingerprinter.fingerprint(target as SVGElement);\n }\n\n // Calculate nth-child position for target (same logic as in PathBuilder)\n const targetParent = target.parentElement;\n let targetNthChild: number | undefined;\n if (targetParent) {\n const siblings = Array.from(targetParent.children);\n const index = siblings.indexOf(target);\n if (index !== -1) {\n targetNthChild = index + 1; // 1-based for CSS nth-child()\n }\n }\n\n const targetNode = {\n tag: target.tagName.toLowerCase(),\n semantics: targetSemantics,\n score: semanticExtractor.scoreElement(target),\n nthChild: targetNthChild,\n };\n\n // 5. Build constraints\n const constraints: Constraint[] = [];\n\n // 6. Build fallback rules\n const fallback: FallbackRules = {\n onMultiple: 'best-score',\n onMissing: 'anchor-only',\n maxDepth: 3,\n };\n\n // Determine overall degradation status\n const isDegraded = anchorNode.degraded || pathResult.degraded;\n const degradationReason = getDegradationReason(anchorNode.degraded, pathResult);\n\n // 7. Build EID\n const eid: ElementIdentity = {\n version: EID_VERSION,\n anchor: anchorNode,\n path: pathResult.path,\n target: targetNode,\n constraints,\n fallback,\n meta: {\n confidence: 0, // Calculated below\n generatedAt: new Date().toISOString(),\n generator: `dom-eid@${EID_VERSION}`,\n source: opts.source,\n degraded: isDegraded,\n degradationReason,\n },\n };\n\n // 8. Calculate confidence\n eid.meta.confidence = calculateConfidence(eid);\n\n // 9. Check threshold\n if (eid.meta.confidence < opts.confidenceThreshold) {\n return null;\n }\n\n // Cache the result\n cache.setEID(target, eid);\n\n return eid;\n}\n\n/**\n * Checks if element is an SVG element\n */\nfunction isSvgElement(el: Element): boolean {\n return (\n el.namespaceURI === 'http://www.w3.org/2000/svg' ||\n el.tagName.toLowerCase() === 'svg' ||\n el instanceof SVGElement\n );\n}\n\n/**\n * Determines the degradation reason based on various factors\n */\nfunction getDegradationReason(\n anchorDegraded: boolean,\n pathResult: { degraded: boolean; degradationReason?: string },\n): string | undefined {\n if (anchorDegraded && pathResult.degraded) {\n return 'anchor-and-path-degraded';\n }\n if (anchorDegraded) {\n return 'no-semantic-anchor';\n }\n if (pathResult.degraded) {\n return pathResult.degradationReason;\n }\n return undefined;\n}\n","import type { ElementIdentity, ElementSemantics, PathNode } from '../types';\nimport { ATTRIBUTE_PRIORITY, IGNORED_ATTRIBUTES, SVG_CHILD_ELEMENTS } from '../utils/constants';\nimport { cleanAttributeValue } from '../utils/attribute-cleaner';\nimport { filterStableClasses } from '../utils/class-classifier';\nimport { isDynamicId, ID_REFERENCE_ATTRIBUTES, hasDynamicIdReference } from '../utils/id-validator';\n\n/**\n * Options for selector building with uniqueness control\n */\nexport interface BuildSelectorOptions {\n /**\n * Enable uniqueness disambiguation for target element.\n * When true and base selector is not unique, adds:\n * 1. Extra stable classes (up to maxClasses)\n * 2. :nth-of-type(N) as last resort\n */\n ensureUnique?: boolean;\n\n /**\n * Maximum number of additional classes to add for disambiguation\n * @default 4\n */\n maxClasses?: number;\n\n /**\n * Root element for uniqueness check (defaults to document)\n */\n root?: Document | Element;\n}\n\n/**\n * Result of selector building with uniqueness info\n */\nexport interface BuildSelectorResult {\n /** Generated CSS selector */\n selector: string;\n /** Whether selector is unique in the given root */\n isUnique: boolean;\n /** Whether nth-of-type was added for disambiguation */\n usedNthOfType: boolean;\n /** Number of extra classes added for disambiguation */\n extraClassesAdded: number;\n}\n\n/**\n * Generates CSS selectors from Element Identity\n * Following SPECIFICATION.md §13.1\n *\n * Priority order (highest to lowest):\n * 1. ID (100)\n * 2. data-testid, data-qa, data-cy (95)\n * 3. aria-label (90)\n * 4. name (85)\n * 5. href, src (80) - semantic for <a>, <img>, etc.\n * 6. role (75)\n * 7. type (70)\n * 8. alt, title, for (60)\n * 9. Stable classes (35)\n * 10. nth-of-type (last resort, added when ensureUnique=true)\n */\nexport class CssGenerator {\n\n /**\n * Builds CSS selector from Element Identity\n * @param eid - Element Identity to convert\n * @param options - Optional uniqueness control settings\n * @returns CSS selector string (simple) or BuildSelectorResult (with options)\n */\n buildSelector(eid: ElementIdentity, options?: BuildSelectorOptions): string;\n buildSelector(\n eid: ElementIdentity,\n options: BuildSelectorOptions & { ensureUnique: true }\n ): BuildSelectorResult;\n buildSelector(\n eid: ElementIdentity,\n options?: BuildSelectorOptions\n ): string | BuildSelectorResult {\n // FIX 1: Check if anchor and target are the same element\n const isAnchorSameAsTarget =\n eid.path.length === 0 &&\n eid.anchor.tag === eid.target.tag &&\n JSON.stringify(eid.anchor.semantics) === JSON.stringify(eid.target.semantics);\n\n if (isAnchorSameAsTarget) {\n // Anchor and target are identical, use only target selector with full semantics\n const selector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: false } // Include classes for same-element case\n );\n\n if (!options?.ensureUnique) {\n return selector;\n }\n\n // Apply uniqueness logic to the single selector\n return this.ensureUniqueSelector(selector, eid, options);\n }\n\n const parts: string[] = [];\n\n // FIX 2: Ensure anchor is unique before building full selector\n const anchorSelector = options?.ensureUnique\n ? this.ensureUniqueAnchor(eid, options.root ?? document)\n : this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n\n parts.push(anchorSelector);\n\n // Path\n for (const node of eid.path) {\n let nodeSelector = this.buildNodeSelector(node.tag, node.semantics);\n\n // Add nth-child if available in EID (for precise positioning)\n if (node.nthChild !== undefined) {\n // For table elements, always use nth-child for correct positioning\n const isTableElement = ['tr', 'td', 'th', 'thead', 'tbody', 'tfoot'].includes(node.tag);\n if (isTableElement) {\n nodeSelector += `:nth-child(${node.nthChild})`;\n } else {\n // For non-table elements, also use nth-child if available for precision\n nodeSelector += `:nth-child(${node.nthChild})`;\n }\n }\n\n parts.push(nodeSelector);\n }\n\n // Target (base selector without disambiguation)\n let targetBaseSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: options?.ensureUnique } // Exclude classes initially if we need unique\n );\n\n // Add nth-child if available in EID (same logic as for path nodes)\n if (eid.target.nthChild !== undefined) {\n const isTableElement = ['tr', 'td', 'th', 'thead', 'tbody', 'tfoot'].includes(eid.target.tag);\n if (isTableElement) {\n targetBaseSelector += `:nth-child(${eid.target.nthChild})`;\n } else {\n // For non-table elements, also use nth-child if available for precision\n targetBaseSelector += `:nth-child(${eid.target.nthChild})`;\n }\n }\n\n parts.push(targetBaseSelector);\n\n // ENHANCED FIX: Use appropriate combinator based on element type and context\n const isSvgChild = this.isSvgChildElement(eid.target.tag);\n const hasSvgInPath = eid.path.some(node => node.tag === 'svg');\n\n let baseSelector: string;\n\n // For SVG children with svg in path: use descendant until svg, then child\n if (isSvgChild && hasSvgInPath) {\n const svgIndexInPath = eid.path.findIndex(node => node.tag === 'svg');\n if (svgIndexInPath !== -1) {\n // parts structure: [anchor, ...path nodes, target]\n // svgIndex in parts is svgIndexInPath + 1 (because anchor is at index 0)\n const svgIndexInParts = svgIndexInPath + 1;\n\n // Everything up to and including svg (use descendant combinator)\n const beforeAndIncludingSvg = parts.slice(0, svgIndexInParts + 1);\n // Everything after svg, excluding target (use child combinator)\n const afterSvgBeforeTarget = parts.slice(svgIndexInParts + 1, -1);\n // The target element\n const target = parts[parts.length - 1];\n\n // Build selector: descendant to svg, then child for everything inside svg\n if (afterSvgBeforeTarget.length > 0) {\n baseSelector = beforeAndIncludingSvg.join(' ') + ' > ' + afterSvgBeforeTarget.join(' > ') + ' > ' + target;\n } else {\n // Direct child of svg\n baseSelector = beforeAndIncludingSvg.join(' ') + ' > ' + target;\n }\n } else {\n baseSelector = parts.join(' ');\n }\n } else {\n // Default: use descendant combinator (space) for flexibility\n // This handles filtered intermediate elements correctly\n baseSelector = parts.join(' ');\n }\n\n // If uniqueness check not requested, return simple selector\n if (!options?.ensureUnique) {\n return baseSelector;\n }\n\n // For ensureUnique: first check if baseSelector is already unique\n const baseSelectorMatches = this.querySelectorSafe(baseSelector, options.root ?? document);\n\n if (baseSelectorMatches.length === 1) {\n // Base selector is already unique, use it\n return {\n selector: baseSelector,\n isUnique: true,\n usedNthOfType: baseSelector.includes(':nth-'),\n extraClassesAdded: 0,\n };\n }\n\n // If base selector finds 0 or multiple elements, try buildFullDomPathSelector\n if (baseSelectorMatches.length === 0 || baseSelectorMatches.length > 1) {\n const fullPathSelector = this.buildFullDomPathSelector(\n eid,\n eid.target.semantics,\n options.root ?? document\n );\n\n if (fullPathSelector && this.isUnique(fullPathSelector, options.root ?? document)) {\n return {\n selector: fullPathSelector,\n isUnique: true,\n usedNthOfType: fullPathSelector.includes(':nth-'),\n extraClassesAdded: 0,\n };\n }\n }\n\n // Fallback: use regular ensureUniqueSelector logic with baseSelector\n return this.ensureUniqueSelector(\n baseSelector,\n eid,\n options\n );\n }\n\n /**\n * Builds selector for anchor only (used in fallback)\n * @param eid - Element Identity\n * @returns CSS selector for anchor\n */\n buildAnchorSelector(eid: ElementIdentity): string {\n return this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n }\n\n /**\n * Ensures selector uniqueness by progressively adding classes and nth-of-type\n */\n private ensureUniqueSelector(\n baseSelector: string,\n eid: ElementIdentity,\n options: BuildSelectorOptions\n ): BuildSelectorResult {\n const root = options.root ?? document;\n const maxClasses = options.maxClasses ?? 4;\n const targetTag = eid.target.tag;\n const targetSemantics = eid.target.semantics;\n\n let currentSelector = baseSelector;\n let extraClassesAdded = 0;\n let usedNthOfType = false;\n\n // Step 0: Check if base selector finds ANY elements\n // If not, the path is incomplete (div elements were filtered out)\n const baseMatches = this.querySelectorSafe(currentSelector, root);\n\n if (baseMatches.length === 0) {\n // Path is broken - try to find actual DOM path from anchor to target\n const fullPathSelector = this.buildFullDomPathSelector(eid, targetSemantics, root);\n if (fullPathSelector) {\n currentSelector = fullPathSelector;\n // Check if full path selector is unique\n if (this.isUnique(currentSelector, root)) {\n return {\n selector: currentSelector,\n isUnique: true,\n usedNthOfType: false,\n extraClassesAdded: 0,\n };\n }\n }\n }\n\n // Check if base selector is already unique\n if (this.isUnique(currentSelector, root)) {\n return {\n selector: currentSelector,\n isUnique: true,\n usedNthOfType: false,\n extraClassesAdded: 0,\n };\n }\n\n // Step 1: Try adding stable classes from target one by one (up to maxClasses)\n const availableTargetClasses = filterStableClasses(targetSemantics.classes ?? []);\n for (let i = 0; i < Math.min(availableTargetClasses.length, maxClasses); i++) {\n const cls = availableTargetClasses[i];\n currentSelector += `.${this.escapeCSS(cls)}`;\n extraClassesAdded++;\n\n if (this.isUnique(currentSelector, root)) {\n return {\n selector: currentSelector,\n isUnique: true,\n usedNthOfType: false,\n extraClassesAdded,\n };\n }\n }\n\n // Step 1.5: If still not unique, try buildFullDomPathSelector\n // This handles cases where multiple sections have same structure (e.g., multiple ul > li > span)\n if (!this.isUnique(currentSelector, root)) {\n const fullPathSelector = this.buildFullDomPathSelector(eid, targetSemantics, root);\n if (fullPathSelector && this.isUnique(fullPathSelector, root)) {\n return {\n selector: fullPathSelector,\n isUnique: true,\n usedNthOfType: fullPathSelector.includes(':nth-of-type('),\n extraClassesAdded,\n };\n }\n }\n\n // Step 2: Last resort - add :nth-of-type or :nth-child (table-aware)\n const targetElement = this.findNthElementByText(currentSelector, targetSemantics, root);\n if (targetElement) {\n currentSelector += this.getNthSelector(targetElement, targetTag);\n usedNthOfType = true; // Note: might be nth-child for table elements\n }\n\n return {\n selector: currentSelector,\n isUnique: this.isUnique(currentSelector, root),\n usedNthOfType,\n extraClassesAdded,\n };\n }\n\n /**\n * Builds full DOM path selector by traversing actual DOM from anchor\n * This handles cases where intermediate div/span elements were filtered out\n */\n private buildFullDomPathSelector(\n eid: ElementIdentity,\n targetSemantics: ElementSemantics,\n root: Document | Element\n ): string | null {\n // First, find the anchor element\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const anchors = this.querySelectorSafe(anchorSelector, root);\n\n if (anchors.length === 0) return null;\n\n // For each anchor, try to find the target within it\n for (const anchor of anchors) {\n // Find target candidates within this anchor\n const targetCandidates = this.findTargetWithinAnchor(\n anchor,\n eid.target.tag,\n targetSemantics\n );\n\n if (targetCandidates.length === 0) continue;\n\n // FIX: Score each candidate by how well its path matches EID path\n // This handles cases where multiple elements have same text (e.g., calendar dates)\n const scoredCandidates = targetCandidates.map(candidate => {\n const score = this.scorePathMatch(candidate, anchor, eid.path);\n return { element: candidate, score };\n });\n\n // Sort by score (highest first)\n scoredCandidates.sort((a, b) => b.score - a.score);\n\n // Try candidates in order of best match\n for (const { element } of scoredCandidates) {\n const pathSelector = this.buildPathFromAnchorToTarget(\n anchor,\n element,\n eid,\n root\n );\n\n if (pathSelector && this.isUnique(pathSelector, root)) {\n return pathSelector;\n }\n }\n }\n\n return null;\n }\n\n /**\n * Scores how well a candidate's DOM path matches the EID path\n * Compares tags, classes, attributes, and nthChild positions\n * @param candidate - Target element candidate\n * @param anchor - Anchor element\n * @param eidPath - EID path nodes\n * @returns Score (higher = better match)\n */\n private scorePathMatch(\n candidate: Element,\n anchor: Element,\n eidPath: PathNode[]\n ): number {\n // Build actual DOM path from anchor to candidate\n const domPath: Element[] = [];\n let el: Element | null = candidate.parentElement;\n\n while (el && el !== anchor) {\n domPath.unshift(el);\n el = el.parentElement;\n }\n\n let score = 0;\n const minLength = Math.min(domPath.length, eidPath.length);\n\n // Compare each level of the path\n for (let i = 0; i < minLength; i++) {\n const domEl = domPath[i];\n const eidNode = eidPath[i];\n\n // Match tag (most important)\n if (domEl.tagName.toLowerCase() === eidNode.tag) {\n score += 10;\n\n // Match nthChild position (very important for tables)\n if (eidNode.nthChild !== undefined) {\n const parent = domEl.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children);\n const actualNthChild = siblings.indexOf(domEl) + 1;\n if (actualNthChild === eidNode.nthChild) {\n score += 20; // High weight for position match\n } else {\n score -= 10; // Penalty for position mismatch\n }\n }\n }\n } else {\n score -= 5; // Penalty for tag mismatch\n }\n\n // Match classes if present in EID\n if (eidNode.semantics.classes && eidNode.semantics.classes.length > 0) {\n const matchingClasses = eidNode.semantics.classes.filter(\n (cls: string) => domEl.classList.contains(cls)\n );\n score += matchingClasses.length * 2;\n }\n\n // Match attributes if present in EID\n if (eidNode.semantics.attributes) {\n const matchingAttrs = Object.entries(eidNode.semantics.attributes).filter(\n ([name, value]) => domEl.getAttribute(name) === value\n );\n score += matchingAttrs.length * 3;\n }\n }\n\n // Penalty if path lengths don't match\n const lengthDiff = Math.abs(domPath.length - eidPath.length);\n score -= lengthDiff * 2;\n\n return score;\n }\n\n /**\n * Finds target elements within an anchor by matching semantics\n */\n private findTargetWithinAnchor(\n anchor: Element,\n targetTag: string,\n targetSemantics: ElementSemantics\n ): Element[] {\n const candidates = Array.from(anchor.querySelectorAll(targetTag));\n\n // Filter by semantics\n return candidates.filter(el => {\n // Match by text if available\n if (targetSemantics.text) {\n const elText = el.textContent?.trim() || '';\n const normalized = targetSemantics.text.normalized;\n if (!elText.includes(normalized) && !normalized.includes(elText)) {\n return false;\n }\n }\n\n // Match by classes if available\n if (targetSemantics.classes && targetSemantics.classes.length > 0) {\n const hasAllClasses = targetSemantics.classes.every(\n cls => el.classList.contains(cls)\n );\n if (hasAllClasses) return true;\n }\n\n // Match by attributes if available\n if (targetSemantics.attributes) {\n const matchesAttrs = Object.entries(targetSemantics.attributes).every(\n ([name, value]) => {\n const attrValue = el.getAttribute(name);\n if (name === 'href' || name === 'src') {\n return cleanAttributeValue(name, attrValue || '') ===\n cleanAttributeValue(name, value);\n }\n return attrValue === value;\n }\n );\n if (matchesAttrs) return true;\n }\n\n // If we have text match, that's sufficient\n if (targetSemantics.text) return true;\n\n return false;\n });\n }\n\n /**\n * Disambiguates a parent element by trying attributes, classes, then nth-of-type\n * Priority: stable attributes > one stable class > nth-of-type\n * @param element The DOM element to disambiguate\n * @param tag The tag name\n * @param pathNode EID path node with semantics (if available)\n * @param fullPath Current selector path (for uniqueness testing)\n * @param root Root element for queries\n * @returns Disambiguated selector part (e.g., \"div[role='main']\" or \"div.sidebar\" or \"div:nth-of-type(3)\")\n */\n private disambiguateParent(\n element: Element,\n tag: string,\n pathNode: { tag: string; semantics: ElementSemantics } | null,\n fullPath: string[],\n root: Document | Element\n ): string {\n // 1. Try with stable attributes from DSL (if available)\n if (pathNode?.semantics?.attributes) {\n const parentWithAttrs = this.buildNodeSelector(tag, pathNode.semantics, {\n excludeClasses: true\n });\n\n // Check if adding attributes reduces ambiguity\n const baseSelector = [...fullPath, tag].join(' > ');\n const baseCandidates = this.querySelectorSafe(baseSelector, root);\n\n const attrSelector = [...fullPath, parentWithAttrs].join(' > ');\n const attrCandidates = this.querySelectorSafe(attrSelector, root);\n\n if (attrCandidates.length > 0 && attrCandidates.length < baseCandidates.length) {\n return parentWithAttrs;\n }\n }\n\n // 2. Try with ONE stable class (if available and NOT utility)\n if (pathNode?.semantics?.classes) {\n const stableClasses = filterStableClasses(pathNode.semantics.classes);\n\n if (stableClasses.length > 0) {\n const parentWithClass = `${tag}.${this.escapeCSS(stableClasses[0])}`;\n\n // Check if adding class reduces ambiguity\n const baseSelector = [...fullPath, tag].join(' > ');\n const baseCandidates = this.querySelectorSafe(baseSelector, root);\n\n const classSelector = [...fullPath, parentWithClass].join(' > ');\n const classCandidates = this.querySelectorSafe(classSelector, root);\n\n if (classCandidates.length > 0 && classCandidates.length < baseCandidates.length) {\n return parentWithClass;\n }\n }\n }\n\n // 3. Fall back to nth-of-type\n const parent = element.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n s => s.tagName.toLowerCase() === tag\n );\n\n if (siblings.length > 1) {\n return `${tag}${this.getNthSelector(element, tag)}`;\n }\n }\n\n return tag;\n }\n\n /**\n * Builds CSS selector path from anchor to target by traversing actual DOM\n */\n private buildPathFromAnchorToTarget(\n anchor: Element,\n target: Element,\n eid: ElementIdentity,\n root: Document | Element\n ): string | null {\n // Build path from target up to anchor\n const pathElements: Element[] = [];\n let current: Element | null = target;\n\n while (current && current !== anchor) {\n pathElements.unshift(current);\n current = current.parentElement;\n }\n\n if (current !== anchor) {\n // Target is not a descendant of anchor\n return null;\n }\n\n // NEW STRATEGY ORDER:\n // 0. anchor path target[attrs_only] - simplest, no classes\n // 1. anchor > parent[attrs|class|nth] > target[attrs_only] - child combinator with smart parent disambiguation\n // 2. anchor parent[attrs|class|nth] target[attrs_only] - descendant combinator with smart parent disambiguation\n // 3. anchor path target[attrs + 1_stable_class] - add ONE stable class to target\n // 4. anchor path target[attrs]:nth-of-type(N) - last resort, nth on target\n\n const strategies = [\n // ============================================================\n // Strategy 0: anchor path target[attrs_only]\n // Most flexible - no classes on target, only semantic attributes\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const targetTag = eid.target.tag;\n const targetSemantics = eid.target.semantics;\n\n // Build path: anchor > meaningful_tags > target\n const meaningfulTags: string[] = [];\n for (const pathNode of eid.path) {\n meaningfulTags.push(pathNode.tag);\n }\n\n // Check if this simplified path is unique\n const parts = [anchorSelector, ...meaningfulTags, targetTag].filter(Boolean);\n const simplePath = parts.join(' ');\n\n if (this.isUnique(simplePath, root)) {\n return simplePath;\n }\n\n // Try adding target semantics (ONLY attributes, NO classes)\n const targetSelector = this.buildNodeSelector(targetTag, targetSemantics, {\n excludeClasses: true // KEY: no classes on target in Strategy 0\n });\n const simplePathWithSemantics = [anchorSelector, ...meaningfulTags.slice(0, -1), targetSelector].join(' ');\n\n if (this.isUnique(simplePathWithSemantics, root)) {\n return simplePathWithSemantics;\n }\n\n return null;\n },\n\n // ============================================================\n // Strategy 1: anchor > parent[attrs|class|nth] > target[attrs_only]\n // Full path with '>' combinator\n // Parents: disambiguated using attrs > stable class > nth-of-type\n // Target: ONLY attributes, NO classes\n // ============================================================\n () => {\n const fullPath = [this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics)];\n\n // Build a mapping from pathElements to eid.path nodes by tag matching\n const pathNodeMap = new Map<Element, { tag: string; semantics: ElementSemantics } | null>();\n let eidPathIndex = 0;\n\n for (let i = 0; i < pathElements.length - 1; i++) {\n const el = pathElements[i];\n const tag = el.tagName.toLowerCase();\n\n // Try to match with next eid.path node\n if (eidPathIndex < eid.path.length && eid.path[eidPathIndex].tag === tag) {\n pathNodeMap.set(el, eid.path[eidPathIndex]);\n eidPathIndex++;\n } else {\n pathNodeMap.set(el, null);\n }\n }\n\n for (let i = 0; i < pathElements.length; i++) {\n const el = pathElements[i];\n const tag = el.tagName.toLowerCase();\n\n // For intermediate nodes (parents), use smart disambiguation\n if (i < pathElements.length - 1) {\n const pathNode = pathNodeMap.get(el) || null;\n const disambiguated = this.disambiguateParent(el, tag, pathNode, fullPath, root);\n fullPath.push(disambiguated);\n continue;\n }\n\n // For target: ONLY attrs, NO classes (Strategy 1 should not add classes to target)\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: true } // KEY: no classes on target\n );\n\n // For table elements, add nth-child if there are siblings with same tag\n const parent = el.parentElement;\n if (parent && ['td', 'th', 'tr', 'thead', 'tbody', 'tfoot'].includes(tag)) {\n const siblings = Array.from(parent.children).filter(\n s => s.tagName.toLowerCase() === tag\n );\n if (siblings.length > 1) {\n fullPath.push(`${targetSelector}${this.getNthSelector(el, tag)}`);\n } else {\n fullPath.push(targetSelector);\n }\n } else {\n fullPath.push(targetSelector);\n }\n }\n\n const selector = fullPath.join(' > ');\n return this.isUnique(selector, root) ? selector : null;\n },\n\n // ============================================================\n // Strategy 2: anchor parent[attrs|class|nth] target[attrs_only]\n // Descendant combinator (space) - more flexible\n // Parents: disambiguated using attrs > stable class > nth-of-type\n // Target: ONLY attributes, NO classes\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const parts = [anchorSelector];\n\n // Build path from EID nodes, using smart disambiguation for parents\n for (let i = 0; i < pathElements.length - 1; i++) {\n const el = pathElements[i];\n const tag = el.tagName.toLowerCase();\n const pathNode = eid.path[i] || null;\n\n // Check if this path node is ambiguous under current selector\n const tempSelector = parts.join(' ') + ' ' + tag;\n const candidates = this.querySelectorSafe(tempSelector, root);\n\n if (candidates.length > 1) {\n // Need disambiguation\n // First try with attributes/class from disambiguateParent logic\n if (pathNode?.semantics?.attributes) {\n const parentWithAttrs = this.buildNodeSelector(tag, pathNode.semantics, {\n excludeClasses: true\n });\n const testSelector = parts.join(' ') + ' ' + parentWithAttrs;\n\n if (this.querySelectorSafe(testSelector, root).length === 1 ||\n this.querySelectorSafe(testSelector + ' ' + eid.target.tag, root).length === 1) {\n parts.push(parentWithAttrs);\n continue;\n }\n }\n\n // Try with stable class\n if (pathNode?.semantics?.classes) {\n const stableClasses = filterStableClasses(pathNode.semantics.classes);\n if (stableClasses.length > 0) {\n const parentWithClass = `${tag}.${this.escapeCSS(stableClasses[0])}`;\n const testSelector = parts.join(' ') + ' ' + parentWithClass;\n\n if (this.querySelectorSafe(testSelector, root).length === 1 ||\n this.querySelectorSafe(testSelector + ' ' + eid.target.tag, root).length === 1) {\n parts.push(parentWithClass);\n continue;\n }\n }\n }\n\n // Fall back to nth-of-type\n // Find the actual element in pathElements that matches this EID path node\n const matchingPathEl = pathElements[i];\n const parent = matchingPathEl.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n s => s.tagName.toLowerCase() === tag\n );\n if (siblings.length > 1) {\n parts.push(`${tag}${this.getNthSelector(matchingPathEl, tag)}`);\n continue;\n }\n }\n }\n\n // No disambiguation needed\n parts.push(tag);\n }\n\n // Add target: ONLY attrs, NO classes\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: true } // KEY: no classes on target\n );\n parts.push(targetSelector);\n\n const selector = parts.join(' ');\n return this.isUnique(selector, root) ? selector : null;\n },\n\n // ============================================================\n // Strategy 3: anchor path target[attrs + 1_stable_class]\n // Add ONE stable class to target (must be semantic, NOT utility)\n // Only use this if attrs alone are not sufficient\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const meaningfulTags: string[] = [];\n\n for (const pathNode of eid.path) {\n meaningfulTags.push(pathNode.tag);\n }\n\n // Target with ONE stable class\n const stableClasses = filterStableClasses(eid.target.semantics.classes ?? []);\n\n if (stableClasses.length === 0) {\n return null; // No stable classes available\n }\n\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { maxClasses: 1 } // KEY: ONE stable class only\n );\n\n const selector = [anchorSelector, ...meaningfulTags.slice(0, -1), targetSelector].join(' ');\n return this.isUnique(selector, root) ? selector : null;\n },\n\n // ============================================================\n // Strategy 4: anchor path target[attrs]:nth-of-type(N)\n // Last resort - add nth-of-type to target\n // Only use when all other strategies fail\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const meaningfulTags: string[] = [];\n\n for (const pathNode of eid.path) {\n meaningfulTags.push(pathNode.tag);\n }\n\n // Target with attrs only + nth-of-type\n const targetEl = pathElements[pathElements.length - 1];\n const targetParent = targetEl.parentElement;\n\n if (!targetParent) return null;\n\n const targetSiblings = Array.from(targetParent.children).filter(\n s => s.tagName.toLowerCase() === eid.target.tag\n );\n\n if (targetSiblings.length <= 1) return null;\n\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: true } // No classes, just attrs + nth\n ) + this.getNthSelector(targetEl, eid.target.tag);\n\n const selector = [anchorSelector, ...meaningfulTags.slice(0, -1), targetSelector].join(' ');\n return this.isUnique(selector, root) ? selector : null;\n }\n ];\n\n // Try each strategy in order\n for (const strategy of strategies) {\n const selector = strategy();\n if (selector) return selector;\n }\n\n // Fallback to the last path generated\n return null;\n }\n\n /**\n * Builds a minimal selector for a DOM element\n * @param element The DOM element to create a selector for\n * @returns A minimal CSS selector for the element\n */\n // @ts-ignore: Method is used dynamically in buildPathFromAnchorToTarget\n private buildElementSelector(element: Element): string {\n const tag = element.tagName.toLowerCase();\n let selector = tag;\n\n // Add ID if stable\n if (element.id && !isDynamicId(element.id)) {\n return `${tag}#${this.escapeCSS(element.id)}`;\n }\n\n // Add stable classes\n const classes = Array.from(element.classList);\n const stableClasses = filterStableClasses(classes);\n if (stableClasses.length > 0) {\n selector += stableClasses.slice(0, 2).map(c => `.${this.escapeCSS(c)}`).join('');\n }\n\n // Add role if present\n const role = element.getAttribute('role');\n if (role) {\n selector += `[role=\"${this.escapeAttr(role)}\"]`;\n }\n\n return selector;\n }\n\n /**\n * Safe querySelectorAll that doesn't throw\n */\n private querySelectorSafe(selector: string, root: Document | Element): Element[] {\n try {\n return Array.from(root.querySelectorAll(selector));\n } catch {\n return [];\n }\n }\n\n /**\n * Finds element by matching text content\n * Returns the matching element (used with getNthSelector for table-aware positioning)\n */\n private findNthElementByText(\n selector: string,\n targetSemantics: ElementSemantics,\n root: Document | Element\n ): Element | null {\n const candidates = this.querySelectorSafe(selector, root);\n if (candidates.length <= 1) return null;\n\n // Find by text content (most reliable for this case)\n if (targetSemantics.text) {\n const normalizedText = targetSemantics.text.normalized;\n\n for (const candidate of candidates) {\n const elText = candidate.textContent?.trim() || '';\n if (elText === normalizedText ||\n elText.includes(normalizedText) ||\n normalizedText.includes(elText)) {\n return candidate; // Return element instead of index\n }\n }\n }\n\n return null;\n }\n\n /**\n * Checks if selector matches exactly one element\n */\n private isUnique(selector: string, root: Document | Element): boolean {\n try {\n const elements = root.querySelectorAll(selector);\n return elements.length === 1;\n } catch {\n return false;\n }\n }\n\n /**\n * Gets nth-of-type index for an element\n */\n private getNthOfTypeIndex(element: Element, tag: string): number | null {\n const parent = element.parentElement;\n if (!parent) return null;\n\n // Get all siblings with same tag\n const siblings = Array.from(parent.children).filter(\n (el) => el.tagName.toLowerCase() === tag\n );\n\n const index = siblings.indexOf(element);\n return index !== -1 ? index + 1 : null;\n }\n\n /**\n * FIX 2: Ensures anchor selector is unique in the document\n * Priority order: tag → tag.class → tag[attr] → tag:nth-of-type(N)\n * @param eid - Element Identity with anchor information\n * @param root - Root element for uniqueness check\n * @returns Unique selector for anchor\n */\n private ensureUniqueAnchor(\n eid: ElementIdentity,\n root: Document | Element\n ): string {\n const tag = eid.anchor.tag;\n const semantics = eid.anchor.semantics;\n\n // Step 1: Try just tag\n if (this.isUnique(tag, root)) {\n return tag;\n }\n\n // Step 2: Try tag with first stable class\n if (semantics.classes && semantics.classes.length > 0) {\n const stableClasses = filterStableClasses(semantics.classes);\n\n if (stableClasses.length > 0) {\n const selectorWithClass = `${tag}.${this.escapeCSS(stableClasses[0])}`;\n if (this.isUnique(selectorWithClass, root)) {\n return selectorWithClass;\n }\n }\n }\n\n // Step 3: Try tag with stable attribute\n if (semantics.attributes) {\n const sortedAttrs = this.getSortedAttributes(semantics.attributes);\n\n for (const { name, value } of sortedAttrs) {\n const cleanedValue =\n name === 'href' || name === 'src'\n ? cleanAttributeValue(name, value)\n : value;\n\n if (cleanedValue) {\n const selectorWithAttr = `${tag}[${name}=\"${this.escapeAttr(cleanedValue)}\"]`;\n if (this.isUnique(selectorWithAttr, root)) {\n return selectorWithAttr;\n }\n }\n }\n }\n\n // Step 4: Try tag with nth-of-type\n // Find all elements with this tag in root\n const allAnchors = Array.from(root.querySelectorAll(tag));\n\n if (allAnchors.length > 1) {\n // Need to match by semantics to find the correct anchor\n const matchingAnchor = this.findElementBySemantics(allAnchors, semantics);\n\n if (matchingAnchor) {\n const nthIndex = this.getNthOfTypeIndex(matchingAnchor, tag);\n if (nthIndex) {\n return `${tag}:nth-of-type(${nthIndex})`;\n }\n }\n }\n\n // Fallback: just tag\n return tag;\n }\n\n /**\n * FIX 2: Finds element by matching semantics\n * @param elements - Array of candidate elements\n * @param semantics - Semantics to match against\n * @returns Matching element or null\n */\n private findElementBySemantics(\n elements: Element[],\n semantics: ElementSemantics\n ): Element | null {\n // If semantics is empty (no classes, attributes, or text), return first element\n const hasSemantics = \n (semantics.classes && semantics.classes.length > 0) ||\n (semantics.attributes && Object.keys(semantics.attributes).length > 0) ||\n semantics.text;\n \n if (!hasSemantics) {\n return elements.length > 0 ? elements[0] : null;\n }\n\n return (\n elements.find((el) => {\n // Match by classes\n if (semantics.classes && semantics.classes.length > 0) {\n const hasClasses = semantics.classes.every((cls) =>\n el.classList.contains(cls)\n );\n if (hasClasses) return true;\n }\n\n // Match by attributes\n if (semantics.attributes) {\n const hasAttrs = Object.entries(semantics.attributes).every(\n ([name, value]) => el.getAttribute(name) === value\n );\n if (hasAttrs) return true;\n }\n\n // Match by text\n if (semantics.text) {\n const elText = el.textContent?.trim() || '';\n const normalized = semantics.text.normalized;\n if (elText.includes(normalized) || normalized.includes(elText)) {\n return true;\n }\n }\n\n return false;\n }) || null\n );\n }\n\n /**\n * FIX 3: Gets nth selector - nth-child for tables, nth-of-type for others\n * This method is now ACTIVELY INTEGRATED in selector generation logic\n * to ensure table elements use position-specific nth-child selectors\n * @param element - Element to get selector for\n * @param tag - Tag name\n * @returns nth selector string (e.g., \":nth-child(2)\" or \":nth-of-type(2)\")\n */\n private getNthSelector(element: Element, tag: string): string {\n const parent = element.parentElement;\n if (!parent) return '';\n\n const siblings = Array.from(parent.children);\n const index = siblings.indexOf(element) + 1;\n\n // For table elements use nth-child (more reliable for table structure)\n if (['tr', 'td', 'th', 'thead', 'tbody', 'tfoot'].includes(tag)) {\n return `:nth-child(${index})`;\n }\n\n // For other elements use nth-of-type\n const sameTags = siblings.filter((s) => s.tagName.toLowerCase() === tag);\n const typeIndex = sameTags.indexOf(element) + 1;\n return `:nth-of-type(${typeIndex})`;\n }\n\n /**\n * Gets attribute priority for sorting\n * @param attrName - Attribute name\n * @returns Priority number (higher = more priority)\n */\n private getAttributePriority(attrName: string): number {\n // Exact match\n if (ATTRIBUTE_PRIORITY[attrName] !== undefined) {\n return ATTRIBUTE_PRIORITY[attrName];\n }\n\n // data-* wildcard\n if (attrName.startsWith('data-')) {\n return ATTRIBUTE_PRIORITY['data-*'];\n }\n\n // aria-* wildcard\n if (attrName.startsWith('aria-')) {\n return ATTRIBUTE_PRIORITY['aria-*'];\n }\n\n return 0; // Don't use in selector\n }\n\n /**\n * Checks if attribute should be ignored\n * @param attrName - Attribute name\n * @returns True if should be ignored\n */\n private shouldIgnoreAttribute(attrName: string): boolean {\n // Ignored attributes\n if (IGNORED_ATTRIBUTES.has(attrName)) return true;\n\n // Inline event handlers\n if (attrName.startsWith('on')) return true;\n\n // Angular/React service attributes\n if (attrName.startsWith('ng-') || attrName.startsWith('_ng')) return true;\n if (attrName.startsWith('data-reactid') || attrName.startsWith('data-react'))\n return true;\n if (attrName.startsWith('data-v-')) return true; // Vue scoped styles\n\n return false;\n }\n\n /**\n * Gets attributes sorted by priority\n * @param attributes - Attributes object\n * @returns Sorted array of attributes with priority\n */\n private getSortedAttributes(\n attributes: Record<string, string>\n ): Array<{ name: string; value: string; priority: number }> {\n return Object.entries(attributes)\n .filter(([name]) => !this.shouldIgnoreAttribute(name))\n // Filter out ID-reference attributes with dynamic values\n .filter(([name, value]) => !ID_REFERENCE_ATTRIBUTES.has(name) || !hasDynamicIdReference(value))\n .map(([name, value]) => ({\n name,\n value,\n priority: this.getAttributePriority(name),\n }))\n .filter((attr) => attr.priority > 0)\n .sort((a, b) => b.priority - a.priority);\n }\n\n /**\n * Builds selector for a single node\n * Priority: ID → semantic attributes → role → classes\n */\n private buildNodeSelector(\n tag: string,\n semantics: ElementSemantics,\n options?: { excludeClasses?: boolean; maxClasses?: number }\n ): string {\n let selector = tag;\n\n // 1. ID (highest priority - score 100)\n if (semantics.id) {\n selector += `#${this.escapeCSS(semantics.id)}`;\n return selector; // ID is unique enough, no need for more\n }\n\n // 2. Attributes in priority order (before classes!)\n if (semantics.attributes) {\n const sortedAttrs = this.getSortedAttributes(semantics.attributes);\n\n for (const { name, value } of sortedAttrs) {\n // Clean href/src from dynamic parts\n const cleanedValue =\n name === 'href' || name === 'src'\n ? cleanAttributeValue(name, value)\n : value;\n\n if (cleanedValue) {\n selector += `[${name}=\"${this.escapeAttr(cleanedValue)}\"]`;\n }\n }\n }\n\n // 3. Role attribute (if not already in attributes)\n if (semantics.role && !semantics.attributes?.role) {\n selector += `[role=\"${this.escapeAttr(semantics.role)}\"]`;\n }\n\n // 4. Stable classes - skip if excludeClasses is true\n if (!options?.excludeClasses && semantics.classes && semantics.classes.length > 0) {\n const stableClasses = filterStableClasses(semantics.classes);\n\n // Apply maxClasses limit if specified\n const classesToAdd = options?.maxClasses !== undefined\n ? stableClasses.slice(0, options.maxClasses)\n : stableClasses;\n\n selector += classesToAdd.map((c) => `.${this.escapeCSS(c)}`).join('');\n }\n\n return selector;\n }\n\n /**\n * Escapes special characters for CSS selector\n */\n private escapeCSS(str: string): string {\n return str.replace(/([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^`{|}~])/g, '\\\\$1');\n }\n\n /**\n * Escapes quotes for attribute values\n */\n private escapeAttr(str: string): string {\n return str.replace(/\"/g, '\\\\\"').replace(/\\\\/g, '\\\\\\\\');\n }\n\n /**\n * Checks if element tag is an SVG child element\n * @param tag - Element tag name\n * @returns True if element is an SVG child\n */\n private isSvgChildElement(tag: string): boolean {\n return SVG_CHILD_ELEMENTS.includes(tag as any);\n }\n}\n","import type { ElementSemantics, TextContent, SvgFingerprint } from '../types';\nimport { normalizeText } from '../utils/text-normalizer';\n\n/**\n * Filters elements by semantic criteria\n * Following SPECIFICATION.md §13.2\n */\nexport class SemanticsMatcher {\n /**\n * Filters elements that match the semantics\n * @param elements - Candidate elements\n * @param semantics - Target semantics to match\n * @returns Filtered elements that match\n */\n match(elements: Element[], semantics: ElementSemantics): Element[] {\n return elements.filter((el) => this.matchElement(el, semantics));\n }\n\n /**\n * Checks if a single element matches the semantics\n */\n private matchElement(element: Element, semantics: ElementSemantics): boolean {\n // Match text (if specified)\n if (semantics.text && !this.matchText(element, semantics.text)) {\n return false;\n }\n\n // Match attributes (if specified)\n if (\n semantics.attributes &&\n !this.matchAttributes(element, semantics.attributes)\n ) {\n return false;\n }\n\n // Match SVG fingerprint (if specified)\n if (\n semantics.svg &&\n !this.matchSvgFingerprint(element as SVGElement, semantics.svg)\n ) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Matches text content\n * Prioritizes direct text nodes, but falls back to full textContent if no direct text\n */\n matchText(element: Element, text: TextContent): boolean {\n // Prioritize direct text nodes\n const directTextNodes = Array.from(element.childNodes)\n .filter(node => node.nodeType === Node.TEXT_NODE);\n\n const directText = directTextNodes\n .map(node => node.textContent?.trim() ?? '')\n .join(' ');\n\n // If no direct text nodes, use full textContent as fallback\n // This handles cases like <td><button>18</button></td> where text is in child element\n const textToMatch = directText || (element.textContent?.trim() ?? '');\n\n if (!textToMatch) return false;\n\n const normalized = normalizeText(textToMatch);\n\n // Support partial matching with option (default is exact)\n return text.matchMode === 'partial'\n ? normalized.includes(text.normalized)\n : normalized === text.normalized;\n }\n\n /**\n * Matches attributes\n */\n matchAttributes(element: Element, attrs: Record<string, string>): boolean {\n for (const [key, value] of Object.entries(attrs)) {\n const elementValue = element.getAttribute(key);\n if (elementValue !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Matches SVG fingerprint\n */\n matchSvgFingerprint(element: SVGElement, fingerprint: SvgFingerprint): boolean {\n // Match shape\n if (element.tagName.toLowerCase() !== fingerprint.shape) {\n return false;\n }\n\n // Match dHash for path elements\n if (fingerprint.dHash && fingerprint.shape === 'path') {\n const d = element.getAttribute('d');\n if (d) {\n const hash = this.computePathHash(d);\n if (hash !== fingerprint.dHash) {\n return false;\n }\n }\n }\n\n // Match geomHash for non-path shapes (circle, rect, ellipse, line)\n if (fingerprint.geomHash && ['circle', 'rect', 'ellipse', 'line'].includes(fingerprint.shape)) {\n const computed = this.computeGeomHash(element, fingerprint.shape);\n if (computed !== fingerprint.geomHash) {\n return false;\n }\n }\n\n // Match title text\n if (fingerprint.titleText) {\n const title = element.querySelector('title');\n if (title?.textContent?.trim() !== fingerprint.titleText) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Computes simple path hash (matching SvgFingerprinter)\n */\n private computePathHash(d: string): string {\n const commands = d.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/gi) ?? [];\n const firstN = commands.slice(0, 5);\n\n const normalized = firstN\n .map((cmd) => {\n return cmd.trim().replace(/(-?\\d+\\.?\\d*)/g, (match) => {\n return parseFloat(match).toFixed(1);\n });\n })\n .join(' ');\n\n return this.simpleHash(normalized);\n }\n\n /**\n * Computes geometry hash for non-path shapes (matching SvgFingerprinter)\n */\n private computeGeomHash(element: SVGElement, shape: string): string {\n const attrs: string[] = [];\n\n switch (shape) {\n case 'circle':\n attrs.push(`r=${element.getAttribute('r') ?? '0'}`);\n break;\n\n case 'rect': {\n const w = parseFloat(element.getAttribute('width') ?? '0');\n const h = parseFloat(element.getAttribute('height') ?? '0');\n if (w > 0 && h > 0) {\n attrs.push(`ratio=${(w / h).toFixed(2)}`);\n }\n break;\n }\n\n case 'ellipse': {\n const rx = parseFloat(element.getAttribute('rx') ?? '0');\n const ry = parseFloat(element.getAttribute('ry') ?? '0');\n if (rx > 0 && ry > 0) {\n attrs.push(`ratio=${(rx / ry).toFixed(2)}`);\n }\n break;\n }\n\n case 'line': {\n const x1 = parseFloat(element.getAttribute('x1') ?? '0');\n const y1 = parseFloat(element.getAttribute('y1') ?? '0');\n const x2 = parseFloat(element.getAttribute('x2') ?? '0');\n const y2 = parseFloat(element.getAttribute('y2') ?? '0');\n const angle = Math.atan2(y2 - y1, x2 - x1);\n attrs.push(`angle=${angle.toFixed(2)}`);\n break;\n }\n }\n\n return this.simpleHash(attrs.join(';'));\n }\n\n /**\n * Simple hash function (matching SvgFingerprinter)\n */\n private simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n }\n}\n","import type { Constraint } from '../types';\n\n/**\n * Evaluates and applies constraints to candidates\n * Following SPECIFICATION.md §13.4\n */\nexport class ConstraintsEvaluator {\n /**\n * Applies a single constraint to candidates\n * @param candidates - Current candidate elements\n * @param constraint - Constraint to apply\n * @returns Filtered candidates\n */\n applyConstraint(candidates: Element[], constraint: Constraint): Element[] {\n switch (constraint.type) {\n case 'text-proximity':\n return this.applyTextProximity(\n candidates,\n constraint.params as { reference: string; maxDistance: number },\n );\n\n case 'position':\n return this.applyPosition(\n candidates,\n constraint.params as { strategy: string },\n );\n\n case 'uniqueness':\n default:\n // Uniqueness doesn't filter, it's handled by the resolver\n return candidates;\n }\n }\n\n /**\n * Applies text proximity constraint using Levenshtein distance\n */\n private applyTextProximity(\n candidates: Element[],\n params: { reference: string; maxDistance: number },\n ): Element[] {\n return candidates.filter((el) => {\n const text = el.textContent?.trim() ?? '';\n const distance = this.levenshteinDistance(text, params.reference);\n return distance <= params.maxDistance;\n });\n }\n\n /**\n * Applies position constraint\n */\n private applyPosition(\n candidates: Element[],\n params: { strategy: string },\n ): Element[] {\n if (candidates.length <= 1) return candidates;\n\n switch (params.strategy) {\n case 'first-in-dom':\n return [candidates[0]];\n\n case 'top-most':\n return [this.getTopMost(candidates)];\n\n case 'left-most':\n return [this.getLeftMost(candidates)];\n\n default:\n return [candidates[0]];\n }\n }\n\n /**\n * Gets the top-most element by bounding rect\n */\n private getTopMost(elements: Element[]): Element {\n return elements.reduce((top, el) => {\n try {\n const topRect = top.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n return elRect.top < topRect.top ? el : top;\n } catch {\n return top;\n }\n });\n }\n\n /**\n * Gets the left-most element by bounding rect\n */\n private getLeftMost(elements: Element[]): Element {\n return elements.reduce((left, el) => {\n try {\n const leftRect = left.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n return elRect.left < leftRect.left ? el : left;\n } catch {\n return left;\n }\n });\n }\n\n /**\n * Calculates Levenshtein distance between two strings\n */\n private levenshteinDistance(a: string, b: string): number {\n // Early exit for identical strings\n if (a === b) return 0;\n\n // Early exit for empty strings\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Use single-row optimization for memory efficiency\n const row: number[] = Array.from({ length: b.length + 1 }, (_, i) => i);\n\n for (let i = 1; i <= a.length; i++) {\n let prev = i;\n for (let j = 1; j <= b.length; j++) {\n const current =\n a[i - 1] === b[j - 1]\n ? row[j - 1]\n : Math.min(row[j - 1], prev, row[j]) + 1;\n row[j - 1] = prev;\n prev = current;\n }\n row[b.length] = prev;\n }\n\n return row[b.length];\n }\n}\n","import type { ElementIdentity, ElementSemantics, ResolveResult } from '../types';\nimport { CssGenerator } from './css-generator';\nimport { normalizeText } from '../utils/text-normalizer';\n\n/**\n * Handles resolution fallback scenarios\n * Following SPECIFICATION.md §13.5\n */\nexport class FallbackHandler {\n private cssGenerator: CssGenerator;\n\n constructor() {\n this.cssGenerator = new CssGenerator();\n }\n\n /**\n * Handles fallback when resolution fails\n * @param eid - Element Identity being resolved\n * @param dom - Document or element to search in\n * @returns Fallback resolution result\n */\n handleFallback(eid: ElementIdentity, dom: Document | Element): ResolveResult {\n const { onMissing } = eid.fallback;\n\n switch (onMissing) {\n case 'anchor-only':\n return this.fallbackToAnchor(eid, dom);\n\n case 'strict':\n return {\n status: 'error',\n elements: [],\n warnings: ['Element not found (strict mode)'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'strict-not-found' },\n };\n\n case 'none':\n default:\n return {\n status: 'error',\n elements: [],\n warnings: ['Element not found'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'not-found' },\n };\n }\n }\n\n /**\n * Attempts to find and return the anchor element as fallback\n */\n private fallbackToAnchor(\n eid: ElementIdentity,\n dom: Document | Element,\n ): ResolveResult {\n const anchorSelector = this.cssGenerator.buildAnchorSelector(eid);\n const root = dom instanceof Document ? dom : (dom.ownerDocument ?? dom);\n\n try {\n const anchor = root.querySelector(anchorSelector);\n\n if (anchor) {\n return {\n status: 'degraded-fallback',\n elements: [anchor],\n warnings: ['Target not found, returning anchor'],\n confidence: eid.meta.confidence * 0.3,\n meta: { degraded: true, degradationReason: 'anchor-fallback' },\n };\n }\n } catch (error) {\n // Log selector error for debugging\n const message =\n error instanceof Error ? error.message : 'Unknown selector error';\n return {\n status: 'error',\n elements: [],\n warnings: [`Invalid anchor selector: ${message}`],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'invalid-anchor-selector' },\n };\n }\n\n return {\n status: 'error',\n elements: [],\n warnings: ['Anchor also not found'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'anchor-not-found' },\n };\n }\n\n /**\n * Handles ambiguous results (multiple matches)\n * @param elements - All matching elements\n * @param eid - Original Element Identity\n * @returns Resolution result based on fallback rules\n */\n handleAmbiguous(elements: Element[], eid: ElementIdentity): ResolveResult {\n const { onMultiple } = eid.fallback;\n\n switch (onMultiple) {\n case 'first':\n return {\n status: 'success',\n elements: [elements[0]],\n warnings: ['Multiple matches, returning first'],\n confidence: eid.meta.confidence * 0.7,\n meta: { degraded: true, degradationReason: 'first-of-multiple' },\n };\n\n case 'best-score':\n return this.selectBestScoring(elements, eid);\n\n case 'allow-multiple':\n default:\n return {\n status: 'ambiguous',\n elements,\n warnings: [`Multiple matches: ${elements.length}`],\n confidence: eid.meta.confidence * 0.5,\n meta: { degraded: true, degradationReason: 'multiple-matches' },\n };\n }\n }\n\n /**\n * Selects the best-scoring element from candidates\n * Re-scores each element based on semantic match quality\n */\n private selectBestScoring(\n elements: Element[],\n eid: ElementIdentity,\n ): ResolveResult {\n const targetSemantics = eid.target.semantics;\n let bestElement = elements[0];\n let bestScore = -1;\n\n for (const element of elements) {\n const score = this.scoreElementMatch(element, targetSemantics);\n if (score > bestScore) {\n bestScore = score;\n bestElement = element;\n }\n }\n\n return {\n status: 'success',\n elements: [bestElement],\n warnings: [\n `Multiple matches (${elements.length}), selected best-scoring element`,\n ],\n confidence: eid.meta.confidence * (0.7 + bestScore * 0.2),\n meta: { degraded: true, degradationReason: 'best-of-multiple' },\n };\n }\n\n /**\n * Scores how well an element matches the target semantics\n * @returns Score from 0 to 1\n */\n private scoreElementMatch(\n element: Element,\n targetSemantics: ElementSemantics,\n ): number {\n let score = 0;\n let maxScore = 0;\n\n // ID match (highest value)\n if (targetSemantics.id) {\n maxScore += 0.3;\n if (element.id === targetSemantics.id) {\n score += 0.3;\n }\n }\n\n // Class match\n if (targetSemantics.classes && targetSemantics.classes.length > 0) {\n maxScore += 0.25;\n const elementClasses = Array.from(element.classList);\n const matchCount = targetSemantics.classes.filter((cls) =>\n elementClasses.includes(cls),\n ).length;\n score += (matchCount / targetSemantics.classes.length) * 0.25;\n }\n\n // Attribute match\n if (targetSemantics.attributes) {\n const attrs = Object.entries(targetSemantics.attributes);\n if (attrs.length > 0) {\n maxScore += 0.2;\n let matchCount = 0;\n for (const [key, value] of attrs) {\n if (element.getAttribute(key) === value) {\n matchCount++;\n }\n }\n score += (matchCount / attrs.length) * 0.2;\n }\n }\n\n // Role match\n if (targetSemantics.role) {\n maxScore += 0.15;\n if (element.getAttribute('role') === targetSemantics.role) {\n score += 0.15;\n }\n }\n\n // Text match\n if (targetSemantics.text) {\n maxScore += 0.1;\n const elementText = normalizeText(element.textContent);\n if (elementText === targetSemantics.text.normalized) {\n score += 0.1;\n } else if (elementText.includes(targetSemantics.text.normalized)) {\n score += 0.05;\n }\n }\n\n // Normalize score if maxScore > 0\n return maxScore > 0 ? score / maxScore : 0;\n }\n}\n","import type { ElementIdentity, ResolveResult, ResolverOptions } from '../types';\nimport { CssGenerator } from './css-generator';\nimport { SemanticsMatcher } from './semantics-matcher';\nimport { ConstraintsEvaluator } from './constraints-evaluator';\nimport { FallbackHandler } from './fallback-handler';\nimport { DEFAULT_RESOLVER_OPTIONS } from '../utils/constants';\n\n/**\n * Resolves Element Identity back to DOM element(s)\n * Following SPECIFICATION.md §13 (5-phase algorithm)\n *\n * @param eid - Element Identity to resolve\n * @param dom - Document or root element to search in\n * @param options - Resolver options\n * @returns Resolution result with matched elements\n */\nexport function resolve(\n eid: ElementIdentity,\n dom: Document | Element,\n options: ResolverOptions = {},\n): ResolveResult {\n const opts = { ...DEFAULT_RESOLVER_OPTIONS, ...options };\n\n const cssGenerator = new CssGenerator();\n const semanticsMatcher = new SemanticsMatcher();\n const constraintsEvaluator = new ConstraintsEvaluator();\n const fallbackHandler = new FallbackHandler();\n\n // Phase 1: CSS Narrowing\n // Build a base selector. We don't use ensureUnique: true here because\n // we want to gather all potential candidates and prioritize them in later phases\n // (e.g., prioritization by visibility).\n const root = dom instanceof Document ? dom : (dom.ownerDocument ?? dom);\n const selector = cssGenerator.buildSelector(eid, {\n ensureUnique: false,\n root,\n });\n\n let candidates: Element[];\n try {\n candidates = Array.from(root.querySelectorAll(selector));\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown selector error';\n return {\n status: 'error',\n elements: [],\n warnings: [\n `Invalid CSS selector: ${selector}`,\n `Error: ${errorMessage}`,\n ],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'invalid-selector' },\n };\n }\n\n // Limit candidates for performance\n if (candidates.length > opts.maxCandidates) {\n candidates = candidates.slice(0, opts.maxCandidates);\n }\n\n // Phase 2: Semantics Filtering\n const filtered = semanticsMatcher.match(candidates, eid.target.semantics);\n\n // Phase 3: Uniqueness Check\n if (filtered.length === 1) {\n return {\n status: 'success',\n elements: filtered,\n warnings: [],\n confidence: eid.meta.confidence,\n meta: { degraded: false },\n };\n }\n\n // No matches found\n if (filtered.length === 0) {\n if (opts.enableFallback) {\n return fallbackHandler.handleFallback(eid, root);\n }\n return {\n status: 'error',\n elements: [],\n warnings: ['No matching elements found'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'not-found' },\n };\n }\n\n // Phase 4: Constraints Application\n let constrained = filtered;\n const sortedConstraints = sortByPriority(eid.constraints);\n\n for (const constraint of sortedConstraints) {\n constrained = constraintsEvaluator.applyConstraint(constrained, constraint);\n\n // Found unique match\n if (constrained.length === 1) {\n return {\n status: 'success',\n elements: constrained,\n warnings: [],\n confidence: eid.meta.confidence * 0.9,\n meta: { degraded: false },\n };\n }\n\n // All candidates eliminated\n if (constrained.length === 0) {\n if (opts.enableFallback) {\n return fallbackHandler.handleFallback(eid, root);\n }\n return {\n status: 'error',\n elements: [],\n warnings: ['Constraints eliminated all candidates'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'over-constrained' },\n };\n }\n }\n\n // Phase 5: Handle Ambiguous Result\n if (opts.strictMode) {\n return {\n status: 'ambiguous',\n elements: constrained,\n warnings: [`Non-unique resolution: ${constrained.length} matches`],\n confidence: eid.meta.confidence * 0.7,\n meta: { degraded: true, degradationReason: 'ambiguous' },\n };\n }\n\n // Apply fallback rules for multiple matches\n return fallbackHandler.handleAmbiguous(constrained, eid);\n}\n\n/**\n * Sorts constraints by priority (descending)\n */\nfunction sortByPriority(\n constraints: ElementIdentity['constraints'],\n): ElementIdentity['constraints'] {\n return [...constraints].sort((a, b) => b.priority - a.priority);\n}\n","import type { ElementIdentity, ValidationResult } from '../types';\n\n/**\n * Validates EID structure for correctness\n * @param eid - The Element Identity to validate\n * @returns Validation result with errors and warnings\n */\nexport function validateEID(eid: ElementIdentity): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Version check\n if (!eid.version) {\n errors.push('Missing version field');\n } else if (eid.version !== '1.0') {\n warnings.push(`Unknown version: ${eid.version}`);\n }\n\n // Anchor check\n if (!eid.anchor) {\n errors.push('Missing anchor field');\n } else {\n if (!eid.anchor.tag) {\n errors.push('Anchor missing tag');\n }\n if (typeof eid.anchor.score !== 'number') {\n errors.push('Anchor missing score');\n }\n if (!eid.anchor.semantics) {\n errors.push('Anchor missing semantics');\n }\n }\n\n // Target check\n if (!eid.target) {\n errors.push('Missing target field');\n } else {\n if (!eid.target.tag) {\n errors.push('Target missing tag');\n }\n if (typeof eid.target.score !== 'number') {\n errors.push('Target missing score');\n }\n if (!eid.target.semantics) {\n errors.push('Target missing semantics');\n }\n }\n\n // Path check\n if (!Array.isArray(eid.path)) {\n errors.push('Path must be an array');\n } else {\n for (let i = 0; i < eid.path.length; i++) {\n const node = eid.path[i];\n if (!node.tag) {\n errors.push(`Path node ${i} missing tag`);\n }\n if (!node.semantics) {\n errors.push(`Path node ${i} missing semantics`);\n }\n }\n }\n\n // Meta check\n if (!eid.meta) {\n errors.push('Missing meta field');\n } else {\n if (typeof eid.meta.confidence !== 'number') {\n warnings.push('Missing confidence score');\n }\n if (!eid.meta.generatedAt) {\n warnings.push('Missing generatedAt timestamp');\n }\n }\n\n // Constraints check\n if (!Array.isArray(eid.constraints)) {\n warnings.push('Constraints should be an array');\n }\n\n // Fallback check\n if (!eid.fallback) {\n warnings.push('Missing fallback rules');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Checks if a value is a valid ElementIdentity object\n * @param value - Value to check\n * @returns True if valid ElementIdentity structure\n */\nexport function isEID(value: unknown): value is ElementIdentity {\n if (!value || typeof value !== 'object') return false;\n\n const obj = value as Record<string, unknown>;\n\n return (\n typeof obj.version === 'string' &&\n typeof obj.anchor === 'object' &&\n Array.isArray(obj.path) &&\n typeof obj.target === 'object'\n );\n}\n","import type { ElementIdentity, AnchorNode, PathNode, TargetNode, ElementSemantics, GeneratorOptions, ResolverOptions } from '../types';\nimport { generateEID as generateEIDInternal } from '../generator';\nimport { resolve as resolveInternal } from '../resolver';\nimport { filterStableClasses } from './class-classifier';\nimport { ATTRIBUTE_PRIORITY, IGNORED_ATTRIBUTES } from './constants';\nimport { cleanAttributeValue } from './attribute-cleaner';\nimport { ID_REFERENCE_ATTRIBUTES, hasDynamicIdReference } from './id-validator';\n\n/**\n * Options for SEQL Selector stringification\n */\nexport interface StringifyOptions {\n /** Maximum number of classes to include per node (default: 2) */\n maxClasses?: number;\n /** Maximum number of attributes to include per node (default: 5) */\n maxAttributes?: number;\n /** Include text content as pseudo-attribute (default: true) */\n includeText?: boolean;\n /** Maximum text length to include (default: 50) */\n maxTextLength?: number;\n /** Simplify target node by removing redundant info (default: true) */\n simplifyTarget?: boolean;\n /** Include resolution constraints in SEQL Selector (default: true) */\n includeConstraints?: boolean;\n}\n\n/**\n * Default stringify options\n */\nconst DEFAULT_STRINGIFY_OPTIONS: Required<StringifyOptions> = {\n maxClasses: 2,\n maxAttributes: 5,\n includeText: true,\n maxTextLength: 50,\n simplifyTarget: true,\n includeConstraints: true,\n};\n\n/**\n * Gets attribute priority for sorting\n */\nfunction getAttributePriority(attrName: string): number {\n // ID is highest priority for SEQL Selector\n if (attrName === 'id') return 101;\n\n // Exact match\n if (ATTRIBUTE_PRIORITY[attrName] !== undefined) {\n return ATTRIBUTE_PRIORITY[attrName];\n }\n\n // data-* wildcard\n if (attrName.startsWith('data-')) {\n return ATTRIBUTE_PRIORITY['data-*'];\n }\n\n // aria-* wildcard\n if (attrName.startsWith('aria-')) {\n return ATTRIBUTE_PRIORITY['aria-*'];\n }\n\n return 0;\n}\n\n/**\n * Checks if attribute is considered unique identifier\n */\nfunction isUniqueAttribute(attrName: string): boolean {\n return ['id', 'data-testid', 'data-qa', 'data-cy', 'href', 'text', 'role'].includes(attrName);\n}\n\n/**\n * Simple PII detection for text content\n */\nfunction isTextPII(text: string): boolean {\n // Email pattern\n if (/@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/.test(text)) return true;\n\n // Phone pattern (basic)\n if (/(\\+?\\d{1,3}[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/.test(text)) return true;\n\n // Credit card pattern (basic)\n if (/\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}/.test(text)) return true;\n\n return false;\n}\n\n/**\n * Converts EID to canonical SEQL Selector string representation\n *\n * Requirements (per SEQL_SPECIFICATION_v1.0.md):\n * - Deterministic (same EID → same SEQL Selector)\n * - Canonical (one EID → one SEQL Selector)\n * - Versioned (includes protocol version)\n * - PII-safe (no personal data)\n * - Sorted attributes and classes\n *\n * @param eid - Element Identity Descriptor\n * @param options - Optional stringify options\n * @returns SEQL Selector (canonical string)\n *\n * @example\n * ```typescript\n * const eid = generateEID(element);\n * const selector = stringifySEQL(eid);\n * // \"v1: footer :: ul.menu > li#3 > a[href=\"/contact\"]\"\n * ```\n */\nexport function stringifySEQL(eid: ElementIdentity, options?: StringifyOptions): string {\n const opts = { ...DEFAULT_STRINGIFY_OPTIONS, ...options };\n\n const version = `v${eid.version}`;\n const anchor = stringifyNode(eid.anchor, false, opts);\n const path = eid.path.length > 0\n ? eid.path.map(node => stringifyNode(node, false, opts)).join(' > ') + ' > '\n : '';\n const target = stringifyNode(eid.target, true, opts); // Pass isTarget=true\n\n // Constraints are optional\n const constraints = opts.includeConstraints ? stringifyConstraints(eid) : '';\n\n return `${version}: ${anchor} :: ${path}${target}${constraints}`;\n}\n\n/**\n * Parses SEQL Selector back to EID structure\n *\n * @param selector - SEQL Selector string (similar to CSS Selector)\n * @returns Element Identity Descriptor\n * @throws {Error} if SEQL Selector is malformed or version unsupported\n *\n * @example\n * ```typescript\n * const selector = \"v1: footer :: ul > li#3 > a[href='/contact']\";\n * const eid = parseSEQL(selector);\n * const elements = resolve(eid, document);\n * ```\n */\nexport function parseSEQL(selector: string): ElementIdentity {\n // Trim whitespace\n selector = selector.trim();\n\n // Parse version\n const versionMatch = selector.match(/^v(\\d+(?:\\.\\d+)?)\\s*:\\s*/);\n if (!versionMatch) {\n throw new Error('Invalid SEQL Selector: missing version prefix (expected \"v1:\")');\n }\n\n const version = versionMatch[1];\n if (version !== '1.0' && version !== '1') {\n throw new Error(`Unsupported SEQL Selector version: v${version} (only v1.0 is supported)`);\n }\n\n // Remove version prefix\n let remaining = selector.slice(versionMatch[0].length);\n\n // Parse anchor (up to ::)\n const anchorMatch = remaining.match(/^(.+?)\\s*::\\s*/);\n if (!anchorMatch) {\n throw new Error('Invalid SEQL Selector: missing anchor separator \"::\"');\n }\n\n const anchorStr = anchorMatch[1].trim();\n remaining = remaining.slice(anchorMatch[0].length);\n\n // Parse path and target (split by >)\n // Need to handle constraints {} at the end\n const constraintsMatch = remaining.match(/\\s*\\{([^}]+)\\}\\s*$/);\n let constraintsStr = '';\n if (constraintsMatch) {\n constraintsStr = constraintsMatch[1];\n remaining = remaining.slice(0, constraintsMatch.index);\n }\n\n // Split by > to get path nodes and target\n const nodes = remaining.split(/\\s*>\\s*/).map((n: string) => n.trim()).filter((n: string) => n);\n\n if (nodes.length === 0) {\n throw new Error('Invalid SEQL Selector: missing target node');\n }\n\n // Last node is target, rest are path\n const targetStr = nodes[nodes.length - 1];\n const pathStrs = nodes.slice(0, -1);\n\n // Parse nodes\n const anchor = parseNode(anchorStr, true) as AnchorNode;\n const path = pathStrs.map((str: string) => parseNode(str, false) as PathNode);\n const target = parseNode(targetStr, false) as TargetNode;\n\n // Parse constraints\n const constraints = parseConstraints(constraintsStr);\n\n // Build EID structure\n const eid: ElementIdentity = {\n version: '1.0',\n anchor,\n path,\n target,\n constraints,\n fallback: {\n onMultiple: 'best-score',\n onMissing: 'anchor-only',\n maxDepth: 10,\n },\n meta: {\n confidence: 0.7,\n generatedAt: new Date().toISOString(),\n generator: `seql-parser@1.0`,\n source: 'seql-string',\n degraded: false,\n },\n };\n\n return eid;\n}\n\n/**\n * Stringify a single node (anchor, path, or target)\n */\nfunction stringifyNode(\n node: AnchorNode | PathNode | TargetNode,\n isTarget: boolean = false,\n options: Required<StringifyOptions> = DEFAULT_STRINGIFY_OPTIONS\n): string {\n const { tag, semantics } = node;\n let result = tag;\n\n // 1. Prepare Attributes (including ID and Role)\n const attrStrings: string[] = [];\n const rawAttributes = { ...semantics.attributes };\n\n // In SEQL Selector, ID is just another attribute [id=\"...\"]\n if (semantics.id) {\n rawAttributes.id = semantics.id;\n }\n\n // Include Role if present in semantics but not in attributes\n if (semantics.role && !rawAttributes.role) {\n rawAttributes.role = semantics.role;\n }\n\n const processedAttrs = Object.entries(rawAttributes)\n .map(([name, value]) => {\n const priority = getAttributePriority(name);\n const cleanedValue = (name === 'href' || name === 'src')\n ? cleanAttributeValue(name, value)\n : value;\n return { name, value: cleanedValue, priority };\n })\n .filter(attr => {\n // Filter out truly ignored attributes (style, xmlns, etc)\n const trulyIgnored = ['style', 'xmlns', 'tabindex', 'contenteditable'];\n if (trulyIgnored.includes(attr.name)) return false;\n\n // Filter out ID-reference attributes with dynamic values\n if (ID_REFERENCE_ATTRIBUTES.has(attr.name) && hasDynamicIdReference(attr.value)) {\n return false;\n }\n\n // Keep if priority > 0 or it's a role or id\n return attr.priority > 0 || attr.name === 'role' || attr.name === 'id';\n });\n\n // Sort by priority (desc) to pick the best ones\n processedAttrs.sort((a, b) => b.priority - a.priority);\n\n // Pick top N attributes\n const topAttrs = processedAttrs.slice(0, options.maxAttributes);\n\n // Sort selected attributes ALPHABETICALLY for SEQL Selector canonical format\n topAttrs.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const { name, value } of topAttrs) {\n attrStrings.push(`${name}=\"${escapeAttributeValue(value)}\"`);\n }\n\n // Add text as pseudo-attribute if enabled\n if (options.includeText && semantics.text && !isTextPII(semantics.text.normalized)) {\n const text = semantics.text.normalized;\n if (text.length > 0 && text.length <= options.maxTextLength) {\n attrStrings.push(`text=\"${escapeAttributeValue(text)}\"`);\n }\n }\n\n if (attrStrings.length > 0) {\n let finalAttrs = attrStrings;\n\n // Advanced simplification for target node\n if (isTarget && options.simplifyTarget && semantics.id) {\n // If we have ID, we can afford to be more selective,\n // but we MUST keep important semantic info like href, text, data-testid\n finalAttrs = attrStrings.filter(s => {\n const name = s.split('=')[0];\n const priority = getAttributePriority(name);\n // Keep high priority attributes (id, data-testid, href, src, role, text)\n return priority >= 60 || name === 'text' || name === 'id' || name === 'role';\n });\n }\n\n if (finalAttrs.length > 0) {\n // Final alphabetical sort for the attributes in the bracket\n finalAttrs.sort((a, b) => a.localeCompare(b));\n result += `[${finalAttrs.join(',')}]`;\n }\n }\n\n // 2. Add stable classes\n if (semantics.classes && semantics.classes.length > 0) {\n const stableClasses = filterStableClasses(semantics.classes);\n\n // If simplifying target and we have strong identifiers, we can skip classes\n const hasStrongIdentifier = !!semantics.id ||\n attrStrings.some(s => s.startsWith('href=') || s.startsWith('data-testid=') || s.startsWith('text=') || s.startsWith('role='));\n const skipClasses = isTarget && options.simplifyTarget && hasStrongIdentifier;\n\n if (!skipClasses && stableClasses.length > 0) {\n const limitedClasses = stableClasses\n .sort() // Alphabetical for determinism\n .slice(0, options.maxClasses);\n\n result += limitedClasses.map(c => `.${c}`).join('');\n }\n }\n\n // 3. Add position (nth-child)\n if ('nthChild' in node && node.nthChild) {\n // SEQL Selector position is #N\n const hasStrongIdentifier = !!semantics.id ||\n (semantics.attributes && Object.keys(semantics.attributes).some(isUniqueAttribute));\n\n const skipPosition = isTarget && options.simplifyTarget && hasStrongIdentifier;\n\n if (!skipPosition) {\n result += `#${node.nthChild}`;\n }\n }\n\n return result;\n}\n\n/**\n * Stringify constraints\n */\nfunction stringifyConstraints(eid: ElementIdentity): string {\n if (!eid.constraints || eid.constraints.length === 0) {\n return '';\n }\n\n // Convert constraints to key=value pairs\n const pairs: string[] = [];\n\n for (const constraint of eid.constraints) {\n switch (constraint.type) {\n case 'uniqueness':\n pairs.push('unique=true');\n break;\n case 'position':\n if (constraint.params && constraint.params.strategy) {\n pairs.push(`pos=${constraint.params.strategy}`);\n }\n break;\n case 'text-proximity':\n if (constraint.params && constraint.params.reference) {\n const escaped = escapeAttributeValue(String(constraint.params.reference));\n pairs.push(`text=\"${escaped}\"`);\n }\n break;\n }\n }\n\n if (pairs.length === 0) {\n return '';\n }\n\n return ` {${pairs.join(',')}}`;\n}\n\n/**\n * Parse a single node string into node structure\n */\nfunction parseNode(nodeStr: string, isAnchor: boolean): AnchorNode | PathNode {\n // Pattern: tag(.class)*[attr=value,attr=value]#position\n\n let remaining = nodeStr;\n const semantics: ElementSemantics = {};\n\n // Extract tag (required)\n const tagMatch = remaining.match(/^([a-z][a-z0-9-]*)/);\n if (!tagMatch) {\n throw new Error(`Invalid node: missing tag name in \"${nodeStr}\"`);\n }\n const tag = tagMatch[1];\n remaining = remaining.slice(tag.length);\n\n // Extract classes\n const classes: string[] = [];\n let classMatch;\n while ((classMatch = remaining.match(/^\\.([a-zA-Z][a-zA-Z0-9-_]*)/))) {\n classes.push(classMatch[1]);\n remaining = remaining.slice(classMatch[0].length);\n }\n if (classes.length > 0) {\n semantics.classes = classes;\n }\n\n // Extract attributes [...]\n const attrMatch = remaining.match(/^\\[([^\\]]+)\\]/);\n if (attrMatch) {\n const attrsStr = attrMatch[1];\n const attributes: Record<string, string> = {};\n\n // Split by comma (but not inside quotes)\n const attrPairs = splitAttributes(attrsStr);\n\n for (const pair of attrPairs) {\n // Match attribute with escaped quotes: key=\"value with \\\" inside\"\n const eqMatch = pair.match(/^([a-z][a-z0-9-]*)(?:=|~=)\"((?:[^\"\\\\]|\\\\.)*)\"/);\n if (eqMatch) {\n const [, key, value] = eqMatch;\n attributes[key] = unescapeAttributeValue(value);\n }\n }\n\n if (Object.keys(attributes).length > 0) {\n // Special handling for pseudo-attributes in SEQL Selector\n if (attributes.text) {\n semantics.text = {\n raw: attributes.text,\n normalized: attributes.text\n };\n delete attributes.text;\n }\n\n if (attributes.id) {\n semantics.id = attributes.id;\n delete attributes.id;\n }\n\n if (attributes.role) {\n semantics.role = attributes.role;\n delete attributes.role;\n }\n\n if (Object.keys(attributes).length > 0) {\n semantics.attributes = attributes;\n }\n }\n\n remaining = remaining.slice(attrMatch[0].length);\n }\n\n // Extract position #N\n let nthChild: number | undefined;\n const posMatch = remaining.match(/^#(\\d+)/);\n if (posMatch) {\n nthChild = parseInt(posMatch[1], 10);\n remaining = remaining.slice(posMatch[0].length);\n }\n\n // Check for remaining unparsed content\n if (remaining.trim()) {\n throw new Error(`Invalid node: unexpected content \"${remaining}\" in \"${nodeStr}\"`);\n }\n\n // Build node\n if (isAnchor) {\n return {\n tag,\n semantics,\n score: 0.7,\n degraded: false,\n };\n } else {\n return {\n tag,\n semantics,\n score: 0.7,\n nthChild,\n };\n }\n}\n\n/**\n * Parse constraints string\n */\nfunction parseConstraints(constraintsStr: string): any[] {\n if (!constraintsStr.trim()) {\n return [];\n }\n\n const constraints: any[] = [];\n const pairs = constraintsStr.split(',').map(p => p.trim());\n\n for (const pair of pairs) {\n const [key, value] = pair.split('=').map(s => s.trim());\n\n switch (key) {\n case 'unique':\n if (value === 'true') {\n constraints.push({\n type: 'uniqueness',\n params: {\n mode: 'strict',\n },\n priority: 90,\n });\n }\n break;\n case 'pos':\n constraints.push({\n type: 'position',\n params: {\n strategy: value,\n },\n priority: 70,\n });\n break;\n case 'text':\n // Remove quotes\n const text = value.replace(/^\"(.*)\"$/, '$1');\n constraints.push({\n type: 'text-proximity',\n params: {\n reference: unescapeAttributeValue(text),\n maxDistance: 5,\n },\n priority: 60,\n });\n break;\n }\n }\n\n return constraints;\n}\n\n/**\n * Split attribute string by comma (respecting quotes)\n */\nfunction splitAttributes(attrsStr: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n\n for (let i = 0; i < attrsStr.length; i++) {\n const char = attrsStr[i];\n\n if (char === '\"' && (i === 0 || attrsStr[i - 1] !== '\\\\')) {\n inQuotes = !inQuotes;\n current += char;\n } else if (char === ',' && !inQuotes) {\n if (current.trim()) {\n result.push(current.trim());\n }\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n result.push(current.trim());\n }\n\n return result;\n}\n\n/**\n * Escape attribute value for SEQL Selector\n */\nfunction escapeAttributeValue(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\') // Backslash\n .replace(/\"/g, '\\\\\"') // Quote\n .replace(/>/g, '\\\\>') // Greater-than\n .replace(/:/g, '\\\\:'); // Colon\n}\n\n/**\n * Unescape attribute value from SEQL Selector\n * Must process in reverse order to avoid double-unescaping\n */\nfunction unescapeAttributeValue(value: string): string {\n return value\n .replace(/\\\\\\\\/g, '\\x00') // Temporary placeholder for backslash\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\>/g, '>')\n .replace(/\\\\:/g, ':')\n .replace(/\\x00/g, '\\\\'); // Restore backslash\n}\n\n// ============================================================================\n// Facade Functions\n// ============================================================================\n\n/**\n * Generate SEQL Selector directly from DOM element\n *\n * This is a convenience function that combines generateEID() and stringifySEQL().\n *\n * @param element - Target DOM element\n * @param generatorOptions - Optional generation options\n * @param stringifyOptions - Optional stringify options\n * @returns SEQL Selector (canonical string)\n *\n * @example\n * ```typescript\n * const button = document.querySelector('.submit-button');\n * const selector = generateSEQL(button);\n * // \"v1: form :: div.actions > button[type=\"submit\",text=\"Submit Order\"]\"\n *\n * // Send to analytics\n * gtag('event', 'click', { element_selector: selector });\n * ```\n */\nexport function generateSEQL(\n element: Element,\n generatorOptions?: GeneratorOptions,\n stringifyOptions?: StringifyOptions\n): string | null {\n const eid = generateEIDInternal(element, generatorOptions);\n if (!eid) {\n return null;\n }\n return stringifySEQL(eid, stringifyOptions);\n}\n\n/**\n * Resolve SEQL Selector directly to DOM elements\n *\n * This is a convenience function that combines parseSEQL() and resolve().\n *\n * @param selector - SEQL Selector string\n * @param root - Root element or document to search in\n * @param options - Optional resolver options\n * @returns Array of matched elements (empty if not found)\n *\n * @example\n * ```typescript\n * // Parse SEQL Selector from analytics\n * const selector = \"v1: form :: button.submit\";\n * const elements = resolveSEQL(selector, document);\n *\n * if (elements.length > 0) {\n * highlightElement(elements[0]);\n * }\n * ```\n */\nexport function resolveSEQL(\n selector: string,\n root: Document | Element,\n options?: ResolverOptions\n): Element[] {\n try {\n const eid = parseSEQL(selector);\n const result = resolveInternal(eid, root, options);\n return result.elements || [];\n } catch (error) {\n console.error('Failed to resolve SEQL Selector:', error);\n return [];\n }\n}\n","import type { ElementIdentity, GeneratorOptions } from '../types';\nimport { generateEID } from '../generator';\nimport type { EIDCache } from './eid-cache';\nimport { getGlobalCache } from './eid-cache';\nimport { isDynamicId } from './id-validator';\n\n/**\n * Elements to skip during batch generation\n */\nconst SKIP_TAGS = new Set([\n 'script',\n 'style',\n 'noscript',\n 'meta',\n 'link',\n 'head',\n 'title',\n]);\n\n/**\n * Options for batch EID generation\n */\nexport interface BatchGeneratorOptions {\n /** Root element to search from (default: document.body) */\n root?: Element | Document;\n /** CSS selector to filter elements (default: '*') */\n filter?: string;\n /** Maximum number of elements to process (default: Infinity) */\n limit?: number;\n /** Progress callback: (current, total) => void */\n onProgress?: (current: number, total: number) => void;\n /** Interval for calling onProgress (default: 100) */\n progressInterval?: number;\n /** Skip elements without semantic features (default: true) */\n skipNonSemantic?: boolean;\n /** Generator options */\n generatorOptions?: GeneratorOptions;\n /** Optional cache instance */\n cache?: EIDCache;\n /** AbortController for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Result of batch generation\n */\nexport interface BatchResult {\n /** Successfully generated Element Identities */\n results: Array<{\n element: Element;\n eid: ElementIdentity;\n generationTimeMs: number;\n }>;\n /** Elements that failed to generate EID */\n failed: Array<{\n element: Element;\n error: string;\n }>;\n /** Statistics */\n stats: {\n totalElements: number;\n successful: number;\n failed: number;\n skipped: number;\n totalTimeMs: number;\n avgTimePerElementMs: number;\n cacheHitRate: number;\n };\n}\n\n/**\n * Element priority for optimized processing order\n */\nenum ElementPriority {\n HIGH = 3, // Elements with ID\n MEDIUM = 2, // Elements with semantic attributes\n LOW = 1, // Other elements\n}\n\n/**\n * Get element priority for processing order\n */\nfunction getElementPriority(element: Element): ElementPriority {\n // High priority: elements with stable ID\n if (element.id && !isDynamicId(element.id)) {\n return ElementPriority.HIGH;\n }\n\n // Medium priority: elements with semantic attributes\n if (\n element.hasAttribute('role') ||\n element.hasAttribute('aria-label') ||\n element.hasAttribute('aria-labelledby') ||\n element.hasAttribute('data-testid') ||\n element.hasAttribute('data-qa') ||\n element.hasAttribute('data-test')\n ) {\n return ElementPriority.MEDIUM;\n }\n\n return ElementPriority.LOW;\n}\n\n/**\n * Check if element should be skipped\n */\nfunction shouldSkipElement(element: Element, skipNonSemantic: boolean): boolean {\n const tag = element.tagName.toLowerCase();\n\n // Skip certain tags\n if (SKIP_TAGS.has(tag)) {\n return true;\n }\n\n // Skip non-semantic elements if requested\n if (skipNonSemantic) {\n const priority = getElementPriority(element);\n if (priority === ElementPriority.LOW) {\n // Check if it's a semantic tag\n const semanticTags = [\n 'form',\n 'main',\n 'nav',\n 'section',\n 'article',\n 'footer',\n 'header',\n 'button',\n 'a',\n 'input',\n 'label',\n 'select',\n 'textarea',\n ];\n if (!semanticTags.includes(tag)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Sort elements by priority for optimized processing\n */\nfunction sortElementsByPriority(elements: Element[]): Element[] {\n return [...elements].sort((a, b) => {\n const priorityA = getElementPriority(a);\n const priorityB = getElementPriority(b);\n return priorityB - priorityA; // Higher priority first\n });\n}\n\n\n/**\n * Generate EID for all elements matching criteria\n */\nexport function generateEIDBatch(\n options: BatchGeneratorOptions = {},\n): BatchResult {\n const startTime = performance.now();\n const {\n root = typeof document !== 'undefined' ? document.body : undefined,\n filter = '*',\n limit = Infinity,\n onProgress,\n progressInterval = 100,\n skipNonSemantic = true,\n generatorOptions = {},\n cache,\n signal,\n } = options;\n\n if (!root) {\n throw new Error('Root element or document is required');\n }\n\n const cacheInstance = cache ?? getGlobalCache();\n const mergedOptions = { ...generatorOptions, cache: cacheInstance };\n\n // Get all elements\n let allElements: Element[];\n try {\n if (root instanceof Document) {\n allElements = Array.from(root.querySelectorAll(filter));\n } else {\n allElements = Array.from(root.querySelectorAll(filter));\n }\n } catch (error) {\n return {\n results: [],\n failed: [],\n stats: {\n totalElements: 0,\n successful: 0,\n failed: 0,\n skipped: 0,\n totalTimeMs: 0,\n avgTimePerElementMs: 0,\n cacheHitRate: 0,\n },\n };\n }\n\n // Filter out skipped elements\n const filteredElements = allElements.filter(\n (el) => !shouldSkipElement(el, skipNonSemantic),\n );\n\n // Sort by priority for optimized processing\n const sortedElements = sortElementsByPriority(filteredElements);\n\n // Apply limit\n const elementsToProcess = sortedElements.slice(0, limit);\n\n const results: BatchResult['results'] = [];\n const failed: BatchResult['failed'] = [];\n let skipped = 0;\n\n const totalElements = elementsToProcess.length;\n let lastProgressCall = 0;\n\n // Process elements\n for (let i = 0; i < elementsToProcess.length; i++) {\n // Check for cancellation\n if (signal?.aborted) {\n break;\n }\n\n const element = elementsToProcess[i];\n\n // Check cache first\n const cachedEID = cacheInstance.getEID(element);\n if (cachedEID) {\n results.push({\n element,\n eid: cachedEID,\n generationTimeMs: 0, // Cached, no generation time\n });\n } else {\n // Generate EID\n const genStart = performance.now();\n try {\n const eid = generateEID(element, mergedOptions);\n const genTime = performance.now() - genStart;\n\n if (eid) {\n results.push({\n element,\n eid,\n generationTimeMs: genTime,\n });\n } else {\n skipped++;\n }\n } catch (error) {\n failed.push({\n element,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Call progress callback\n if (onProgress && i - lastProgressCall >= progressInterval) {\n onProgress(i + 1, totalElements);\n lastProgressCall = i;\n }\n }\n\n // Final progress call\n if (onProgress) {\n onProgress(totalElements, totalElements);\n }\n\n const totalTime = performance.now() - startTime;\n\n // Calculate cache hit rate\n const cacheStats = cacheInstance.getStats();\n const totalCacheOps =\n cacheStats.eidHits + cacheStats.eidMisses + cacheStats.selectorHits + cacheStats.selectorMisses;\n const totalCacheHits = cacheStats.eidHits + cacheStats.selectorHits;\n const cacheHitRate =\n totalCacheOps > 0 ? totalCacheHits / totalCacheOps : 0;\n\n return {\n results,\n failed,\n stats: {\n totalElements,\n successful: results.length,\n failed: failed.length,\n skipped,\n totalTimeMs: totalTime,\n avgTimePerElementMs:\n results.length > 0 ? totalTime / results.length : 0,\n cacheHitRate,\n },\n };\n}\n\n/**\n * Generate EID for specific elements\n */\nexport function generateEIDForElements(\n elements: Element[],\n options: Omit<BatchGeneratorOptions, 'root' | 'filter'> = {},\n): BatchResult {\n const startTime = performance.now();\n const {\n limit = Infinity,\n onProgress,\n progressInterval = 100,\n skipNonSemantic = true,\n generatorOptions = {},\n cache,\n signal,\n } = options;\n\n const cacheInstance = cache ?? getGlobalCache();\n const mergedOptions = { ...generatorOptions, cache: cacheInstance };\n\n // Filter out skipped elements\n const filteredElements = elements.filter(\n (el) => !shouldSkipElement(el, skipNonSemantic),\n );\n\n // Sort by priority\n const sortedElements = sortElementsByPriority(filteredElements);\n\n // Apply limit\n const elementsToProcess = sortedElements.slice(0, limit);\n\n const results: BatchResult['results'] = [];\n const failed: BatchResult['failed'] = [];\n let skipped = 0;\n\n const totalElements = elementsToProcess.length;\n let lastProgressCall = 0;\n\n // Process elements\n for (let i = 0; i < elementsToProcess.length; i++) {\n // Check for cancellation\n if (signal?.aborted) {\n break;\n }\n\n const element = elementsToProcess[i];\n\n // Check cache first\n const cachedEID = cacheInstance.getEID(element);\n if (cachedEID) {\n results.push({\n element,\n eid: cachedEID,\n generationTimeMs: 0,\n });\n } else {\n // Generate EID\n const genStart = performance.now();\n try {\n const eid = generateEID(element, mergedOptions);\n const genTime = performance.now() - genStart;\n\n if (eid) {\n results.push({\n element,\n eid,\n generationTimeMs: genTime,\n });\n } else {\n skipped++;\n }\n } catch (error) {\n failed.push({\n element,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Call progress callback\n if (onProgress && i - lastProgressCall >= progressInterval) {\n onProgress(i + 1, totalElements);\n lastProgressCall = i;\n }\n }\n\n // Final progress call\n if (onProgress) {\n onProgress(totalElements, totalElements);\n }\n\n const totalTime = performance.now() - startTime;\n\n // Calculate cache hit rate\n const cacheStats = cacheInstance.getStats();\n const totalCacheOps =\n cacheStats.eidHits + cacheStats.eidMisses + cacheStats.selectorHits + cacheStats.selectorMisses;\n const totalCacheHits = cacheStats.eidHits + cacheStats.selectorHits;\n const cacheHitRate =\n totalCacheOps > 0 ? totalCacheHits / totalCacheOps : 0;\n\n return {\n results,\n failed,\n stats: {\n totalElements,\n successful: results.length,\n failed: failed.length,\n skipped,\n totalTimeMs: totalTime,\n avgTimePerElementMs:\n results.length > 0 ? totalTime / results.length : 0,\n cacheHitRate,\n },\n };\n}\n"],"names":["CONFIDENCE_WEIGHTS","ANCHOR_SCORE","PATH_SCORE","SEMANTIC_ANCHOR_TAGS","ROLE_ANCHOR_VALUES","SEMANTIC_TAGS","SVG_CHILD_ELEMENTS","SEMANTIC_ATTRIBUTES","ATTRIBUTE_PRIORITY","IGNORED_ATTRIBUTES","DEFAULT_GENERATOR_OPTIONS","DEFAULT_RESOLVER_OPTIONS","isDynamicId","id","ID_REFERENCE_ATTRIBUTES","hasDynamicIdReference","value","AnchorFinder","options","cache","target","cached","current","depth","bestCandidate","rawScore","score","tier","candidate","result","element","tag","role","penalty","DYNAMIC_CLASS_PATTERNS","UTILITY_CLASS_PATTERNS","SEMANTIC_CLASS_PATTERNS","isDynamicClass","className","pattern","isUtilityClass","isSemanticClass","isStableClass","filterStableClasses","classes","cls","scoreClass","filterClasses","semantic","utility","isUtilityClassFromClassifier","getClassScore","scoreClassFromClassifier","cssEscape","str","PathBuilder","anchor","extractor","rawPath","depthOverflow","filteredPath","el","parent","nthChild","index","selector","doc","matches","skippedNodes","node","testPath","testSelector","testMatches","path","nodeIndex","insertIndex","i","parts","elements","attr","normalizeText","text","DEFAULT_OPTIONS","isDynamicHash","hash","p","cleanUrlValue","isAbsolute","baseWithQuery","base","query","cleaned","cleanAttributeValue","attrName","opts","SemanticExtractor","semantics","attrs","name","rawText","normalized","maxLength","truncatedRaw","truncatedNorm","texts","trimmed","SvgFingerprinter","shape","fingerprint","d","title","cmd","match","w","h","rx","ry","x1","y1","x2","y2","angle","style","char","calculateConfidence","eid","uniquenessBonus","anchorScore","avgPathScore","sum","n","targetScore","confidence","degradationPenalty","calculateElementScore","semanticsCount","hasId","hasRole","LRUCache","maxSize","key","firstKey","EIDCache","results","_element","total","totalHits","totalMisses","createEIDCache","globalCache","getGlobalCache","resetGlobalCache","generateEID","cachedEID","anchorFinder","pathBuilder","semanticExtractor","svgFingerprinter","anchorResult","anchorElement","anchorDegraded","anchorSemantics","anchorNode","pathResult","targetSemantics","isSvgElement","targetParent","targetNthChild","targetNode","constraints","fallback","isDegraded","degradationReason","getDegradationReason","CssGenerator","anchorSelector","nodeSelector","targetBaseSelector","isSvgChild","hasSvgInPath","baseSelector","svgIndexInPath","svgIndexInParts","beforeAndIncludingSvg","afterSvgBeforeTarget","baseSelectorMatches","fullPathSelector","root","maxClasses","targetTag","currentSelector","extraClassesAdded","usedNthOfType","availableTargetClasses","targetElement","anchors","targetCandidates","scoredCandidates","a","b","pathSelector","eidPath","domPath","minLength","domEl","eidNode","matchingClasses","matchingAttrs","lengthDiff","elText","attrValue","pathNode","fullPath","parentWithAttrs","baseCandidates","attrSelector","attrCandidates","stableClasses","parentWithClass","classSelector","classCandidates","s","pathElements","strategies","meaningfulTags","simplePath","targetSelector","simplePathWithSemantics","pathNodeMap","eidPathIndex","disambiguated","tempSelector","matchingPathEl","targetEl","strategy","c","candidates","normalizedText","selectorWithClass","sortedAttrs","cleanedValue","selectorWithAttr","allAnchors","matchingAnchor","nthIndex","siblings","attributes","classesToAdd","SemanticsMatcher","textToMatch","ConstraintsEvaluator","constraint","params","top","topRect","left","leftRect","row","_","prev","j","FallbackHandler","dom","onMissing","error","message","onMultiple","bestElement","bestScore","maxScore","elementClasses","matchCount","elementText","resolve","cssGenerator","semanticsMatcher","constraintsEvaluator","fallbackHandler","errorMessage","filtered","constrained","sortedConstraints","sortByPriority","validateEID","errors","warnings","isEID","obj","DEFAULT_STRINGIFY_OPTIONS","getAttributePriority","isUniqueAttribute","isTextPII","stringifySEQL","version","stringifyNode","stringifyConstraints","parseSEQL","versionMatch","remaining","anchorMatch","anchorStr","constraintsMatch","constraintsStr","nodes","targetStr","pathStrs","parseNode","parseConstraints","isTarget","attrStrings","rawAttributes","processedAttrs","priority","topAttrs","escapeAttributeValue","finalAttrs","hasStrongIdentifier","limitedClasses","pairs","escaped","nodeStr","isAnchor","tagMatch","classMatch","attrMatch","attrsStr","attrPairs","splitAttributes","pair","eqMatch","unescapeAttributeValue","posMatch","inQuotes","generateSEQL","generatorOptions","stringifyOptions","generateEIDInternal","resolveSEQL","resolveInternal","SKIP_TAGS","getElementPriority","shouldSkipElement","skipNonSemantic","sortElementsByPriority","priorityA","generateEIDBatch","startTime","filter","limit","onProgress","progressInterval","signal","cacheInstance","mergedOptions","allElements","filteredElements","elementsToProcess","failed","skipped","totalElements","lastProgressCall","genStart","genTime","totalTime","cacheStats","totalCacheOps","totalCacheHits","cacheHitRate","generateEIDForElements"],"mappings":"+NAiBO,MAAMA,EAAqB,CAChC,OAAQ,GACR,KAAM,GACN,OAAQ,GACR,WAAY,EACd,EAMaC,EAAe,CAC1B,aAAc,GACd,KAAM,GACN,WAAY,GACZ,UAAW,GACX,YAAa,IACb,wBAAyB,EACzB,qBAAsB,IACtB,eAAgB,EAClB,EAKaC,GAAa,CACxB,wBAAyB,EAC3B,EAMaC,EAAuB,CAClC,OACA,OACA,MACA,UACA,UACA,SACA,QACF,EAMaC,EAAqB,CAChC,OACA,aACA,OACA,SACA,cACA,gBACA,SACA,QACF,EAMaC,GAAgB,CAE3B,UACA,QACA,UACA,aACA,SACA,SACA,SACA,OACA,OACA,MACA,UACA,UACA,OAEA,SACA,WACA,WACA,OACA,QACA,QACA,SACA,QACA,WACA,SACA,SACA,WACA,SACA,WAEA,IACA,QACA,QACA,SACA,SACA,OAEA,aACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,IACA,MACA,KACA,KACA,KACA,KACA,KACA,KAEA,UACA,MACA,WACA,QACA,QACA,KACA,QACA,KACA,QACA,KAEA,MACA,OACA,SACA,OACA,OACA,WACA,UACA,UACA,IACA,OACA,KACF,EAMaC,GAAqB,CAChC,OACA,OACA,SACA,OACA,WACA,UACA,UACA,IACA,OACA,MACA,OACA,WACA,MACF,EAKaC,GAAsB,CACjC,aACA,kBACA,mBACA,OACA,OACA,cACA,UACA,YACA,OACA,QACA,cACA,KACF,EAMaC,EAA6C,CAExD,cAAe,IACf,UAAW,GACX,UAAW,GACX,YAAa,GACb,eAAgB,GAGhB,aAAc,GACd,kBAAmB,GACnB,mBAAoB,GAGpB,KAAQ,GACR,KAAQ,GACR,IAAO,GACP,KAAQ,GACR,KAAQ,GACR,IAAO,GACP,MAAS,GACT,IAAO,GACP,YAAe,GAGf,SAAU,GAGV,SAAU,EACZ,EAKaC,OAAyB,IAAI,CACxC,KACA,QACA,QACA,QACA,WACA,iBACF,CAAC,EAMYC,GAAyG,CACpH,aAAc,GACd,qBAAsB,GACtB,oBAAqB,GACrB,eAAgB,GAChB,sBAAuB,GACvB,OAA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"}
|
|
1
|
+
{"version":3,"file":"seql-js.umd.cjs","sources":["../src/utils/constants.ts","../src/utils/id-validator.ts","../src/generator/anchor-finder.ts","../src/utils/class-classifier.ts","../src/utils/class-filter.ts","../src/generator/path-builder.ts","../src/utils/text-normalizer.ts","../src/utils/attribute-cleaner.ts","../src/utils/attribute-filters.ts","../src/generator/semantic-extractor.ts","../src/generator/svg-fingerprinter.ts","../src/utils/scorer.ts","../src/utils/eid-cache.ts","../src/generator/generator.ts","../src/resolver/css-generator.ts","../src/resolver/semantics-matcher.ts","../src/resolver/constraints-evaluator.ts","../src/resolver/fallback-handler.ts","../src/resolver/resolver.ts","../src/utils/validator.ts","../src/utils/seql-parser.ts","../src/utils/batch-generator.ts"],"sourcesContent":["import type { EIDVersion, GeneratorOptions, ResolverOptions } from '../types';\n\n/**\n * EID specification version\n */\nexport const EID_VERSION: EIDVersion = '1.0';\n\n/**\n * Maximum path depth for traversal\n * Following SPECIFICATION.md §8\n */\nexport const MAX_PATH_DEPTH = 10;\n\n/**\n * Confidence calculation weights\n * Following SPECIFICATION.md §13\n */\nexport const CONFIDENCE_WEIGHTS = {\n ANCHOR: 0.4,\n PATH: 0.3,\n TARGET: 0.2,\n UNIQUENESS: 0.1,\n} as const;\n\n/**\n * Anchor scoring weights\n * Following SPECIFICATION.md §7\n */\nexport const ANCHOR_SCORE = {\n SEMANTIC_TAG: 0.5,\n ROLE: 0.3,\n ARIA_LABEL: 0.1,\n STABLE_ID: 0.1,\n TEST_MARKER: 0.05,\n DEPTH_PENALTY_THRESHOLD: 5,\n DEPTH_PENALTY_FACTOR: 0.05,\n DEGRADED_SCORE: 0.3,\n} as const;\n\n/**\n * Path building constants\n */\nexport const PATH_SCORE = {\n MIN_CONFIDENCE_FOR_SKIP: 0.7,\n} as const;\n\n/**\n * Tier A semantic anchor tags - highest priority\n * Following SPECIFICATION.md §7\n */\nexport const SEMANTIC_ANCHOR_TAGS = [\n 'form',\n 'main',\n 'nav',\n 'section',\n 'article',\n 'footer',\n 'header',\n];\n\n/**\n * Tier B role values for anchor detection\n * Following SPECIFICATION.md §7\n */\nexport const ROLE_ANCHOR_VALUES = [\n 'form',\n 'navigation',\n 'main',\n 'region',\n 'contentinfo',\n 'complementary',\n 'banner',\n 'search',\n];\n\n/**\n * All semantic tags to include in path\n * Following SPECIFICATION.md §8\n */\nexport const SEMANTIC_TAGS = [\n // HTML5 Semantic\n 'article',\n 'aside',\n 'details',\n 'figcaption',\n 'figure',\n 'footer',\n 'header',\n 'main',\n 'mark',\n 'nav',\n 'section',\n 'summary',\n 'time',\n // Form elements\n 'button',\n 'datalist',\n 'fieldset',\n 'form',\n 'input',\n 'label',\n 'legend',\n 'meter',\n 'optgroup',\n 'option',\n 'output',\n 'progress',\n 'select',\n 'textarea',\n // Interactive\n 'a',\n 'audio',\n 'video',\n 'canvas',\n 'dialog',\n 'menu',\n // Text content\n 'blockquote',\n 'dd',\n 'dl',\n 'dt',\n 'hr',\n 'li',\n 'ol',\n 'ul',\n 'p',\n 'pre',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n // Table\n 'caption',\n 'col',\n 'colgroup',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'tr',\n // SVG\n 'svg',\n 'path',\n 'circle',\n 'rect',\n 'line',\n 'polyline',\n 'polygon',\n 'ellipse',\n 'g',\n 'text',\n 'use',\n];\n\n/**\n * SVG child elements that are direct children of svg element\n * These require child combinator for accurate selection\n */\nexport const SVG_CHILD_ELEMENTS = [\n 'rect',\n 'path',\n 'circle',\n 'line',\n 'polyline',\n 'polygon',\n 'ellipse',\n 'g',\n 'text',\n 'use',\n 'defs',\n 'clipPath',\n 'mask',\n] as const;\n\n/**\n * Semantic attributes to extract\n */\nexport const SEMANTIC_ATTRIBUTES = [\n 'aria-label',\n 'aria-labelledby',\n 'aria-describedby',\n 'name',\n 'type',\n 'data-testid',\n 'data-qa',\n 'data-test',\n 'href',\n 'title',\n 'placeholder',\n 'alt',\n];\n\n/**\n * Attribute priorities for CSS selector generation (higher = more priority)\n * Following the priority order for selector building\n */\nexport const ATTRIBUTE_PRIORITY: Record<string, number> = {\n // Test attributes (highest priority)\n 'data-testid': 100,\n 'data-qa': 99,\n 'data-cy': 98,\n 'data-test': 97,\n 'data-test-id': 96,\n\n // ARIA (accessibility semantics)\n 'aria-label': 90,\n 'aria-labelledby': 85,\n 'aria-describedby': 80,\n\n // Semantic HTML attributes\n 'name': 75,\n 'href': 70, // for <a>\n 'src': 70, // for <img>, <script>, etc.\n 'type': 65,\n 'role': 60,\n 'alt': 55,\n 'title': 50,\n 'for': 45,\n 'placeholder': 40,\n\n // Any data-* attribute (if not above)\n 'data-*': 30,\n\n // Any aria-* attribute (if not above)\n 'aria-*': 25,\n};\n\n/**\n * Attributes to ignore in selector generation\n */\nexport const IGNORED_ATTRIBUTES = new Set([\n 'id', // handled separately\n 'class', // handled separately\n 'style', // unstable\n 'xmlns', // service attribute for SVG\n 'tabindex', // can change\n 'contenteditable',\n]);\n\n/**\n * Default generator options\n * Note: cache is optional and not included in defaults\n */\nexport const DEFAULT_GENERATOR_OPTIONS: Omit<Required<GeneratorOptions>, 'cache'> & Pick<GeneratorOptions, 'cache'> = {\n maxPathDepth: MAX_PATH_DEPTH,\n enableSvgFingerprint: true,\n confidenceThreshold: 0.1,\n fallbackToBody: true,\n includeUtilityClasses: false,\n source: 'dom-dsl',\n};\n\n/**\n * Default resolver options\n */\nexport const DEFAULT_RESOLVER_OPTIONS: Required<ResolverOptions> = {\n strictMode: false,\n enableFallback: true,\n maxCandidates: 20,\n};\n","/**\n * Checks if an ID appears to be dynamically generated\n * Dynamic IDs should not be used in DSL as they change between sessions\n *\n * @param id - ID string to check\n * @returns True if ID appears to be dynamic/generated\n */\nexport function isDynamicId(id: string): boolean {\n // Pattern: prefix-number (e.g., \"input-123\", \"react-select-2\")\n if (/^[a-z]+-\\d+$/i.test(id)) return true;\n\n // Pattern: multi-word-prefix-number (e.g., \"react-day-picker-1\", \"mui-date-input-42\")\n if (/^[a-z]+(-[a-z]+)+-\\d+$/i.test(id)) return true;\n\n // Pattern: prefix_number (underscore variant, e.g., \"radix_dialog_1\")\n if (/^[a-z]+(_[a-z]+)*_\\d+$/i.test(id)) return true;\n\n // Pattern: just numbers (e.g., \"123\")\n if (/^\\d+$/.test(id)) return true;\n\n // Pattern: React-style IDs (e.g., \":r0:\", \":R1:\")\n if (/^:[a-z0-9]+:$/i.test(id)) return true;\n\n // Pattern: UUID-like (e.g., \"550e8400-e29b-41d4-a716-446655440000\")\n if (\n /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(id)\n ) {\n return true;\n }\n\n // Pattern: hash-like (long random strings with mixed case/numbers)\n // e.g., \"css-1a2b3c4d\", \"sc-bdVaJa\"\n if (\n /^[a-z]{1,3}[A-Za-z0-9]{8,}$/.test(id) &&\n (/\\d/.test(id) || /[A-Z]/.test(id))\n ) {\n return true;\n }\n\n // Pattern: Radix UI style (e.g., \"radix-:r0:\")\n if (/^radix-/.test(id)) return true;\n\n // Pattern: MUI style (e.g., \"mui-123456\")\n if (/^mui-\\d+$/.test(id)) return true;\n\n return false;\n}\n\n/**\n * Attributes whose values are references to IDs of other elements.\n * If the value contains a dynamic ID, the attribute should be ignored.\n */\nexport const ID_REFERENCE_ATTRIBUTES = new Set([\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-controls',\n 'aria-owns',\n 'aria-activedescendant',\n 'for',\n 'form',\n 'list',\n 'headers',\n 'aria-details',\n 'aria-errormessage',\n 'aria-flowto',\n]);\n\n/**\n * Checks if an attribute value contains a dynamic ID reference.\n * Used for filtering attributes like aria-labelledby.\n * @param value - Attribute value (may contain space-separated IDs)\n * @returns True if value contains at least one dynamic ID\n */\nexport function hasDynamicIdReference(value: string): boolean {\n // Value may contain a list of IDs separated by spaces\n const ids = value.trim().split(/\\s+/);\n return ids.some(id => isDynamicId(id));\n}\n\n/**\n * Validates if an ID is stable and suitable for DSL\n * @param id - ID string to validate\n * @returns True if ID is stable and can be used in DSL\n */\nexport function isStableId(id: string | null | undefined): boolean {\n if (!id) return false;\n return !isDynamicId(id);\n}\n","import type { GeneratorOptions } from '../types';\nimport {\n SEMANTIC_ANCHOR_TAGS,\n ROLE_ANCHOR_VALUES,\n MAX_PATH_DEPTH,\n ANCHOR_SCORE,\n} from '../utils/constants';\nimport { isDynamicId } from '../utils/id-validator';\nimport type { EIDCache } from '../utils/eid-cache';\n\n/**\n * Result of anchor finding\n */\nexport interface AnchorResult {\n element: Element;\n score: number;\n tier: 'A' | 'B' | 'C';\n depth: number;\n}\n\n/**\n * Finds semantic anchor for element identification\n * Traverses up from target to find semantic root\n * Following SPECIFICATION.md §7\n */\nexport class AnchorFinder {\n private maxDepth: number;\n private cache?: EIDCache;\n\n constructor(options: GeneratorOptions, cache?: EIDCache) {\n this.maxDepth = options.maxPathDepth ?? MAX_PATH_DEPTH;\n this.cache = cache;\n }\n\n /**\n * Finds the best anchor element for the target\n * @param target - Target element to find anchor for\n * @returns Anchor result or null if not found\n */\n findAnchor(target: Element): AnchorResult | null {\n // Check cache first\n if (this.cache) {\n const cached = this.cache.getAnchor(target);\n if (cached !== undefined) {\n return cached;\n }\n }\n\n let current: Element | null = target.parentElement;\n let depth = 0;\n let bestCandidate: AnchorResult | null = null;\n\n while (current && depth < this.maxDepth) {\n // Check for body - stop here if reached (SPECIFICATION.md §7)\n if (current.tagName.toLowerCase() === 'body') {\n // Return best candidate if found, otherwise return body as degraded anchor\n if (bestCandidate) {\n return bestCandidate;\n }\n return {\n element: current,\n score: ANCHOR_SCORE.DEGRADED_SCORE,\n tier: 'C',\n depth,\n };\n }\n\n const rawScore = this.scoreAnchor(current);\n\n if (rawScore > 0) {\n // Apply depth penalty (SPECIFICATION.md §7)\n const score = this.applyDepthPenalty(rawScore, depth);\n const tier = this.getTier(current);\n const candidate: AnchorResult = { element: current, score, tier, depth };\n\n // Tier A is always best, stop immediately\n if (tier === 'A') {\n return candidate;\n }\n\n // Keep best candidate\n if (!bestCandidate || score > bestCandidate.score) {\n bestCandidate = candidate;\n }\n }\n\n current = current.parentElement;\n depth++;\n }\n\n const result = bestCandidate;\n\n // Cache the result\n if (this.cache) {\n this.cache.setAnchor(target, result);\n }\n\n return result;\n }\n\n /**\n * Scores an element as anchor candidate (without depth penalty)\n * @param element - Element to score\n * @returns Raw score from 0 to 1\n */\n scoreAnchor(element: Element): number {\n let score = 0;\n const tag = element.tagName.toLowerCase();\n\n // Tier A: Native semantic tags (highest weight)\n if (SEMANTIC_ANCHOR_TAGS.includes(tag)) {\n score += ANCHOR_SCORE.SEMANTIC_TAG;\n }\n\n // Tier B: Role-based semantics\n const role = element.getAttribute('role');\n if (role && ROLE_ANCHOR_VALUES.includes(role)) {\n score += ANCHOR_SCORE.ROLE;\n }\n\n // ARIA attributes bonus\n if (\n element.hasAttribute('aria-label') ||\n element.hasAttribute('aria-labelledby')\n ) {\n score += ANCHOR_SCORE.ARIA_LABEL;\n }\n\n // Stable ID bonus\n const id = element.id;\n if (id && !isDynamicId(id)) {\n score += ANCHOR_SCORE.STABLE_ID;\n }\n\n // Tier C: Test markers (lower score)\n if (\n element.hasAttribute('data-testid') ||\n element.hasAttribute('data-qa') ||\n element.hasAttribute('data-test')\n ) {\n score += ANCHOR_SCORE.TEST_MARKER;\n }\n\n return Math.min(score, 1.0);\n }\n\n /**\n * Applies depth penalty to score\n * Following SPECIFICATION.md §7: depthPenalty = (depth - threshold) * factor\n */\n private applyDepthPenalty(score: number, depth: number): number {\n if (depth <= ANCHOR_SCORE.DEPTH_PENALTY_THRESHOLD) {\n return score;\n }\n\n const penalty =\n (depth - ANCHOR_SCORE.DEPTH_PENALTY_THRESHOLD) *\n ANCHOR_SCORE.DEPTH_PENALTY_FACTOR;\n\n return Math.max(0, score - penalty);\n }\n\n /**\n * Determines the tier of an anchor element\n */\n private getTier(element: Element): 'A' | 'B' | 'C' {\n const tag = element.tagName.toLowerCase();\n\n if (SEMANTIC_ANCHOR_TAGS.includes(tag)) {\n return 'A';\n }\n\n const role = element.getAttribute('role');\n if (role && ROLE_ANCHOR_VALUES.includes(role)) {\n return 'B';\n }\n\n return 'C';\n }\n}\n","/**\n * Class classification for CSS selector generation\n * Categorizes classes into dynamic, utility, and semantic classes\n */\n\n/**\n * Classification result for a CSS class\n */\nexport interface ClassClassification {\n /** Whether class is dynamically generated (CSS-in-JS, hashes) */\n isDynamic: boolean;\n /** Whether class is a utility class (Tailwind, Bootstrap, animations) */\n isUtility: boolean;\n /** Whether class has semantic meaning */\n isSemantic: boolean;\n /** Whether class is stable (not dynamic and not utility) */\n isStable: boolean;\n}\n\n/**\n * Patterns for detecting dynamically generated classes\n * These classes should be IGNORED in selectors\n */\nconst DYNAMIC_CLASS_PATTERNS = [\n // CSS-in-JS\n /^css-[a-z0-9]+$/i,\n /^sc-[a-z0-9]+-\\d+$/i,\n /^[a-z]{5,8}$/i, // Short generated classes (abcdef)\n\n // Material-UI / MUI\n /^Mui[A-Z]\\w+-\\w+-\\w+/,\n /^makeStyles-\\w+-\\d+$/,\n\n // JSS\n /^jss\\d+$/,\n\n // Emotion / Linaria\n /^(emotion|linaria)-[a-z0-9]+/i,\n\n // Component libraries with hashes\n /^(chakra|tw-|ant-)[a-z0-9]+-\\w+/i,\n\n // Hash-based (hashes in classes)\n /-[a-f0-9]{6,}$/i,\n /^_[a-z0-9]{5,}$/i,\n /\\d{5,}/, // 5+ digits in a row\n];\n\n/**\n * Patterns for detecting utility classes\n * These classes should be IGNORED in selectors\n */\nconst UTILITY_CLASS_PATTERNS = [\n // === FIX 4: Tailwind arbitrary values and variants (highest priority) ===\n /^\\[/, // Any arbitrary value or variant starting with [ (e.g., [&_svg]:..., [mask-type:luminance])\n\n // === FIX 4: Pseudo-class variants (must be before specific patterns) ===\n /^(first|last|odd|even|only|first-of-type|last-of-type|only-of-type):/, // first:, last:, etc.\n /^(hover|focus|active|disabled|enabled|checked|indeterminate|default|required|valid|invalid|in-range|out-of-range|placeholder-shown|autofill|read-only):/, // State pseudo-classes\n /^(focus-within|focus-visible|visited|target|open):/, // Advanced pseudo-classes\n\n // === FIX 4: Responsive variants (must be before specific patterns) ===\n /^(sm|md|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl):/,\n\n // === FIX 4: Dark mode and directional variants ===\n /^dark:/,\n /^(rtl|ltr):/,\n\n // === FIX 4: Group and peer variants ===\n /^(group|peer)(-hover|-focus|-active)?:/,\n\n // === FIX 4: Tailwind utilities with fraction values ===\n /\\/([\\d.]+|full|auto|screen)$/, // /50, /100, /full, /auto, /screen\n\n // === FIX 4: Positioning utilities ===\n /^(inset|top|right|bottom|left)(-|$)/, // inset-0, top-0, left-0\n\n // === Layout & Display ===\n /^(flex|inline-flex|grid|block|inline|inline-block|hidden|visible)$/,\n /^(absolute|relative|fixed|sticky|static)$/,\n\n // === Flexbox & Grid ===\n /^(items|justify|content|self|place)-/,\n /^flex-(row|col|wrap|nowrap|1|auto|initial|none)/,\n /^grid-(cols|rows|flow)/,\n\n // === Spacing (Tailwind) ===\n /^(gap|space)-/,\n /^[mp][trblxy]?-(\\d+|auto|px)$/,\n\n // === Negative Tailwind utilities (margins, positioning, transforms) ===\n /^-[mp][trblxy]?-\\d+$/, // -m-4, -mt-2, -mx-4, -px-4, -py-2\n /^-(top|right|bottom|left|inset)-\\d+$/, // -top-4, -bottom-6, -left-6, -inset-0\n /^-z-\\d+$/, // -z-10, -z-20\n /^-space-[xy]-\\d+$/, // -space-x-2, -space-y-4\n /^-translate-[xy]-\\d+$/, // -translate-x-4, -translate-y-2\n /^-rotate-\\d+$/, // -rotate-45, -rotate-90\n /^-scale-\\d+$/, // -scale-50, -scale-75\n /^-skew-[xy]-\\d+$/, // -skew-x-12, -skew-y-6\n\n // === Sizing ===\n /^(w|h|min-w|min-h|max-w|max-h|size)-/,\n\n // === Colors & Styling ===\n // Note: text-* can be semantic (text-muted, text-primary) or utility (text-center, text-lg)\n // More specific patterns for utility text-* classes\n /^text-(center|left|right|justify|start|end|xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/,\n /^text-(uppercase|lowercase|capitalize|normal-case|underline|line-through|no-underline)$/,\n /^text-(truncate|ellipsis|clip)$/,\n /^(bg|border|ring|shadow|outline)-/,\n /^rounded(-|$)/,\n\n // === Typography ===\n /^(font|leading|tracking|whitespace|break|truncate)-/,\n /^(uppercase|lowercase|capitalize|normal-case)$/,\n\n // === Transform & Animation (IMPORTANT!) ===\n /^(transform|transition|duration|delay|ease|animate)-/,\n /^(scale|rotate|translate|skew)-/,\n /^transform$/,\n /^backdrop-blur-/,\n /^motion-/, // Framer Motion\n /^(fade|slide|zoom|bounce|pulse|spin|ping)-/, // animations\n\n // === Overflow & Scrolling ===\n /^(overflow|overscroll|scroll)-/,\n /^object-(contain|cover|fill|none|scale-down)$/,\n\n // === Interactivity ===\n /^(cursor|pointer-events|select|resize)-/,\n\n // === Visibility & Opacity ===\n /^(opacity|z)-/,\n /^(visible|invisible|collapse)$/,\n\n // === Bootstrap utilities ===\n /^d-(none|inline|inline-block|block|grid|table|flex)$/,\n /^(float|clearfix|text)-(left|right|center|justify|start|end)$/,\n /^(m|p)[trblxy]?-[0-5]$/,\n /^(w|h)-(25|50|75|100|auto)$/,\n // Note: btn-* classes are semantic (component classes), not utility\n // /^btn-(primary|secondary|success|danger|warning|info|light|dark|link)$/,\n /^btn-(sm|lg|block)$/, // Only size modifiers are utility\n /^text-(muted|primary|success|danger|warning|info|light|dark|white)$/,\n /^bg-(primary|secondary|success|danger|warning|info|light|dark|white|transparent)$/,\n /^border(-top|-bottom|-left|-right)?(-0)?$/,\n /^rounded(-top|-bottom|-left|-right|-circle|-pill|-0)?$/,\n /^shadow(-sm|-lg|-none)?$/,\n /^(align|justify|order|flex)-(start|end|center|between|around|fill|grow|shrink)$/,\n /^col(-sm|-md|-lg|-xl)?(-\\d+|-auto)?$/,\n /^row(-cols)?(-\\d+)?$/,\n /^g[xy]?-[0-5]$/,\n /^(show|hide|invisible|visible)$/,\n /^(position|top|bottom|start|end)-(static|relative|absolute|fixed|sticky|-\\d+)$/,\n\n // === Common utility patterns ===\n /^(row|col)$/,\n /^clearfix$/,\n /^pull-(left|right)$/,\n /^float-(left|right|none)$/,\n];\n\n/**\n * Patterns for detecting semantic classes\n * These classes should be USED in selectors\n */\nconst SEMANTIC_CLASS_PATTERNS = [\n // === Navigation ===\n /^(nav|menu|header|footer|sidebar|topbar|navbar|breadcrumb)/,\n /(navigation|dropdown|megamenu)$/,\n\n // === Components ===\n /^(btn|button|link|card|modal|dialog|popup|tooltip|alert|badge|chip)/,\n /^(form|input|select|checkbox|radio|textarea|label|fieldset)/,\n /^(table|list|item|row|cell|column)/,\n /^(accordion|tab|carousel|slider|gallery)/,\n\n // === Content ===\n /^(content|main|article|post|comment|title|subtitle|description|caption)/,\n /^(hero|banner|jumbotron|section|wrapper|box)/,\n\n // === User/Data ===\n /^(user|profile|avatar|account|auth)/,\n /^(product|item|price|cart|checkout|order)/,\n\n // === Layout sections ===\n /^(page|layout|panel|widget|block)/,\n\n // === States (semantic naming) ===\n /-(primary|secondary|tertiary|success|error|warning|info|danger)$/,\n /-(active|inactive|disabled|enabled|selected|highlighted|focused)$/,\n /-(open|closed|expanded|collapsed|visible|hidden)$/,\n /-(large|medium|small|tiny|xs|sm|md|lg|xl)$/,\n\n // === Action buttons ===\n /^(submit|cancel|close|delete|edit|save|back|next|prev|search)/,\n\n // === Status ===\n /^(loading|pending|complete|failed|draft|published)/,\n];\n\n/**\n * Classifies a CSS class into categories\n * @param className - Class name to classify\n * @returns Classification result\n */\nexport function classifyClass(className: string): ClassClassification {\n const isDynamic = isDynamicClass(className);\n const isUtility = isUtilityClass(className);\n const isSemantic = isSemanticClass(className);\n const isStable = !isDynamic && !isUtility;\n\n return {\n isDynamic,\n isUtility,\n isSemantic,\n isStable,\n };\n}\n\n/**\n * Checks if class is dynamically generated (CSS-in-JS, hashes)\n * @param className - Class name to check\n * @returns True if dynamic class\n */\nexport function isDynamicClass(className: string): boolean {\n return DYNAMIC_CLASS_PATTERNS.some((pattern) => pattern.test(className));\n}\n\n/**\n * Checks if class is a utility class (Tailwind, Bootstrap, animations)\n * @param className - Class name to check\n * @returns True if utility class\n */\nexport function isUtilityClass(className: string): boolean {\n // Very short classes are often utility\n if (className.length <= 2) return true;\n\n // Classes that are just numbers or start with numbers\n if (/^\\d/.test(className)) return true;\n\n // Check utility patterns\n return UTILITY_CLASS_PATTERNS.some((pattern) => pattern.test(className));\n}\n\n/**\n * Checks if class has semantic meaning\n * @param className - Class name to check\n * @returns True if semantic class\n */\nexport function isSemanticClass(className: string): boolean {\n // Must not be dynamic or utility\n if (isDynamicClass(className) || isUtilityClass(className)) {\n return false;\n }\n\n // Check semantic patterns\n return SEMANTIC_CLASS_PATTERNS.some((pattern) => pattern.test(className));\n}\n\n/**\n * Checks if class is stable (not dynamic and not utility)\n * @param className - Class name to check\n * @returns True if stable class\n */\nexport function isStableClass(className: string): boolean {\n return !isDynamicClass(className) && !isUtilityClass(className);\n}\n\n/**\n * Filters classes to keep only stable ones (not dynamic and not utility)\n * @param classes - Array of class names\n * @returns Array of stable class names\n */\nexport function filterStableClasses(classes: string[]): string[] {\n return classes.filter((cls) => isStableClass(cls));\n}\n\n/**\n * Filters classes to keep only semantic ones\n * @param classes - Array of class names\n * @returns Array of semantic class names\n */\nexport function filterSemanticClasses(classes: string[]): string[] {\n return classes.filter((cls) => isSemanticClass(cls));\n}\n\n/**\n * Scores a class for semantic value (0-1)\n * Higher score = more semantic value\n * @param className - Class name to score\n * @returns Score from 0 to 1\n */\nexport function scoreClass(className: string): number {\n // Dynamic or utility classes get 0\n if (isDynamicClass(className) || isUtilityClass(className)) {\n return 0;\n }\n\n let score = 0.5; // Base score for stable classes\n\n // Semantic classes get higher score\n if (isSemanticClass(className)) {\n score = 0.8;\n }\n\n // Short classes are less semantic\n if (className.length < 3) {\n score *= 0.3;\n } else if (className.length < 5) {\n score *= 0.6;\n }\n\n // Numeric parts reduce score (but not to 0 if already stable)\n if (/\\d/.test(className)) {\n score *= 0.7;\n }\n\n return Math.min(score, 1.0);\n}\n","import {\n isUtilityClass as isUtilityClassFromClassifier,\n isDynamicClass,\n scoreClass as scoreClassFromClassifier,\n} from './class-classifier';\n\n/**\n * Filters classes into semantic and utility\n * Uses class-classifier for improved detection\n * @param classes - Array of class names\n * @returns Object with semantic and utility arrays\n */\nexport function filterClasses(classes: string[]): {\n semantic: string[];\n utility: string[];\n} {\n const semantic: string[] = [];\n const utility: string[] = [];\n\n for (const cls of classes) {\n // Use new classifier logic\n if (isUtilityClassFromClassifier(cls) || isDynamicClass(cls)) {\n utility.push(cls);\n } else {\n semantic.push(cls);\n }\n }\n\n return { semantic, utility };\n}\n\n/**\n * Checks if class is a utility/atomic CSS class\n * Uses class-classifier for improved detection\n * @param className - Class name to check\n * @returns True if utility class\n */\nexport function isUtilityClass(className: string): boolean {\n // Use new classifier logic\n return isUtilityClassFromClassifier(className) || isDynamicClass(className);\n}\n\n/**\n * Scores a class for semantic value\n * Uses class-classifier for improved scoring\n * @param className - Class name to score\n * @returns Score from 0 to 1\n */\nexport function getClassScore(className: string): number {\n // Use new classifier scoring\n return scoreClassFromClassifier(className);\n}\n","import type { PathNode, GeneratorOptions } from '../types';\nimport type { SemanticExtractor } from './semantic-extractor';\nimport { SEMANTIC_TAGS, MAX_PATH_DEPTH, PATH_SCORE } from '../utils/constants';\nimport { isUtilityClass } from '../utils/class-filter';\nimport { isDynamicId } from '../utils/id-validator';\nimport type { EIDCache } from '../utils/eid-cache';\n\n// Fallback CSS escape function if not available\nconst cssEscape = (str: string) => {\n // Simplified CSS escape for special characters\n return str.replace(/([#:.[\\]@])/g, '\\\\$1');\n};\n\n/**\n * Result of path building including degradation info\n */\nexport interface PathBuildResult {\n path: PathNode[];\n degraded: boolean;\n degradationReason?: string;\n}\n\n/**\n * Builds semantic path from anchor to target\n * Following SPECIFICATION.md §8\n */\nexport class PathBuilder {\n private maxDepth: number;\n private cache?: EIDCache;\n\n constructor(options: GeneratorOptions, cache?: EIDCache) {\n this.maxDepth = options.maxPathDepth ?? MAX_PATH_DEPTH;\n this.cache = cache;\n }\n\n /**\n * Builds path from anchor to target (excluding both)\n * @param anchor - Anchor element (start)\n * @param target - Target element (end)\n * @param extractor - Semantic extractor instance\n * @returns Path build result with nodes and degradation info\n */\n buildPath(\n anchor: Element,\n target: Element,\n extractor: SemanticExtractor,\n ): PathBuildResult {\n const rawPath: Element[] = [];\n let current: Element | null = target.parentElement;\n\n // Traverse up from target to anchor\n while (current && current !== anchor && rawPath.length < this.maxDepth) {\n rawPath.unshift(current); // Add to beginning (we're going backwards)\n current = current.parentElement;\n }\n\n // Check for depth overflow (SPECIFICATION.md §8)\n const depthOverflow = rawPath.length >= this.maxDepth && current !== anchor;\n\n // Filter noise elements, keeping semantic ones\n let filteredPath = this.filterNoise(rawPath);\n\n // Check uniqueness and add disambiguation nodes if needed (SPECIFICATION.md §8)\n filteredPath = this.ensureUniqueness(\n rawPath,\n filteredPath,\n anchor,\n target,\n extractor,\n );\n\n // Convert to PathNodes\n const pathNodes = filteredPath.map((el) => {\n // Calculate nth-child position (1-based index)\n const parent = el.parentElement;\n let nthChild: number | undefined;\n\n if (parent) {\n const siblings = Array.from(parent.children);\n const index = siblings.indexOf(el);\n if (index !== -1) {\n nthChild = index + 1; // 1-based for CSS nth-child()\n }\n }\n\n return {\n tag: el.tagName.toLowerCase(),\n semantics: extractor.extract(el),\n score: extractor.scoreElement(el),\n nthChild,\n };\n });\n\n return {\n path: pathNodes,\n degraded: depthOverflow,\n degradationReason: depthOverflow ? 'path-depth-overflow' : undefined,\n };\n }\n\n /**\n * Legacy method for backward compatibility\n */\n buildPathNodes(\n anchor: Element,\n target: Element,\n extractor: SemanticExtractor,\n ): PathNode[] {\n return this.buildPath(anchor, target, extractor).path;\n }\n\n /**\n * Ensures path uniqueness by adding nodes if needed\n * Following SPECIFICATION.md §8 Disambiguation Algorithm\n */\n private ensureUniqueness(\n rawPath: Element[],\n filteredPath: Element[],\n anchor: Element,\n target: Element,\n extractor: SemanticExtractor,\n ): Element[] {\n // Build selector from current path and check uniqueness\n const selector = this.buildTestSelector(anchor, filteredPath, target);\n\n try {\n const doc = target.ownerDocument;\n if (!doc) return filteredPath;\n\n // Check cache first\n let matches: NodeListOf<Element> | Element[];\n if (this.cache) {\n const cached = this.cache.getSelectorResults(selector);\n if (cached !== undefined) {\n matches = cached;\n } else {\n matches = Array.from(doc.querySelectorAll(selector));\n this.cache.setSelectorResults(selector, matches);\n }\n } else {\n matches = doc.querySelectorAll(selector);\n }\n\n // If unique, return as-is\n if (matches.length <= 1) {\n return filteredPath;\n }\n\n // Not unique - try adding skipped nodes one by one\n const skippedNodes = rawPath.filter((el) => !filteredPath.includes(el));\n\n for (const node of skippedNodes) {\n // Only add if it has good semantic score\n const score = extractor.scoreElement(node);\n if (score < PATH_SCORE.MIN_CONFIDENCE_FOR_SKIP) {\n continue; // Skip low-value nodes\n }\n\n // Try adding this node\n const testPath = this.insertNodeInOrder(filteredPath, node, rawPath);\n const testSelector = this.buildTestSelector(anchor, testPath, target);\n\n try {\n // Check cache for test selector\n let testMatches: NodeListOf<Element> | Element[];\n if (this.cache) {\n const cached = this.cache.getSelectorResults(testSelector);\n if (cached !== undefined) {\n testMatches = cached;\n } else {\n testMatches = Array.from(doc.querySelectorAll(testSelector));\n this.cache.setSelectorResults(testSelector, testMatches);\n }\n } else {\n testMatches = doc.querySelectorAll(testSelector);\n }\n if (testMatches.length === 1) {\n return testPath; // Found unique path\n }\n if (testMatches.length < matches.length) {\n // Improved, continue with this path\n filteredPath = testPath;\n }\n } catch {\n // Invalid selector, skip this node\n }\n }\n\n return filteredPath;\n } catch {\n // querySelectorAll failed, return original\n return filteredPath;\n }\n }\n\n /**\n * Inserts node into path maintaining original order\n */\n private insertNodeInOrder(\n path: Element[],\n node: Element,\n rawPath: Element[],\n ): Element[] {\n const nodeIndex = rawPath.indexOf(node);\n const result = [...path];\n\n // Find insertion point\n let insertIndex = 0;\n for (let i = 0; i < result.length; i++) {\n const pathNodeIndex = rawPath.indexOf(result[i]);\n if (pathNodeIndex > nodeIndex) {\n break;\n }\n insertIndex = i + 1;\n }\n\n result.splice(insertIndex, 0, node);\n return result;\n }\n\n /**\n * Builds a test CSS selector from path\n */\n private buildTestSelector(\n anchor: Element,\n path: Element[],\n target: Element,\n ): string {\n const parts: string[] = [];\n\n // Anchor\n parts.push(this.elementToSelector(anchor));\n\n // Path\n for (const el of path) {\n parts.push(this.elementToSelector(el));\n }\n\n // Target\n parts.push(this.elementToSelector(target));\n\n return parts.join(' ');\n }\n\n /**\n * Converts element to basic CSS selector\n */\n private elementToSelector(element: Element): string {\n let selector = element.tagName.toLowerCase();\n\n if (element.id && !isDynamicId(element.id)) {\n selector += `#${cssEscape(element.id)}`;\n }\n\n for (const cls of Array.from(element.classList)) {\n if (!isUtilityClass(cls)) {\n selector += `.${cssEscape(cls)}`;\n }\n }\n\n return selector;\n }\n\n /**\n * Filters out noise/layout elements\n */\n private filterNoise(elements: Element[]): Element[] {\n return elements.filter((el) => this.shouldInclude(el));\n }\n\n /**\n * Determines if element should be included in path\n */\n shouldInclude(element: Element): boolean {\n const tag = element.tagName.toLowerCase();\n\n // Always include semantic HTML tags\n if (SEMANTIC_TAGS.includes(tag)) {\n return true;\n }\n\n // div/span need additional semantic features to be included\n if (tag === 'div' || tag === 'span') {\n return this.hasSemanticFeatures(element);\n }\n\n return false;\n }\n\n /**\n * Checks if element has meaningful semantic features\n */\n private hasSemanticFeatures(element: Element): boolean {\n // Has role attribute\n if (element.hasAttribute('role')) return true;\n\n // Has ARIA attributes\n for (const attr of Array.from(element.attributes)) {\n if (attr.name.startsWith('aria-')) return true;\n }\n\n // Has semantic classes (not just utility)\n if (element.classList.length > 0) {\n for (const cls of Array.from(element.classList)) {\n if (!isUtilityClass(cls)) return true;\n }\n }\n\n // Has data-testid or similar\n if (\n element.hasAttribute('data-testid') ||\n element.hasAttribute('data-qa') ||\n element.hasAttribute('data-test')\n ) {\n return true;\n }\n\n // Has stable ID\n const id = element.id;\n if (id && !isDynamicId(id)) {\n return true;\n }\n\n return false;\n }\n}\n","/**\n * Normalizes text for comparison\n * Following SPECIFICATION.md §11\n *\n * - Trims whitespace\n * - Collapses multiple spaces\n * - Replaces newlines/tabs with spaces\n */\nexport function normalizeText(text: string | null | undefined): string {\n if (!text) return '';\n\n return text.trim().replace(/[\\n\\t\\r]/g, ' ').replace(/\\s+/g, ' ');\n}\n","/**\n * Attribute value cleaning for stable CSS selector generation\n * Cleans href/src attributes by removing dynamic query parameters and hashes\n */\n\n/**\n * Options for cleaning attribute values\n */\nexport interface CleanAttributeOptions {\n /** Preserve query parameters for absolute URLs */\n preserveQueryForAbsolute?: boolean; // default: true\n /** Remove dynamic hashes */\n removeDynamicHashes?: boolean; // default: true\n}\n\n/**\n * Default options for cleaning\n */\nconst DEFAULT_OPTIONS: Required<CleanAttributeOptions> = {\n preserveQueryForAbsolute: true,\n removeDynamicHashes: true,\n};\n\n/**\n * Checks if a hash value is dynamic (should be removed)\n * @param hash - Hash string to check (without #)\n * @returns True if hash is dynamic\n */\nfunction isDynamicHash(hash: string): boolean {\n if (!hash) return false;\n\n const dynamicPatterns = [\n /\\d{5,}/, // 5+ digits\n /[a-f0-9]{8,}/i, // hex hash 8+ characters\n /(session|token|temp|random|timestamp|nonce|cache)/i, // dynamic words\n /^\\d+$/, // only digits\n /^[a-f0-9-]{32,}$/i, // UUID-like\n ];\n\n return dynamicPatterns.some((p) => p.test(hash));\n}\n\n/**\n * Cleans URL value (href/src) by removing dynamic parts\n * @param value - URL value to clean\n * @param options - Cleaning options\n * @returns Cleaned URL value\n */\nfunction cleanUrlValue(\n value: string,\n options: Required<CleanAttributeOptions>,\n): string {\n if (!value) return value;\n\n const isAbsolute = value.startsWith('http://') || value.startsWith('https://');\n\n // Split into parts: base, query, hash\n let [baseWithQuery, hash] = value.split('#');\n const [base, query] = baseWithQuery.split('?');\n\n let cleaned = base;\n\n // Handle query parameters\n if (isAbsolute) {\n // For absolute URLs, preserve query if preserveQueryForAbsolute is true\n if (options.preserveQueryForAbsolute && query) {\n cleaned += `?${query}`;\n }\n }\n // For relative URLs, query is always removed (already handled by base)\n\n // Handle hash\n if (hash) {\n if (options.removeDynamicHashes && isDynamicHash(hash)) {\n // Remove dynamic hash\n } else {\n // Preserve non-dynamic hash\n cleaned += `#${hash}`;\n }\n }\n\n return cleaned;\n}\n\n/**\n * Cleans attribute value based on attribute name\n * Currently handles href and src attributes\n * @param attrName - Attribute name\n * @param value - Attribute value\n * @param options - Cleaning options\n * @returns Cleaned attribute value\n */\nexport function cleanAttributeValue(\n attrName: string,\n value: string,\n options: CleanAttributeOptions = {},\n): string {\n if (!value) return value;\n\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Only clean href and src attributes\n if (attrName === 'href' || attrName === 'src') {\n return cleanUrlValue(value, opts);\n }\n\n // For other attributes, return as-is\n return value;\n}\n","/**\n * Attribute filtering for stable element identification\n * Separates stable identity attributes from temporary state attributes\n *\n * PHILOSOPHY: SEQL selectors identify elements by their semantic identity,\n * not their current state. An element is the same whether it's active/inactive,\n * visible/hidden, expanded/collapsed.\n */\n\n/**\n * ARIA attributes that represent stable element identity/semantics\n */\nexport const ARIA_STABLE_ATTRIBUTES = [\n 'role',\n 'aria-label',\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-controls',\n 'aria-owns',\n 'aria-level',\n 'aria-posinset',\n 'aria-setsize',\n 'aria-haspopup',\n] as const;\n\n/**\n * ARIA attributes that represent temporary element state\n */\nexport const ARIA_STATE_ATTRIBUTES = [\n 'aria-selected',\n 'aria-checked',\n 'aria-pressed',\n 'aria-expanded',\n 'aria-hidden',\n 'aria-disabled',\n 'aria-current',\n 'aria-busy',\n 'aria-invalid',\n 'aria-grabbed',\n 'aria-live',\n 'aria-atomic',\n] as const;\n\n/**\n * data-* attributes representing temporary state\n */\nexport const DATA_STATE_ATTRIBUTES = [\n 'data-state',\n 'data-active',\n 'data-inactive',\n 'data-selected',\n 'data-open',\n 'data-closed',\n 'data-visible',\n 'data-hidden',\n 'data-disabled',\n 'data-enabled',\n 'data-loading',\n 'data-error',\n 'data-success',\n 'data-highlighted',\n 'data-focused',\n 'data-hover',\n 'data-orientation',\n 'data-theme',\n] as const;\n\n/**\n * Library-specific data-* prefixes to exclude\n */\nexport const LIBRARY_DATA_PREFIXES = [\n 'data-radix-',\n 'data-headlessui-',\n 'data-reach-',\n 'data-mui-',\n 'data-chakra-',\n 'data-mantine-',\n 'data-tw-',\n] as const;\n\n/**\n * data-* patterns that represent stable IDs\n */\nexport const DATA_ID_PATTERNS = [\n 'data-testid',\n 'data-test-id',\n 'data-test',\n 'data-cy',\n 'data-qa',\n 'data-automation-id',\n 'data-id',\n 'data-component',\n 'data-entity-id',\n 'data-product-id',\n 'data-user-id',\n] as const;\n\n/**\n * Standard HTML attributes that are stable\n */\nexport const HTML_STABLE_ATTRIBUTES = [\n 'id',\n 'name',\n 'type',\n 'placeholder',\n 'title',\n 'for',\n 'alt',\n 'href',\n] as const;\n\n/**\n * Standard HTML attributes that represent state\n */\nexport const HTML_STATE_ATTRIBUTES = [\n 'disabled',\n 'checked',\n 'selected',\n 'hidden',\n 'readonly',\n 'required',\n 'value',\n] as const;\n\n/**\n * Patterns for generated IDs that should be excluded\n */\nexport const GENERATED_ID_PATTERNS = [\n /^radix-/,\n /^headlessui-/,\n /^mui-/,\n /:\\w+:/, // matches :ru:, :r1:, etc.\n] as const;\n\n/**\n * Determines if an attribute represents stable element identity\n * @param name - attribute name\n * @param value - attribute value\n * @returns true if attribute should be included in SEQL selector\n */\nexport function isStableAttribute(name: string, value: string): boolean {\n // Whitelist stable ARIA attributes\n if (ARIA_STABLE_ATTRIBUTES.includes(name as any)) return true;\n\n // Blacklist ARIA state attributes\n if (ARIA_STATE_ATTRIBUTES.includes(name as any)) return false;\n\n // Blacklist data-* state attributes\n if (DATA_STATE_ATTRIBUTES.includes(name as any)) return false;\n\n // Blacklist library-specific data-* prefixes\n if (LIBRARY_DATA_PREFIXES.some(prefix => name.startsWith(prefix))) {\n return false;\n }\n\n // Whitelist data-* ID patterns (exact match)\n if (DATA_ID_PATTERNS.includes(name as any)) return true;\n\n // Whitelist data-* ending with -id\n if (name.startsWith('data-') && name.endsWith('-id')) return true;\n\n // Filter generated IDs by pattern\n if (name === 'id') {\n if (GENERATED_ID_PATTERNS.some(pattern => pattern.test(value))) {\n return false;\n }\n return true;\n }\n\n // Whitelist stable HTML attributes\n if (HTML_STABLE_ATTRIBUTES.includes(name as any)) return true;\n\n // Blacklist HTML state attributes\n if (HTML_STATE_ATTRIBUTES.includes(name as any)) return false;\n\n // Allow other data-* attributes by default (blacklist approach)\n if (name.startsWith('data-')) return true;\n\n // Reject unknown attributes\n return false;\n}\n","import type { ElementSemantics, TextContent, GeneratorOptions } from '../types';\nimport { normalizeText } from '../utils/text-normalizer';\nimport { filterClasses } from '../utils/class-filter';\nimport { isDynamicId, ID_REFERENCE_ATTRIBUTES, hasDynamicIdReference } from '../utils/id-validator';\nimport { ATTRIBUTE_PRIORITY, IGNORED_ATTRIBUTES } from '../utils/constants';\nimport { cleanAttributeValue } from '../utils/attribute-cleaner';\nimport { isStableAttribute } from '../utils/attribute-filters';\nimport type { EIDCache } from '../utils/eid-cache';\n\n/**\n * Extracts semantic features from DOM elements\n * Following SPECIFICATION.md §10\n */\nexport class SemanticExtractor {\n private includeUtilityClasses: boolean;\n private cache?: EIDCache;\n\n constructor(options: GeneratorOptions, cache?: EIDCache) {\n this.includeUtilityClasses = options.includeUtilityClasses ?? false;\n this.cache = cache;\n }\n\n /**\n * Extracts semantic features from element\n * @param element - DOM element to extract from\n * @returns Semantic features object\n */\n extract(element: Element): ElementSemantics {\n // Check cache first\n if (this.cache) {\n const cached = this.cache.getSemantics(element);\n if (cached !== undefined) {\n return cached;\n }\n }\n\n const semantics: ElementSemantics = {};\n\n // ID (if stable)\n const id = element.id;\n if (id && !isDynamicId(id)) {\n semantics.id = id;\n }\n\n // Classes (filtered)\n if (element.classList.length > 0) {\n const classes = Array.from(element.classList);\n if (this.includeUtilityClasses) {\n semantics.classes = classes;\n } else {\n const { semantic } = filterClasses(classes);\n if (semantic.length > 0) {\n semantics.classes = semantic;\n }\n }\n }\n\n // Semantic attributes\n const attrs = this.extractAttributes(element);\n if (Object.keys(attrs).length > 0) {\n semantics.attributes = attrs;\n }\n\n // Role\n const role = element.getAttribute('role');\n if (role) {\n semantics.role = role;\n }\n\n // Text content (for certain elements)\n if (this.shouldExtractText(element)) {\n const text = this.extractText(element);\n if (text) {\n semantics.text = text;\n }\n }\n\n // Cache the result\n if (this.cache) {\n this.cache.setSemantics(element, semantics);\n }\n\n return semantics;\n }\n\n /**\n * Scores element based on semantic richness\n * @param element - Element to score\n * @returns Score from 0 to 1\n */\n scoreElement(element: Element): number {\n let score = 0.5; // Base score\n\n const semantics = this.extract(element);\n\n if (semantics.id) score += 0.15;\n if (semantics.classes && semantics.classes.length > 0) score += 0.1;\n if (semantics.attributes && Object.keys(semantics.attributes).length > 0) {\n score += 0.1;\n }\n if (semantics.role) score += 0.1;\n if (semantics.text) score += 0.05;\n\n return Math.min(score, 1.0);\n }\n\n /**\n * Checks if attribute should be ignored\n * @param attrName - Attribute name\n * @returns True if should be ignored\n */\n private shouldIgnoreAttribute(attrName: string): boolean {\n // Ignored attributes\n if (IGNORED_ATTRIBUTES.has(attrName)) return true;\n\n // Inline event handlers\n if (attrName.startsWith('on')) return true;\n\n // Angular/React service attributes\n if (attrName.startsWith('ng-') || attrName.startsWith('_ng')) return true;\n if (attrName.startsWith('data-reactid') || attrName.startsWith('data-react'))\n return true;\n if (attrName.startsWith('data-v-')) return true; // Vue scoped styles\n\n return false;\n }\n\n /**\n * Gets attribute priority\n * @param attrName - Attribute name\n * @returns Priority number (higher = more priority)\n */\n private getAttributePriority(attrName: string): number {\n // Exact match\n if (ATTRIBUTE_PRIORITY[attrName] !== undefined) {\n return ATTRIBUTE_PRIORITY[attrName];\n }\n\n // data-* wildcard\n if (attrName.startsWith('data-')) {\n return ATTRIBUTE_PRIORITY['data-*'];\n }\n\n // aria-* wildcard\n if (attrName.startsWith('aria-')) {\n return ATTRIBUTE_PRIORITY['aria-*'];\n }\n\n return 0; // Don't use in selector\n }\n\n /**\n * Checks if attribute value is dynamic (should be ignored)\n * @param value - Attribute value\n * @returns True if value is dynamic\n */\n private isDynamicValue(value: string): boolean {\n const dynamicPatterns = [\n /^[a-f0-9]{32,}$/i, // Long hashes\n /^\\d{10,}$/, // Timestamp\n /^(undefined|null|\\[object)/, // JS artifacts\n /^{{.*}}$/, // Template literals\n ];\n\n return dynamicPatterns.some((p) => p.test(value));\n }\n\n /**\n * Extracts relevant semantic attributes from element\n * Iterates through all attributes and filters by priority\n */\n private extractAttributes(element: Element): Record<string, string> {\n const attrs: Record<string, string> = {};\n\n // Iterate through all attributes (not just SEMANTIC_ATTRIBUTES)\n for (const attr of Array.from(element.attributes)) {\n const name = attr.name;\n\n // Skip ignored attributes\n if (this.shouldIgnoreAttribute(name)) continue;\n\n // Skip unstable/state-based attributes (e.g., aria-selected, data-state, disabled)\n if (!isStableAttribute(name, attr.value)) continue;\n\n // Skip ID-reference attributes with dynamic IDs\n if (ID_REFERENCE_ATTRIBUTES.has(name) && hasDynamicIdReference(attr.value)) {\n continue;\n }\n\n // Get priority\n const priority = this.getAttributePriority(name);\n if (priority === 0) continue;\n\n // Clean value for href/src\n const value =\n name === 'href' || name === 'src'\n ? cleanAttributeValue(name, attr.value)\n : attr.value;\n\n // Skip empty values\n if (!value || value.trim() === '') continue;\n\n // Skip dynamic values\n if (this.isDynamicValue(value)) continue;\n\n attrs[name] = value;\n }\n\n return attrs;\n }\n\n /**\n * Extracts and normalizes text content\n */\n private extractText(element: Element): TextContent | null {\n // Get direct text content, not from children\n const rawText = this.getDirectTextContent(element);\n if (!rawText) return null;\n\n const normalized = normalizeText(rawText);\n if (!normalized) return null;\n\n // Limit text length for performance\n const maxLength = 100;\n const truncatedRaw =\n rawText.length > maxLength ? rawText.slice(0, maxLength) + '...' : rawText;\n const truncatedNorm =\n normalized.length > maxLength\n ? normalized.slice(0, maxLength) + '...'\n : normalized;\n\n return {\n raw: truncatedRaw,\n normalized: truncatedNorm,\n };\n }\n\n /**\n * Gets direct text content excluding child elements\n */\n private getDirectTextContent(element: Element): string | null {\n const texts: string[] = [];\n\n for (const node of Array.from(element.childNodes)) {\n if (node.nodeType === Node.TEXT_NODE && node.textContent) {\n const trimmed = node.textContent.trim();\n if (trimmed) {\n texts.push(trimmed);\n }\n }\n }\n\n // Return direct text if found, otherwise fallback to textContent (with null safety)\n return texts.length > 0 ? texts.join(' ') : (element.textContent ?? null);\n }\n\n /**\n * Determines if text should be extracted for this element\n */\n private shouldExtractText(element: Element): boolean {\n const tag = element.tagName.toLowerCase();\n // Extract text for interactive and semantic text elements\n return [\n 'button',\n 'a',\n 'label',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'p',\n 'span',\n 'li',\n 'th',\n 'td',\n 'dt',\n 'dd',\n 'legend',\n 'figcaption',\n 'summary',\n ].includes(tag);\n }\n\n}\n","import type { SvgFingerprint } from '../types';\n\n/**\n * Generates stable fingerprints for SVG elements\n * Following SPECIFICATION.md §9\n */\nexport class SvgFingerprinter {\n /**\n * Generates fingerprint for SVG element\n * @param element - SVG element to fingerprint\n * @returns SVG fingerprint object\n */\n fingerprint(element: SVGElement): SvgFingerprint {\n const tag = element.tagName.toLowerCase();\n const shape = this.getShape(tag);\n\n const fingerprint: SvgFingerprint = {\n shape,\n hasAnimation: this.hasAnimation(element),\n };\n\n // Path-specific hash\n if (shape === 'path') {\n const d = element.getAttribute('d');\n if (d) {\n fingerprint.dHash = this.computePathHash(d);\n }\n } else if (['circle', 'rect', 'ellipse', 'line'].includes(shape)) {\n fingerprint.geomHash = this.computeGeomHash(element, shape);\n }\n\n // ARIA role\n const role = element.getAttribute('role');\n if (role) {\n fingerprint.role = role;\n }\n\n // Title text (accessibility)\n const title = element.querySelector('title');\n if (title?.textContent) {\n fingerprint.titleText = title.textContent.trim();\n }\n\n return fingerprint;\n }\n\n /**\n * Computes hash from path data (first N commands)\n * @param d - SVG path d attribute value\n * @returns Hash string\n */\n computePathHash(d: string): string {\n const normalized = this.normalizePathData(d);\n return this.simpleHash(normalized);\n }\n\n /**\n * Gets the shape type from tag name\n */\n private getShape(tag: string): SvgFingerprint['shape'] {\n const shapes: SvgFingerprint['shape'][] = [\n 'path',\n 'circle',\n 'rect',\n 'line',\n 'polyline',\n 'polygon',\n 'ellipse',\n 'g',\n 'text',\n 'use',\n 'svg',\n ];\n return shapes.find((s) => s === tag) ?? 'path';\n }\n\n /**\n * Normalizes path data for consistent hashing\n */\n private normalizePathData(d: string): string {\n // Split into commands, take first 5 for fingerprint\n const commands = d.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/gi) ?? [];\n const firstN = commands.slice(0, 5);\n\n // Normalize: trim, round numbers to 1 decimal\n return firstN\n .map((cmd) => {\n return cmd.trim().replace(/(-?\\d+\\.?\\d*)/g, (match) => {\n return parseFloat(match).toFixed(1);\n });\n })\n .join(' ');\n }\n\n /**\n * Computes geometry hash for non-path shapes\n */\n private computeGeomHash(element: SVGElement, shape: string): string {\n const attrs: string[] = [];\n\n switch (shape) {\n case 'circle':\n // Use radius ratio for scale independence\n attrs.push(`r=${element.getAttribute('r') ?? '0'}`);\n break;\n\n case 'rect':\n // Use aspect ratio for scale independence\n {\n const w = parseFloat(element.getAttribute('width') ?? '0');\n const h = parseFloat(element.getAttribute('height') ?? '0');\n if (w > 0 && h > 0) {\n attrs.push(`ratio=${(w / h).toFixed(2)}`);\n }\n }\n break;\n\n case 'ellipse':\n // Use radii ratio\n {\n const rx = parseFloat(element.getAttribute('rx') ?? '0');\n const ry = parseFloat(element.getAttribute('ry') ?? '0');\n if (rx > 0 && ry > 0) {\n attrs.push(`ratio=${(rx / ry).toFixed(2)}`);\n }\n }\n break;\n\n case 'line':\n // Use angle/direction\n {\n const x1 = parseFloat(element.getAttribute('x1') ?? '0');\n const y1 = parseFloat(element.getAttribute('y1') ?? '0');\n const x2 = parseFloat(element.getAttribute('x2') ?? '0');\n const y2 = parseFloat(element.getAttribute('y2') ?? '0');\n const angle = Math.atan2(y2 - y1, x2 - x1);\n attrs.push(`angle=${angle.toFixed(2)}`);\n }\n break;\n }\n\n return this.simpleHash(attrs.join(';'));\n }\n\n /**\n * Detects animations on SVG element\n */\n hasAnimation(element: SVGElement): boolean {\n // Check for SMIL animation elements\n if (element.querySelector('animate, animateTransform, animateMotion')) {\n return true;\n }\n\n // Check for CSS animations\n const doc = element.ownerDocument;\n if (doc?.defaultView) {\n try {\n const style = doc.defaultView.getComputedStyle(element);\n if (\n style.animationName !== 'none' ||\n (style.transitionProperty !== 'all' &&\n style.transitionProperty !== 'none')\n ) {\n return true;\n }\n } catch {\n // getComputedStyle may fail in some contexts\n }\n }\n\n return false;\n }\n\n /**\n * Simple hash function for fingerprinting (not cryptographic)\n */\n private simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n }\n}\n","import type { ElementIdentity } from '../types';\nimport { CONFIDENCE_WEIGHTS } from './constants';\n\n/**\n * Calculates overall confidence score for Element Identity\n * Following SPECIFICATION.md §13 confidence formula\n *\n * @param eid - The Element Identity to score\n * @param uniquenessBonus - Bonus for unique resolution (0-1), determined during resolution\n * @returns Confidence score from 0 to 1\n */\nexport function calculateConfidence(\n eid: ElementIdentity,\n uniquenessBonus: number = 0,\n): number {\n const anchorScore = eid.anchor.score;\n\n const avgPathScore =\n eid.path.length > 0\n ? eid.path.reduce((sum, n) => sum + n.score, 0) / eid.path.length\n : 0.5;\n\n const targetScore = eid.target.score;\n\n // Weighted sum formula using constants\n const confidence =\n anchorScore * CONFIDENCE_WEIGHTS.ANCHOR +\n avgPathScore * CONFIDENCE_WEIGHTS.PATH +\n targetScore * CONFIDENCE_WEIGHTS.TARGET +\n uniquenessBonus * CONFIDENCE_WEIGHTS.UNIQUENESS;\n\n // Degradation penalty\n const degradationPenalty = eid.anchor.degraded ? 0.2 : 0;\n\n return Math.max(0, Math.min(1, confidence - degradationPenalty));\n}\n\n/**\n * Calculates score for a single element based on its semantic features\n * @param semanticsCount - Number of semantic features present\n * @param hasId - Whether element has stable ID\n * @param hasRole - Whether element has ARIA role\n * @returns Score from 0 to 1\n */\nexport function calculateElementScore(\n semanticsCount: number,\n hasId: boolean,\n hasRole: boolean,\n): number {\n let score = 0.5; // Base score\n\n if (hasId) score += 0.2;\n if (hasRole) score += 0.15;\n\n // Each semantic feature adds to score\n score += Math.min(semanticsCount * 0.05, 0.15);\n\n return Math.min(score, 1.0);\n}\n","import type { ElementIdentity, ElementSemantics } from '../types';\nimport type { AnchorResult } from '../generator/anchor-finder';\n\n/**\n * LRU Cache implementation for selector results\n */\nclass LRUCache<K, V> {\n private cache: Map<K, V>;\n private maxSize: number;\n\n constructor(maxSize: number) {\n this.cache = new Map();\n this.maxSize = maxSize;\n }\n\n get(key: K): V | undefined {\n if (!this.cache.has(key)) return undefined;\n // Move to end (most recently used)\n const value = this.cache.get(key)!;\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n // Remove oldest (first) entry\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n delete(key: K): void {\n this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n/**\n * Cache statistics\n */\nexport interface CacheStats {\n /** Total number of EID cache hits */\n eidHits: number;\n /** Total number of EID cache misses */\n eidMisses: number;\n /** Total number of selector cache hits */\n selectorHits: number;\n /** Total number of selector cache misses */\n selectorMisses: number;\n /** Total number of anchor cache hits */\n anchorHits: number;\n /** Total number of anchor cache misses */\n anchorMisses: number;\n /** Total number of semantics cache hits */\n semanticsHits: number;\n /** Total number of semantics cache misses */\n semanticsMisses: number;\n /** Current selector cache size */\n selectorCacheSize: number;\n /** Maximum selector cache size */\n maxSelectorCacheSize: number;\n}\n\n/**\n * Options for creating an EID cache\n */\nexport interface EIDCacheOptions {\n /** Maximum size for selector cache (default: 1000) */\n maxSelectorCacheSize?: number;\n}\n\n/**\n * EID Cache for multi-level caching\n * Provides caching for:\n * - Complete EID identities (WeakMap)\n * - Selector query results (LRU Map)\n * - Anchor finder results (WeakMap)\n * - Semantic extraction results (WeakMap)\n */\nexport class EIDCache {\n private eidCache: WeakMap<Element, ElementIdentity>;\n private selectorResultCache: LRUCache<string, Element[]>;\n private anchorCache: WeakMap<Element, AnchorResult | null>;\n private semanticsCache: WeakMap<Element, ElementSemantics>;\n private stats: CacheStats;\n\n constructor(options: EIDCacheOptions = {}) {\n this.eidCache = new WeakMap();\n this.selectorResultCache = new LRUCache<string, Element[]>(\n options.maxSelectorCacheSize ?? 1000,\n );\n this.anchorCache = new WeakMap();\n this.semanticsCache = new WeakMap();\n this.stats = {\n eidHits: 0,\n eidMisses: 0,\n selectorHits: 0,\n selectorMisses: 0,\n anchorHits: 0,\n anchorMisses: 0,\n semanticsHits: 0,\n semanticsMisses: 0,\n selectorCacheSize: 0,\n maxSelectorCacheSize: options.maxSelectorCacheSize ?? 1000,\n };\n }\n\n /**\n * Get cached EID for element\n */\n getEID(element: Element): ElementIdentity | undefined {\n const cached = this.eidCache.get(element);\n if (cached !== undefined) {\n this.stats.eidHits++;\n return cached;\n }\n this.stats.eidMisses++;\n return undefined;\n }\n\n /**\n * Cache EID for element\n */\n setEID(element: Element, eid: ElementIdentity): void {\n this.eidCache.set(element, eid);\n }\n\n /**\n * Get cached selector results\n */\n getSelectorResults(selector: string): Element[] | undefined {\n const cached = this.selectorResultCache.get(selector);\n if (cached !== undefined) {\n this.stats.selectorHits++;\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n return cached;\n }\n this.stats.selectorMisses++;\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n return undefined;\n }\n\n /**\n * Cache selector results\n */\n setSelectorResults(selector: string, results: Element[]): void {\n this.selectorResultCache.set(selector, results);\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n }\n\n /**\n * Get cached anchor result\n */\n getAnchor(element: Element): AnchorResult | null | undefined {\n if (this.anchorCache.has(element)) {\n this.stats.anchorHits++;\n return this.anchorCache.get(element);\n }\n this.stats.anchorMisses++;\n return undefined;\n }\n\n /**\n * Cache anchor result\n */\n setAnchor(element: Element, result: AnchorResult | null): void {\n this.anchorCache.set(element, result);\n }\n\n /**\n * Get cached semantics\n */\n getSemantics(element: Element): ElementSemantics | undefined {\n const cached = this.semanticsCache.get(element);\n if (cached !== undefined) {\n this.stats.semanticsHits++;\n return cached;\n }\n this.stats.semanticsMisses++;\n return undefined;\n }\n\n /**\n * Cache semantics\n */\n setSemantics(element: Element, semantics: ElementSemantics): void {\n this.semanticsCache.set(element, semantics);\n }\n\n /**\n * Clear all caches\n */\n clear(): void {\n this.selectorResultCache.clear();\n this.stats.selectorCacheSize = 0;\n // WeakMaps are automatically cleared when elements are GC'd\n // Reset stats\n this.stats = {\n eidHits: 0,\n eidMisses: 0,\n selectorHits: 0,\n selectorMisses: 0,\n anchorHits: 0,\n anchorMisses: 0,\n semanticsHits: 0,\n semanticsMisses: 0,\n selectorCacheSize: 0,\n maxSelectorCacheSize: this.stats.maxSelectorCacheSize,\n };\n }\n\n /**\n * Invalidate cache for a specific element\n * Note: WeakMaps don't support deletion, but we can clear selector cache\n * if needed. This method is mainly for future extensibility.\n */\n invalidateElement(_element: Element): void {\n // WeakMaps don't support explicit deletion\n // Elements will be automatically removed when GC'd\n // We can only clear selector cache entries that might reference this element\n // For now, this is a no-op, but kept for API consistency\n }\n\n /**\n * Invalidate a specific selector from cache\n */\n invalidateSelector(selector: string): void {\n this.selectorResultCache.delete(selector);\n this.stats.selectorCacheSize = this.selectorResultCache.size;\n }\n\n /**\n * Get cache statistics\n */\n getStats(): CacheStats {\n return {\n ...this.stats,\n selectorCacheSize: this.selectorResultCache.size,\n };\n }\n\n /**\n * Get cache hit rate for EID cache\n */\n getEIDHitRate(): number {\n const total = this.stats.eidHits + this.stats.eidMisses;\n return total > 0 ? this.stats.eidHits / total : 0;\n }\n\n /**\n * Get cache hit rate for selector cache\n */\n getSelectorHitRate(): number {\n const total = this.stats.selectorHits + this.stats.selectorMisses;\n return total > 0 ? this.stats.selectorHits / total : 0;\n }\n\n /**\n * Get cache hit rate for anchor cache\n */\n getAnchorHitRate(): number {\n const total = this.stats.anchorHits + this.stats.anchorMisses;\n return total > 0 ? this.stats.anchorHits / total : 0;\n }\n\n /**\n * Get cache hit rate for semantics cache\n */\n getSemanticsHitRate(): number {\n const total = this.stats.semanticsHits + this.stats.semanticsMisses;\n return total > 0 ? this.stats.semanticsHits / total : 0;\n }\n\n /**\n * Get overall cache hit rate\n */\n getOverallHitRate(): number {\n const totalHits =\n this.stats.eidHits +\n this.stats.selectorHits +\n this.stats.anchorHits +\n this.stats.semanticsHits;\n const totalMisses =\n this.stats.eidMisses +\n this.stats.selectorMisses +\n this.stats.anchorMisses +\n this.stats.semanticsMisses;\n const total = totalHits + totalMisses;\n return total > 0 ? totalHits / total : 0;\n }\n}\n\n/**\n * Create a new EID cache instance\n */\nexport function createEIDCache(options?: EIDCacheOptions): EIDCache {\n return new EIDCache(options);\n}\n\n/**\n * Global default cache instance\n * Used automatically if no cache is provided to generateEID\n */\nlet globalCache: EIDCache | null = null;\n\n/**\n * Get or create the global default cache\n */\nexport function getGlobalCache(): EIDCache {\n if (!globalCache) {\n globalCache = createEIDCache();\n }\n return globalCache;\n}\n\n/**\n * Reset the global cache\n */\nexport function resetGlobalCache(): void {\n globalCache = null;\n}\n","import type {\n ElementIdentity,\n GeneratorOptions,\n Constraint,\n FallbackRules,\n} from '../types';\nimport { AnchorFinder } from './anchor-finder';\nimport { PathBuilder } from './path-builder';\nimport { SemanticExtractor } from './semantic-extractor';\nimport { SvgFingerprinter } from './svg-fingerprinter';\nimport { calculateConfidence } from '../utils/scorer';\nimport { EID_VERSION, DEFAULT_GENERATOR_OPTIONS, ANCHOR_SCORE } from '../utils/constants';\nimport { getGlobalCache } from '../utils/eid-cache';\n\n/**\n * Generates EID (Element Identity) for a DOM element\n * Following SPECIFICATION.md §7-11\n *\n * @param target - Target DOM element\n * @param options - Generation options\n * @returns Element identity or null if generation failed\n */\nexport function generateEID(\n target: Element,\n options: GeneratorOptions = {},\n): ElementIdentity | null {\n // Input validation\n if (!target || !target.ownerDocument) {\n return null;\n }\n\n // Ensure target is part of a document\n if (!target.isConnected) {\n return null;\n }\n\n const opts = { ...DEFAULT_GENERATOR_OPTIONS, ...options };\n\n // Get cache instance (use provided or global default)\n const cache = opts.cache ?? getGlobalCache();\n\n // Check cache for complete EID first\n const cachedEID = cache.getEID(target);\n if (cachedEID !== undefined) {\n return cachedEID;\n }\n\n const anchorFinder = new AnchorFinder(opts, cache);\n const pathBuilder = new PathBuilder(opts, cache);\n const semanticExtractor = new SemanticExtractor(opts, cache);\n const svgFingerprinter = new SvgFingerprinter();\n\n // 1. Find anchor\n const anchorResult = anchorFinder.findAnchor(target);\n if (!anchorResult && !opts.fallbackToBody) {\n return null;\n }\n\n // Use found anchor or fallback to body\n const anchorElement =\n anchorResult?.element ?? target.ownerDocument?.body ?? null;\n if (!anchorElement) return null;\n\n // Determine if anchor is degraded\n const anchorDegraded = !anchorResult || anchorResult.tier === 'C';\n\n // Calculate nth-child position for anchor (same logic as for target)\n // Skip for body/html elements as they are unique by definition\n const anchorTag = anchorElement.tagName.toLowerCase();\n const anchorParent = anchorElement.parentElement;\n let anchorNthChild: number | undefined;\n if (anchorParent && anchorTag !== 'body' && anchorTag !== 'html') {\n const siblings = Array.from(anchorParent.children);\n const index = siblings.indexOf(anchorElement);\n if (index !== -1) {\n anchorNthChild = index + 1; // 1-based for CSS nth-child()\n }\n }\n\n // 2. Build anchor node\n const anchorSemantics = semanticExtractor.extract(anchorElement);\n const anchorNode = {\n tag: anchorElement.tagName.toLowerCase(),\n semantics: anchorSemantics,\n score: anchorResult?.score ?? ANCHOR_SCORE.DEGRADED_SCORE,\n degraded: anchorDegraded,\n nthChild: anchorNthChild,\n };\n\n // 3. Build path (now returns PathBuildResult with degradation info)\n const pathResult = pathBuilder.buildPath(\n anchorElement,\n target,\n semanticExtractor,\n );\n\n // 4. Build target node\n const targetSemantics = semanticExtractor.extract(target);\n\n // Add SVG fingerprint if applicable\n if (opts.enableSvgFingerprint && isSvgElement(target)) {\n targetSemantics.svg = svgFingerprinter.fingerprint(target as SVGElement);\n }\n\n // Calculate nth-child position for target (same logic as in PathBuilder)\n const targetParent = target.parentElement;\n let targetNthChild: number | undefined;\n if (targetParent) {\n const siblings = Array.from(targetParent.children);\n const index = siblings.indexOf(target);\n if (index !== -1) {\n targetNthChild = index + 1; // 1-based for CSS nth-child()\n }\n }\n\n const targetNode = {\n tag: target.tagName.toLowerCase(),\n semantics: targetSemantics,\n score: semanticExtractor.scoreElement(target),\n nthChild: targetNthChild,\n };\n\n // 5. Build constraints\n const constraints: Constraint[] = [];\n\n // 6. Build fallback rules\n const fallback: FallbackRules = {\n onMultiple: 'best-score',\n onMissing: 'anchor-only',\n maxDepth: 3,\n };\n\n // Determine overall degradation status\n const isDegraded = anchorNode.degraded || pathResult.degraded;\n const degradationReason = getDegradationReason(anchorNode.degraded, pathResult);\n\n // 7. Build EID\n const eid: ElementIdentity = {\n version: EID_VERSION,\n anchor: anchorNode,\n path: pathResult.path,\n target: targetNode,\n constraints,\n fallback,\n meta: {\n confidence: 0, // Calculated below\n generatedAt: new Date().toISOString(),\n generator: `dom-eid@${EID_VERSION}`,\n source: opts.source,\n degraded: isDegraded,\n degradationReason,\n },\n };\n\n // 8. Calculate confidence\n eid.meta.confidence = calculateConfidence(eid);\n\n // 9. Check threshold\n if (eid.meta.confidence < opts.confidenceThreshold) {\n return null;\n }\n\n // Cache the result\n cache.setEID(target, eid);\n\n return eid;\n}\n\n/**\n * Checks if element is an SVG element\n */\nfunction isSvgElement(el: Element): boolean {\n return (\n el.namespaceURI === 'http://www.w3.org/2000/svg' ||\n el.tagName.toLowerCase() === 'svg' ||\n el instanceof SVGElement\n );\n}\n\n/**\n * Determines the degradation reason based on various factors\n */\nfunction getDegradationReason(\n anchorDegraded: boolean,\n pathResult: { degraded: boolean; degradationReason?: string },\n): string | undefined {\n if (anchorDegraded && pathResult.degraded) {\n return 'anchor-and-path-degraded';\n }\n if (anchorDegraded) {\n return 'no-semantic-anchor';\n }\n if (pathResult.degraded) {\n return pathResult.degradationReason;\n }\n return undefined;\n}\n","import type { ElementIdentity, ElementSemantics, PathNode } from '../types';\nimport { ATTRIBUTE_PRIORITY, IGNORED_ATTRIBUTES, SVG_CHILD_ELEMENTS } from '../utils/constants';\nimport { cleanAttributeValue } from '../utils/attribute-cleaner';\nimport { filterStableClasses } from '../utils/class-classifier';\nimport { isDynamicId, ID_REFERENCE_ATTRIBUTES, hasDynamicIdReference } from '../utils/id-validator';\n\n/**\n * Options for selector building with uniqueness control\n */\nexport interface BuildSelectorOptions {\n /**\n * Enable uniqueness disambiguation for target element.\n * When true and base selector is not unique, adds:\n * 1. Extra stable classes (up to maxClasses)\n * 2. :nth-of-type(N) as last resort\n */\n ensureUnique?: boolean;\n\n /**\n * Maximum number of additional classes to add for disambiguation\n * @default 4\n */\n maxClasses?: number;\n\n /**\n * Root element for uniqueness check (defaults to document)\n */\n root?: Document | Element;\n}\n\n/**\n * Result of selector building with uniqueness info\n */\nexport interface BuildSelectorResult {\n /** Generated CSS selector */\n selector: string;\n /** Whether selector is unique in the given root */\n isUnique: boolean;\n /** Whether nth-of-type was added for disambiguation */\n usedNthOfType: boolean;\n /** Number of extra classes added for disambiguation */\n extraClassesAdded: number;\n}\n\n/**\n * Generates CSS selectors from Element Identity\n * Following SPECIFICATION.md §13.1\n *\n * Priority order (highest to lowest):\n * 1. ID (100)\n * 2. data-testid, data-qa, data-cy (95)\n * 3. aria-label (90)\n * 4. name (85)\n * 5. href, src (80) - semantic for <a>, <img>, etc.\n * 6. role (75)\n * 7. type (70)\n * 8. alt, title, for (60)\n * 9. Stable classes (35)\n * 10. nth-of-type (last resort, added when ensureUnique=true)\n */\nexport class CssGenerator {\n\n /**\n * Builds CSS selector from Element Identity\n * @param eid - Element Identity to convert\n * @param options - Optional uniqueness control settings\n * @returns CSS selector string (simple) or BuildSelectorResult (with options)\n */\n buildSelector(eid: ElementIdentity, options?: BuildSelectorOptions): string;\n buildSelector(\n eid: ElementIdentity,\n options: BuildSelectorOptions & { ensureUnique: true }\n ): BuildSelectorResult;\n buildSelector(\n eid: ElementIdentity,\n options?: BuildSelectorOptions\n ): string | BuildSelectorResult {\n // FIX 1: Check if anchor and target are the same element\n const isAnchorSameAsTarget =\n eid.path.length === 0 &&\n eid.anchor.tag === eid.target.tag &&\n JSON.stringify(eid.anchor.semantics) === JSON.stringify(eid.target.semantics);\n\n if (isAnchorSameAsTarget) {\n // Anchor and target are identical, use only target selector with full semantics\n const selector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: false } // Include classes for same-element case\n );\n\n if (!options?.ensureUnique) {\n return selector;\n }\n\n // Apply uniqueness logic to the single selector\n return this.ensureUniqueSelector(selector, eid, options);\n }\n\n const parts: string[] = [];\n\n // FIX 2: Ensure anchor is unique before building full selector\n const anchorSelector = options?.ensureUnique\n ? this.ensureUniqueAnchor(eid, options.root ?? document)\n : this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n\n parts.push(anchorSelector);\n\n // Path\n for (const node of eid.path) {\n let nodeSelector = this.buildNodeSelector(node.tag, node.semantics);\n\n // Add nth-child if available in EID (for precise positioning)\n if (node.nthChild !== undefined) {\n // For table elements, always use nth-child for correct positioning\n const isTableElement = ['tr', 'td', 'th', 'thead', 'tbody', 'tfoot'].includes(node.tag);\n if (isTableElement) {\n nodeSelector += `:nth-child(${node.nthChild})`;\n } else {\n // For non-table elements, also use nth-child if available for precision\n nodeSelector += `:nth-child(${node.nthChild})`;\n }\n }\n\n parts.push(nodeSelector);\n }\n\n // Target (base selector without disambiguation)\n let targetBaseSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: options?.ensureUnique } // Exclude classes initially if we need unique\n );\n\n // Add nth-child if available in EID (same logic as for path nodes)\n if (eid.target.nthChild !== undefined) {\n const isTableElement = ['tr', 'td', 'th', 'thead', 'tbody', 'tfoot'].includes(eid.target.tag);\n if (isTableElement) {\n targetBaseSelector += `:nth-child(${eid.target.nthChild})`;\n } else {\n // For non-table elements, also use nth-child if available for precision\n targetBaseSelector += `:nth-child(${eid.target.nthChild})`;\n }\n }\n\n parts.push(targetBaseSelector);\n\n // ENHANCED FIX: Use appropriate combinator based on element type and context\n const isSvgChild = this.isSvgChildElement(eid.target.tag);\n const hasSvgInPath = eid.path.some(node => node.tag === 'svg');\n\n let baseSelector: string;\n\n // For SVG children with svg in path: use descendant until svg, then child\n if (isSvgChild && hasSvgInPath) {\n const svgIndexInPath = eid.path.findIndex(node => node.tag === 'svg');\n if (svgIndexInPath !== -1) {\n // parts structure: [anchor, ...path nodes, target]\n // svgIndex in parts is svgIndexInPath + 1 (because anchor is at index 0)\n const svgIndexInParts = svgIndexInPath + 1;\n\n // Everything up to and including svg (use descendant combinator)\n const beforeAndIncludingSvg = parts.slice(0, svgIndexInParts + 1);\n // Everything after svg, excluding target (use child combinator)\n const afterSvgBeforeTarget = parts.slice(svgIndexInParts + 1, -1);\n // The target element\n const target = parts[parts.length - 1];\n\n // Build selector: descendant to svg, then child for everything inside svg\n if (afterSvgBeforeTarget.length > 0) {\n baseSelector = beforeAndIncludingSvg.join(' ') + ' > ' + afterSvgBeforeTarget.join(' > ') + ' > ' + target;\n } else {\n // Direct child of svg\n baseSelector = beforeAndIncludingSvg.join(' ') + ' > ' + target;\n }\n } else {\n baseSelector = parts.join(' ');\n }\n } else {\n // Default: use descendant combinator (space) for flexibility\n // This handles filtered intermediate elements correctly\n baseSelector = parts.join(' ');\n }\n\n // If uniqueness check not requested, return simple selector\n if (!options?.ensureUnique) {\n return baseSelector;\n }\n\n // For ensureUnique: first check if baseSelector is already unique\n const baseSelectorMatches = this.querySelectorSafe(baseSelector, options.root ?? document);\n\n if (baseSelectorMatches.length === 1) {\n // Base selector is already unique, use it\n return {\n selector: baseSelector,\n isUnique: true,\n usedNthOfType: baseSelector.includes(':nth-'),\n extraClassesAdded: 0,\n };\n }\n\n // If base selector finds 0 or multiple elements, try buildFullDomPathSelector\n if (baseSelectorMatches.length === 0 || baseSelectorMatches.length > 1) {\n const fullPathSelector = this.buildFullDomPathSelector(\n eid,\n eid.target.semantics,\n options.root ?? document\n );\n\n if (fullPathSelector && this.isUnique(fullPathSelector, options.root ?? document)) {\n return {\n selector: fullPathSelector,\n isUnique: true,\n usedNthOfType: fullPathSelector.includes(':nth-'),\n extraClassesAdded: 0,\n };\n }\n }\n\n // Fallback: use regular ensureUniqueSelector logic with baseSelector\n return this.ensureUniqueSelector(\n baseSelector,\n eid,\n options\n );\n }\n\n /**\n * Builds selector for anchor only (used in fallback)\n * @param eid - Element Identity\n * @returns CSS selector for anchor\n */\n buildAnchorSelector(eid: ElementIdentity): string {\n return this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n }\n\n /**\n * Ensures selector uniqueness by progressively adding classes and nth-of-type\n */\n private ensureUniqueSelector(\n baseSelector: string,\n eid: ElementIdentity,\n options: BuildSelectorOptions\n ): BuildSelectorResult {\n const root = options.root ?? document;\n const maxClasses = options.maxClasses ?? 4;\n const targetTag = eid.target.tag;\n const targetSemantics = eid.target.semantics;\n\n let currentSelector = baseSelector;\n let extraClassesAdded = 0;\n let usedNthOfType = false;\n\n // Step 0: Check if base selector finds ANY elements\n // If not, the path is incomplete (div elements were filtered out)\n const baseMatches = this.querySelectorSafe(currentSelector, root);\n\n if (baseMatches.length === 0) {\n // Path is broken - try to find actual DOM path from anchor to target\n const fullPathSelector = this.buildFullDomPathSelector(eid, targetSemantics, root);\n if (fullPathSelector) {\n currentSelector = fullPathSelector;\n // Check if full path selector is unique\n if (this.isUnique(currentSelector, root)) {\n return {\n selector: currentSelector,\n isUnique: true,\n usedNthOfType: false,\n extraClassesAdded: 0,\n };\n }\n }\n }\n\n // Check if base selector is already unique\n if (this.isUnique(currentSelector, root)) {\n return {\n selector: currentSelector,\n isUnique: true,\n usedNthOfType: false,\n extraClassesAdded: 0,\n };\n }\n\n // Step 1: Try adding stable classes from target one by one (up to maxClasses)\n const availableTargetClasses = filterStableClasses(targetSemantics.classes ?? []);\n for (let i = 0; i < Math.min(availableTargetClasses.length, maxClasses); i++) {\n const cls = availableTargetClasses[i];\n currentSelector += `.${this.escapeCSS(cls)}`;\n extraClassesAdded++;\n\n if (this.isUnique(currentSelector, root)) {\n return {\n selector: currentSelector,\n isUnique: true,\n usedNthOfType: false,\n extraClassesAdded,\n };\n }\n }\n\n // Step 1.5: If still not unique, try buildFullDomPathSelector\n // This handles cases where multiple sections have same structure (e.g., multiple ul > li > span)\n if (!this.isUnique(currentSelector, root)) {\n const fullPathSelector = this.buildFullDomPathSelector(eid, targetSemantics, root);\n if (fullPathSelector && this.isUnique(fullPathSelector, root)) {\n return {\n selector: fullPathSelector,\n isUnique: true,\n usedNthOfType: fullPathSelector.includes(':nth-of-type('),\n extraClassesAdded,\n };\n }\n }\n\n // Step 2: Last resort - add :nth-of-type or :nth-child (table-aware)\n const targetElement = this.findNthElementByText(currentSelector, targetSemantics, root);\n if (targetElement) {\n currentSelector += this.getNthSelector(targetElement, targetTag);\n usedNthOfType = true; // Note: might be nth-child for table elements\n }\n\n return {\n selector: currentSelector,\n isUnique: this.isUnique(currentSelector, root),\n usedNthOfType,\n extraClassesAdded,\n };\n }\n\n /**\n * Builds full DOM path selector by traversing actual DOM from anchor\n * This handles cases where intermediate div/span elements were filtered out\n */\n private buildFullDomPathSelector(\n eid: ElementIdentity,\n targetSemantics: ElementSemantics,\n root: Document | Element\n ): string | null {\n // First, find the anchor element\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const anchors = this.querySelectorSafe(anchorSelector, root);\n\n if (anchors.length === 0) return null;\n\n // For each anchor, try to find the target within it\n for (const anchor of anchors) {\n // Find target candidates within this anchor\n const targetCandidates = this.findTargetWithinAnchor(\n anchor,\n eid.target.tag,\n targetSemantics\n );\n\n if (targetCandidates.length === 0) continue;\n\n // FIX: Score each candidate by how well its path matches EID path\n // This handles cases where multiple elements have same text (e.g., calendar dates)\n const scoredCandidates = targetCandidates.map(candidate => {\n const score = this.scorePathMatch(candidate, anchor, eid.path);\n return { element: candidate, score };\n });\n\n // Sort by score (highest first)\n scoredCandidates.sort((a, b) => b.score - a.score);\n\n // Try candidates in order of best match\n for (const { element } of scoredCandidates) {\n const pathSelector = this.buildPathFromAnchorToTarget(\n anchor,\n element,\n eid,\n root\n );\n\n if (pathSelector && this.isUnique(pathSelector, root)) {\n return pathSelector;\n }\n }\n }\n\n return null;\n }\n\n /**\n * Scores how well a candidate's DOM path matches the EID path\n * Compares tags, classes, attributes, and nthChild positions\n * @param candidate - Target element candidate\n * @param anchor - Anchor element\n * @param eidPath - EID path nodes\n * @returns Score (higher = better match)\n */\n private scorePathMatch(\n candidate: Element,\n anchor: Element,\n eidPath: PathNode[]\n ): number {\n // Build actual DOM path from anchor to candidate\n const domPath: Element[] = [];\n let el: Element | null = candidate.parentElement;\n\n while (el && el !== anchor) {\n domPath.unshift(el);\n el = el.parentElement;\n }\n\n let score = 0;\n const minLength = Math.min(domPath.length, eidPath.length);\n\n // Compare each level of the path\n for (let i = 0; i < minLength; i++) {\n const domEl = domPath[i];\n const eidNode = eidPath[i];\n\n // Match tag (most important)\n if (domEl.tagName.toLowerCase() === eidNode.tag) {\n score += 10;\n\n // Match nthChild position (very important for tables)\n if (eidNode.nthChild !== undefined) {\n const parent = domEl.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children);\n const actualNthChild = siblings.indexOf(domEl) + 1;\n if (actualNthChild === eidNode.nthChild) {\n score += 20; // High weight for position match\n } else {\n score -= 10; // Penalty for position mismatch\n }\n }\n }\n } else {\n score -= 5; // Penalty for tag mismatch\n }\n\n // Match classes if present in EID\n if (eidNode.semantics.classes && eidNode.semantics.classes.length > 0) {\n const matchingClasses = eidNode.semantics.classes.filter(\n (cls: string) => domEl.classList.contains(cls)\n );\n score += matchingClasses.length * 2;\n }\n\n // Match attributes if present in EID\n if (eidNode.semantics.attributes) {\n const matchingAttrs = Object.entries(eidNode.semantics.attributes).filter(\n ([name, value]) => domEl.getAttribute(name) === value\n );\n score += matchingAttrs.length * 3;\n }\n }\n\n // Penalty if path lengths don't match\n const lengthDiff = Math.abs(domPath.length - eidPath.length);\n score -= lengthDiff * 2;\n\n return score;\n }\n\n /**\n * Finds target elements within an anchor by matching semantics\n */\n private findTargetWithinAnchor(\n anchor: Element,\n targetTag: string,\n targetSemantics: ElementSemantics\n ): Element[] {\n const candidates = Array.from(anchor.querySelectorAll(targetTag));\n\n // Filter by semantics\n return candidates.filter(el => {\n // Match by text if available\n if (targetSemantics.text) {\n const elText = el.textContent?.trim() || '';\n const normalized = targetSemantics.text.normalized;\n if (!elText.includes(normalized) && !normalized.includes(elText)) {\n return false;\n }\n }\n\n // Match by classes if available\n if (targetSemantics.classes && targetSemantics.classes.length > 0) {\n const hasAllClasses = targetSemantics.classes.every(\n cls => el.classList.contains(cls)\n );\n if (hasAllClasses) return true;\n }\n\n // Match by attributes if available\n if (targetSemantics.attributes) {\n const matchesAttrs = Object.entries(targetSemantics.attributes).every(\n ([name, value]) => {\n const attrValue = el.getAttribute(name);\n if (name === 'href' || name === 'src') {\n return cleanAttributeValue(name, attrValue || '') ===\n cleanAttributeValue(name, value);\n }\n return attrValue === value;\n }\n );\n if (matchesAttrs) return true;\n }\n\n // If we have text match, that's sufficient\n if (targetSemantics.text) return true;\n\n return false;\n });\n }\n\n /**\n * Disambiguates a parent element by trying attributes, classes, then nth-of-type\n * Priority: stable attributes > one stable class > nth-of-type\n * @param element The DOM element to disambiguate\n * @param tag The tag name\n * @param pathNode EID path node with semantics (if available)\n * @param fullPath Current selector path (for uniqueness testing)\n * @param root Root element for queries\n * @returns Disambiguated selector part (e.g., \"div[role='main']\" or \"div.sidebar\" or \"div:nth-of-type(3)\")\n */\n private disambiguateParent(\n element: Element,\n tag: string,\n pathNode: { tag: string; semantics: ElementSemantics } | null,\n fullPath: string[],\n root: Document | Element\n ): string {\n // 1. Try with stable attributes from DSL (if available)\n if (pathNode?.semantics?.attributes) {\n const parentWithAttrs = this.buildNodeSelector(tag, pathNode.semantics, {\n excludeClasses: true\n });\n\n // Check if adding attributes reduces ambiguity\n const baseSelector = [...fullPath, tag].join(' > ');\n const baseCandidates = this.querySelectorSafe(baseSelector, root);\n\n const attrSelector = [...fullPath, parentWithAttrs].join(' > ');\n const attrCandidates = this.querySelectorSafe(attrSelector, root);\n\n if (attrCandidates.length > 0 && attrCandidates.length < baseCandidates.length) {\n return parentWithAttrs;\n }\n }\n\n // 2. Try with ONE stable class (if available and NOT utility)\n if (pathNode?.semantics?.classes) {\n const stableClasses = filterStableClasses(pathNode.semantics.classes);\n\n if (stableClasses.length > 0) {\n const parentWithClass = `${tag}.${this.escapeCSS(stableClasses[0])}`;\n\n // Check if adding class reduces ambiguity\n const baseSelector = [...fullPath, tag].join(' > ');\n const baseCandidates = this.querySelectorSafe(baseSelector, root);\n\n const classSelector = [...fullPath, parentWithClass].join(' > ');\n const classCandidates = this.querySelectorSafe(classSelector, root);\n\n if (classCandidates.length > 0 && classCandidates.length < baseCandidates.length) {\n return parentWithClass;\n }\n }\n }\n\n // 3. Fall back to nth-of-type\n const parent = element.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n s => s.tagName.toLowerCase() === tag\n );\n\n if (siblings.length > 1) {\n return `${tag}${this.getNthSelector(element, tag)}`;\n }\n }\n\n return tag;\n }\n\n /**\n * Builds CSS selector path from anchor to target by traversing actual DOM\n */\n private buildPathFromAnchorToTarget(\n anchor: Element,\n target: Element,\n eid: ElementIdentity,\n root: Document | Element\n ): string | null {\n // Build path from target up to anchor\n const pathElements: Element[] = [];\n let current: Element | null = target;\n\n while (current && current !== anchor) {\n pathElements.unshift(current);\n current = current.parentElement;\n }\n\n if (current !== anchor) {\n // Target is not a descendant of anchor\n return null;\n }\n\n // NEW STRATEGY ORDER:\n // 0. anchor path target[attrs_only] - simplest, no classes\n // 1. anchor > parent[attrs|class|nth] > target[attrs_only] - child combinator with smart parent disambiguation\n // 2. anchor parent[attrs|class|nth] target[attrs_only] - descendant combinator with smart parent disambiguation\n // 3. anchor path target[attrs + 1_stable_class] - add ONE stable class to target\n // 4. anchor path target[attrs]:nth-of-type(N) - last resort, nth on target\n\n const strategies = [\n // ============================================================\n // Strategy 0: anchor path target[attrs_only]\n // Most flexible - no classes on target, only semantic attributes\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const targetTag = eid.target.tag;\n const targetSemantics = eid.target.semantics;\n\n // Build path: anchor > meaningful_tags > target\n const meaningfulTags: string[] = [];\n for (const pathNode of eid.path) {\n meaningfulTags.push(pathNode.tag);\n }\n\n // Check if this simplified path is unique\n const parts = [anchorSelector, ...meaningfulTags, targetTag].filter(Boolean);\n const simplePath = parts.join(' ');\n\n if (this.isUnique(simplePath, root)) {\n return simplePath;\n }\n\n // Try adding target semantics (ONLY attributes, NO classes)\n const targetSelector = this.buildNodeSelector(targetTag, targetSemantics, {\n excludeClasses: true // KEY: no classes on target in Strategy 0\n });\n const simplePathWithSemantics = [anchorSelector, ...meaningfulTags.slice(0, -1), targetSelector].join(' ');\n\n if (this.isUnique(simplePathWithSemantics, root)) {\n return simplePathWithSemantics;\n }\n\n return null;\n },\n\n // ============================================================\n // Strategy 1: anchor > parent[attrs|class|nth] > target[attrs_only]\n // Full path with '>' combinator\n // Parents: disambiguated using attrs > stable class > nth-of-type\n // Target: ONLY attributes, NO classes\n // ============================================================\n () => {\n const fullPath = [this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics)];\n\n // Build a mapping from pathElements to eid.path nodes by tag matching\n const pathNodeMap = new Map<Element, { tag: string; semantics: ElementSemantics } | null>();\n let eidPathIndex = 0;\n\n for (let i = 0; i < pathElements.length - 1; i++) {\n const el = pathElements[i];\n const tag = el.tagName.toLowerCase();\n\n // Try to match with next eid.path node\n if (eidPathIndex < eid.path.length && eid.path[eidPathIndex].tag === tag) {\n pathNodeMap.set(el, eid.path[eidPathIndex]);\n eidPathIndex++;\n } else {\n pathNodeMap.set(el, null);\n }\n }\n\n for (let i = 0; i < pathElements.length; i++) {\n const el = pathElements[i];\n const tag = el.tagName.toLowerCase();\n\n // For intermediate nodes (parents), use smart disambiguation\n if (i < pathElements.length - 1) {\n const pathNode = pathNodeMap.get(el) || null;\n const disambiguated = this.disambiguateParent(el, tag, pathNode, fullPath, root);\n fullPath.push(disambiguated);\n continue;\n }\n\n // For target: ONLY attrs, NO classes (Strategy 1 should not add classes to target)\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: true } // KEY: no classes on target\n );\n\n // For table elements, add nth-child if there are siblings with same tag\n const parent = el.parentElement;\n if (parent && ['td', 'th', 'tr', 'thead', 'tbody', 'tfoot'].includes(tag)) {\n const siblings = Array.from(parent.children).filter(\n s => s.tagName.toLowerCase() === tag\n );\n if (siblings.length > 1) {\n fullPath.push(`${targetSelector}${this.getNthSelector(el, tag)}`);\n } else {\n fullPath.push(targetSelector);\n }\n } else {\n fullPath.push(targetSelector);\n }\n }\n\n const selector = fullPath.join(' > ');\n return this.isUnique(selector, root) ? selector : null;\n },\n\n // ============================================================\n // Strategy 2: anchor parent[attrs|class|nth] target[attrs_only]\n // Descendant combinator (space) - more flexible\n // Parents: disambiguated using attrs > stable class > nth-of-type\n // Target: ONLY attributes, NO classes\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const parts = [anchorSelector];\n\n // Build path from EID nodes, using smart disambiguation for parents\n for (let i = 0; i < pathElements.length - 1; i++) {\n const el = pathElements[i];\n const tag = el.tagName.toLowerCase();\n const pathNode = eid.path[i] || null;\n\n // Check if this path node is ambiguous under current selector\n const tempSelector = parts.join(' ') + ' ' + tag;\n const candidates = this.querySelectorSafe(tempSelector, root);\n\n if (candidates.length > 1) {\n // Need disambiguation\n // First try with attributes/class from disambiguateParent logic\n if (pathNode?.semantics?.attributes) {\n const parentWithAttrs = this.buildNodeSelector(tag, pathNode.semantics, {\n excludeClasses: true\n });\n const testSelector = parts.join(' ') + ' ' + parentWithAttrs;\n\n if (this.querySelectorSafe(testSelector, root).length === 1 ||\n this.querySelectorSafe(testSelector + ' ' + eid.target.tag, root).length === 1) {\n parts.push(parentWithAttrs);\n continue;\n }\n }\n\n // Try with stable class\n if (pathNode?.semantics?.classes) {\n const stableClasses = filterStableClasses(pathNode.semantics.classes);\n if (stableClasses.length > 0) {\n const parentWithClass = `${tag}.${this.escapeCSS(stableClasses[0])}`;\n const testSelector = parts.join(' ') + ' ' + parentWithClass;\n\n if (this.querySelectorSafe(testSelector, root).length === 1 ||\n this.querySelectorSafe(testSelector + ' ' + eid.target.tag, root).length === 1) {\n parts.push(parentWithClass);\n continue;\n }\n }\n }\n\n // Fall back to nth-of-type\n // Find the actual element in pathElements that matches this EID path node\n const matchingPathEl = pathElements[i];\n const parent = matchingPathEl.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n s => s.tagName.toLowerCase() === tag\n );\n if (siblings.length > 1) {\n parts.push(`${tag}${this.getNthSelector(matchingPathEl, tag)}`);\n continue;\n }\n }\n }\n\n // No disambiguation needed\n parts.push(tag);\n }\n\n // Add target: ONLY attrs, NO classes\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: true } // KEY: no classes on target\n );\n parts.push(targetSelector);\n\n const selector = parts.join(' ');\n return this.isUnique(selector, root) ? selector : null;\n },\n\n // ============================================================\n // Strategy 3: anchor path target[attrs + 1_stable_class]\n // Add ONE stable class to target (must be semantic, NOT utility)\n // Only use this if attrs alone are not sufficient\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const meaningfulTags: string[] = [];\n\n for (const pathNode of eid.path) {\n meaningfulTags.push(pathNode.tag);\n }\n\n // Target with ONE stable class\n const stableClasses = filterStableClasses(eid.target.semantics.classes ?? []);\n\n if (stableClasses.length === 0) {\n return null; // No stable classes available\n }\n\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { maxClasses: 1 } // KEY: ONE stable class only\n );\n\n const selector = [anchorSelector, ...meaningfulTags.slice(0, -1), targetSelector].join(' ');\n return this.isUnique(selector, root) ? selector : null;\n },\n\n // ============================================================\n // Strategy 4: anchor path target[attrs]:nth-of-type(N)\n // Last resort - add nth-of-type to target\n // Only use when all other strategies fail\n // ============================================================\n () => {\n const anchorSelector = this.buildNodeSelector(eid.anchor.tag, eid.anchor.semantics);\n const meaningfulTags: string[] = [];\n\n for (const pathNode of eid.path) {\n meaningfulTags.push(pathNode.tag);\n }\n\n // Target with attrs only + nth-of-type\n const targetEl = pathElements[pathElements.length - 1];\n const targetParent = targetEl.parentElement;\n\n if (!targetParent) return null;\n\n const targetSiblings = Array.from(targetParent.children).filter(\n s => s.tagName.toLowerCase() === eid.target.tag\n );\n\n if (targetSiblings.length <= 1) return null;\n\n const targetSelector = this.buildNodeSelector(\n eid.target.tag,\n eid.target.semantics,\n { excludeClasses: true } // No classes, just attrs + nth\n ) + this.getNthSelector(targetEl, eid.target.tag);\n\n const selector = [anchorSelector, ...meaningfulTags.slice(0, -1), targetSelector].join(' ');\n return this.isUnique(selector, root) ? selector : null;\n }\n ];\n\n // Try each strategy in order\n for (const strategy of strategies) {\n const selector = strategy();\n if (selector) return selector;\n }\n\n // Fallback to the last path generated\n return null;\n }\n\n /**\n * Builds a minimal selector for a DOM element\n * @param element The DOM element to create a selector for\n * @returns A minimal CSS selector for the element\n */\n // @ts-ignore: Method is used dynamically in buildPathFromAnchorToTarget\n private buildElementSelector(element: Element): string {\n const tag = element.tagName.toLowerCase();\n let selector = tag;\n\n // Add ID if stable\n if (element.id && !isDynamicId(element.id)) {\n return `${tag}#${this.escapeCSS(element.id)}`;\n }\n\n // Add stable classes\n const classes = Array.from(element.classList);\n const stableClasses = filterStableClasses(classes);\n if (stableClasses.length > 0) {\n selector += stableClasses.slice(0, 2).map(c => `.${this.escapeCSS(c)}`).join('');\n }\n\n // Add role if present\n const role = element.getAttribute('role');\n if (role) {\n selector += `[role=\"${this.escapeAttr(role)}\"]`;\n }\n\n return selector;\n }\n\n /**\n * Safe querySelectorAll that doesn't throw\n */\n private querySelectorSafe(selector: string, root: Document | Element): Element[] {\n try {\n return Array.from(root.querySelectorAll(selector));\n } catch {\n return [];\n }\n }\n\n /**\n * Finds element by matching text content\n * Returns the matching element (used with getNthSelector for table-aware positioning)\n */\n private findNthElementByText(\n selector: string,\n targetSemantics: ElementSemantics,\n root: Document | Element\n ): Element | null {\n const candidates = this.querySelectorSafe(selector, root);\n if (candidates.length <= 1) return null;\n\n // Find by text content (most reliable for this case)\n if (targetSemantics.text) {\n const normalizedText = targetSemantics.text.normalized;\n\n for (const candidate of candidates) {\n const elText = candidate.textContent?.trim() || '';\n if (elText === normalizedText ||\n elText.includes(normalizedText) ||\n normalizedText.includes(elText)) {\n return candidate; // Return element instead of index\n }\n }\n }\n\n return null;\n }\n\n /**\n * Checks if selector matches exactly one element\n */\n private isUnique(selector: string, root: Document | Element): boolean {\n try {\n const elements = root.querySelectorAll(selector);\n return elements.length === 1;\n } catch {\n return false;\n }\n }\n\n /**\n * Gets nth-of-type index for an element\n */\n private getNthOfTypeIndex(element: Element, tag: string): number | null {\n const parent = element.parentElement;\n if (!parent) return null;\n\n // Get all siblings with same tag\n const siblings = Array.from(parent.children).filter(\n (el) => el.tagName.toLowerCase() === tag\n );\n\n const index = siblings.indexOf(element);\n return index !== -1 ? index + 1 : null;\n }\n\n /**\n * FIX 2: Ensures anchor selector is unique in the document\n * Priority order: tag → tag.class → tag[attr] → tag:nth-of-type(N)\n * @param eid - Element Identity with anchor information\n * @param root - Root element for uniqueness check\n * @returns Unique selector for anchor\n */\n private ensureUniqueAnchor(\n eid: ElementIdentity,\n root: Document | Element\n ): string {\n const tag = eid.anchor.tag;\n const semantics = eid.anchor.semantics;\n\n // Step 1: Try just tag\n if (this.isUnique(tag, root)) {\n return tag;\n }\n\n // Step 2: Try tag with first stable class\n if (semantics.classes && semantics.classes.length > 0) {\n const stableClasses = filterStableClasses(semantics.classes);\n\n if (stableClasses.length > 0) {\n const selectorWithClass = `${tag}.${this.escapeCSS(stableClasses[0])}`;\n if (this.isUnique(selectorWithClass, root)) {\n return selectorWithClass;\n }\n }\n }\n\n // Step 3: Try tag with stable attribute\n if (semantics.attributes) {\n const sortedAttrs = this.getSortedAttributes(semantics.attributes);\n\n for (const { name, value } of sortedAttrs) {\n const cleanedValue =\n name === 'href' || name === 'src'\n ? cleanAttributeValue(name, value)\n : value;\n\n if (cleanedValue) {\n const selectorWithAttr = `${tag}[${name}=\"${this.escapeAttr(cleanedValue)}\"]`;\n if (this.isUnique(selectorWithAttr, root)) {\n return selectorWithAttr;\n }\n }\n }\n }\n\n // Step 4: Use nth-child from EID if available (most reliable)\n if (eid.anchor.nthChild !== undefined) {\n // Use nth-child instead of nth-of-type for accuracy\n const selectorWithNth = `${tag}:nth-child(${eid.anchor.nthChild})`;\n if (this.isUnique(selectorWithNth, root)) {\n return selectorWithNth;\n }\n }\n\n // Step 5: Try tag with nth-of-type (fallback for old EIDs without nthChild)\n // Find all elements with this tag in root\n const allAnchors = Array.from(root.querySelectorAll(tag));\n\n if (allAnchors.length > 1) {\n // Need to match by semantics to find the correct anchor\n const matchingAnchor = this.findElementBySemantics(allAnchors, semantics);\n\n if (matchingAnchor) {\n const nthIndex = this.getNthOfTypeIndex(matchingAnchor, tag);\n if (nthIndex) {\n return `${tag}:nth-of-type(${nthIndex})`;\n }\n }\n }\n\n // Fallback: just tag\n return tag;\n }\n\n /**\n * FIX 2: Finds element by matching semantics\n * @param elements - Array of candidate elements\n * @param semantics - Semantics to match against\n * @returns Matching element or null\n */\n private findElementBySemantics(\n elements: Element[],\n semantics: ElementSemantics\n ): Element | null {\n // If semantics is empty (no classes, attributes, or text), return first element\n const hasSemantics = \n (semantics.classes && semantics.classes.length > 0) ||\n (semantics.attributes && Object.keys(semantics.attributes).length > 0) ||\n semantics.text;\n \n if (!hasSemantics) {\n return elements.length > 0 ? elements[0] : null;\n }\n\n return (\n elements.find((el) => {\n // Match by classes\n if (semantics.classes && semantics.classes.length > 0) {\n const hasClasses = semantics.classes.every((cls) =>\n el.classList.contains(cls)\n );\n if (hasClasses) return true;\n }\n\n // Match by attributes\n if (semantics.attributes) {\n const hasAttrs = Object.entries(semantics.attributes).every(\n ([name, value]) => el.getAttribute(name) === value\n );\n if (hasAttrs) return true;\n }\n\n // Match by text\n if (semantics.text) {\n const elText = el.textContent?.trim() || '';\n const normalized = semantics.text.normalized;\n if (elText.includes(normalized) || normalized.includes(elText)) {\n return true;\n }\n }\n\n return false;\n }) || null\n );\n }\n\n /**\n * FIX 3: Gets nth selector - nth-child for tables, nth-of-type for others\n * This method is now ACTIVELY INTEGRATED in selector generation logic\n * to ensure table elements use position-specific nth-child selectors\n * @param element - Element to get selector for\n * @param tag - Tag name\n * @returns nth selector string (e.g., \":nth-child(2)\" or \":nth-of-type(2)\")\n */\n private getNthSelector(element: Element, tag: string): string {\n const parent = element.parentElement;\n if (!parent) return '';\n\n const siblings = Array.from(parent.children);\n const index = siblings.indexOf(element) + 1;\n\n // For table elements use nth-child (more reliable for table structure)\n if (['tr', 'td', 'th', 'thead', 'tbody', 'tfoot'].includes(tag)) {\n return `:nth-child(${index})`;\n }\n\n // For other elements use nth-of-type\n const sameTags = siblings.filter((s) => s.tagName.toLowerCase() === tag);\n const typeIndex = sameTags.indexOf(element) + 1;\n return `:nth-of-type(${typeIndex})`;\n }\n\n /**\n * Gets attribute priority for sorting\n * @param attrName - Attribute name\n * @returns Priority number (higher = more priority)\n */\n private getAttributePriority(attrName: string): number {\n // Exact match\n if (ATTRIBUTE_PRIORITY[attrName] !== undefined) {\n return ATTRIBUTE_PRIORITY[attrName];\n }\n\n // data-* wildcard\n if (attrName.startsWith('data-')) {\n return ATTRIBUTE_PRIORITY['data-*'];\n }\n\n // aria-* wildcard\n if (attrName.startsWith('aria-')) {\n return ATTRIBUTE_PRIORITY['aria-*'];\n }\n\n return 0; // Don't use in selector\n }\n\n /**\n * Checks if attribute should be ignored\n * @param attrName - Attribute name\n * @returns True if should be ignored\n */\n private shouldIgnoreAttribute(attrName: string): boolean {\n // Ignored attributes\n if (IGNORED_ATTRIBUTES.has(attrName)) return true;\n\n // Inline event handlers\n if (attrName.startsWith('on')) return true;\n\n // Angular/React service attributes\n if (attrName.startsWith('ng-') || attrName.startsWith('_ng')) return true;\n if (attrName.startsWith('data-reactid') || attrName.startsWith('data-react'))\n return true;\n if (attrName.startsWith('data-v-')) return true; // Vue scoped styles\n\n return false;\n }\n\n /**\n * Gets attributes sorted by priority\n * @param attributes - Attributes object\n * @returns Sorted array of attributes with priority\n */\n private getSortedAttributes(\n attributes: Record<string, string>\n ): Array<{ name: string; value: string; priority: number }> {\n return Object.entries(attributes)\n .filter(([name]) => !this.shouldIgnoreAttribute(name))\n // Filter out ID-reference attributes with dynamic values\n .filter(([name, value]) => !ID_REFERENCE_ATTRIBUTES.has(name) || !hasDynamicIdReference(value))\n .map(([name, value]) => ({\n name,\n value,\n priority: this.getAttributePriority(name),\n }))\n .filter((attr) => attr.priority > 0)\n .sort((a, b) => b.priority - a.priority);\n }\n\n /**\n * Builds selector for a single node\n * Priority: ID → semantic attributes → role → classes\n */\n private buildNodeSelector(\n tag: string,\n semantics: ElementSemantics,\n options?: { excludeClasses?: boolean; maxClasses?: number }\n ): string {\n let selector = tag;\n\n // 1. ID (highest priority - score 100)\n if (semantics.id) {\n selector += `#${this.escapeCSS(semantics.id)}`;\n return selector; // ID is unique enough, no need for more\n }\n\n // 2. Attributes in priority order (before classes!)\n if (semantics.attributes) {\n const sortedAttrs = this.getSortedAttributes(semantics.attributes);\n\n for (const { name, value } of sortedAttrs) {\n // Clean href/src from dynamic parts\n const cleanedValue =\n name === 'href' || name === 'src'\n ? cleanAttributeValue(name, value)\n : value;\n\n if (cleanedValue) {\n selector += `[${name}=\"${this.escapeAttr(cleanedValue)}\"]`;\n }\n }\n }\n\n // 3. Role attribute (if not already in attributes)\n if (semantics.role && !semantics.attributes?.role) {\n selector += `[role=\"${this.escapeAttr(semantics.role)}\"]`;\n }\n\n // 4. Stable classes - skip if excludeClasses is true\n if (!options?.excludeClasses && semantics.classes && semantics.classes.length > 0) {\n const stableClasses = filterStableClasses(semantics.classes);\n\n // Apply maxClasses limit if specified\n const classesToAdd = options?.maxClasses !== undefined\n ? stableClasses.slice(0, options.maxClasses)\n : stableClasses;\n\n selector += classesToAdd.map((c) => `.${this.escapeCSS(c)}`).join('');\n }\n\n return selector;\n }\n\n /**\n * Escapes special characters for CSS selector\n */\n private escapeCSS(str: string): string {\n // Экранируем ведущий дефис для классов, начинающихся с дефиса (например, -bottom-6)\n // Согласно CSS спецификации, класс -bottom-6 должен быть записан как \\-bottom-6\n let escaped = str;\n \n // Если строка начинается с дефиса, экранируем его\n if (escaped.startsWith('-')) {\n escaped = '\\\\-' + escaped.slice(1);\n }\n \n // Экранируем другие специальные символы (дефис внутри имен классов валиден, только ведущий требует экранирования)\n escaped = escaped.replace(/([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^`{|}~])/g, '\\\\$1');\n \n return escaped;\n }\n\n /**\n * Escapes quotes for attribute values\n */\n private escapeAttr(str: string): string {\n return str.replace(/\"/g, '\\\\\"').replace(/\\\\/g, '\\\\\\\\');\n }\n\n /**\n * Checks if element tag is an SVG child element\n * @param tag - Element tag name\n * @returns True if element is an SVG child\n */\n private isSvgChildElement(tag: string): boolean {\n return SVG_CHILD_ELEMENTS.includes(tag as any);\n }\n}\n","import type { ElementSemantics, TextContent, SvgFingerprint } from '../types';\nimport { normalizeText } from '../utils/text-normalizer';\n\n/**\n * Filters elements by semantic criteria\n * Following SPECIFICATION.md §13.2\n */\nexport class SemanticsMatcher {\n /**\n * Filters elements that match the semantics\n * @param elements - Candidate elements\n * @param semantics - Target semantics to match\n * @returns Filtered elements that match\n */\n match(elements: Element[], semantics: ElementSemantics): Element[] {\n return elements.filter((el) => this.matchElement(el, semantics));\n }\n\n /**\n * Checks if a single element matches the semantics\n */\n private matchElement(element: Element, semantics: ElementSemantics): boolean {\n // Match text (if specified)\n if (semantics.text && !this.matchText(element, semantics.text)) {\n return false;\n }\n\n // Match attributes (if specified)\n if (\n semantics.attributes &&\n !this.matchAttributes(element, semantics.attributes)\n ) {\n return false;\n }\n\n // Match SVG fingerprint (if specified)\n if (\n semantics.svg &&\n !this.matchSvgFingerprint(element as SVGElement, semantics.svg)\n ) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Matches text content\n * Prioritizes direct text nodes, but falls back to full textContent if no direct text\n */\n matchText(element: Element, text: TextContent): boolean {\n // Prioritize direct text nodes\n const directTextNodes = Array.from(element.childNodes)\n .filter(node => node.nodeType === Node.TEXT_NODE);\n\n const directText = directTextNodes\n .map(node => node.textContent?.trim() ?? '')\n .join(' ');\n\n // If no direct text nodes, use full textContent as fallback\n // This handles cases like <td><button>18</button></td> where text is in child element\n const textToMatch = directText || (element.textContent?.trim() ?? '');\n\n if (!textToMatch) return false;\n\n const normalized = normalizeText(textToMatch);\n\n // Support partial matching with option (default is exact)\n return text.matchMode === 'partial'\n ? normalized.includes(text.normalized)\n : normalized === text.normalized;\n }\n\n /**\n * Matches attributes\n */\n matchAttributes(element: Element, attrs: Record<string, string>): boolean {\n for (const [key, value] of Object.entries(attrs)) {\n const elementValue = element.getAttribute(key);\n if (elementValue !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Matches SVG fingerprint\n */\n matchSvgFingerprint(element: SVGElement, fingerprint: SvgFingerprint): boolean {\n // Match shape\n if (element.tagName.toLowerCase() !== fingerprint.shape) {\n return false;\n }\n\n // Match dHash for path elements\n if (fingerprint.dHash && fingerprint.shape === 'path') {\n const d = element.getAttribute('d');\n if (d) {\n const hash = this.computePathHash(d);\n if (hash !== fingerprint.dHash) {\n return false;\n }\n }\n }\n\n // Match geomHash for non-path shapes (circle, rect, ellipse, line)\n if (fingerprint.geomHash && ['circle', 'rect', 'ellipse', 'line'].includes(fingerprint.shape)) {\n const computed = this.computeGeomHash(element, fingerprint.shape);\n if (computed !== fingerprint.geomHash) {\n return false;\n }\n }\n\n // Match title text\n if (fingerprint.titleText) {\n const title = element.querySelector('title');\n if (title?.textContent?.trim() !== fingerprint.titleText) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Computes simple path hash (matching SvgFingerprinter)\n */\n private computePathHash(d: string): string {\n const commands = d.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/gi) ?? [];\n const firstN = commands.slice(0, 5);\n\n const normalized = firstN\n .map((cmd) => {\n return cmd.trim().replace(/(-?\\d+\\.?\\d*)/g, (match) => {\n return parseFloat(match).toFixed(1);\n });\n })\n .join(' ');\n\n return this.simpleHash(normalized);\n }\n\n /**\n * Computes geometry hash for non-path shapes (matching SvgFingerprinter)\n */\n private computeGeomHash(element: SVGElement, shape: string): string {\n const attrs: string[] = [];\n\n switch (shape) {\n case 'circle':\n attrs.push(`r=${element.getAttribute('r') ?? '0'}`);\n break;\n\n case 'rect': {\n const w = parseFloat(element.getAttribute('width') ?? '0');\n const h = parseFloat(element.getAttribute('height') ?? '0');\n if (w > 0 && h > 0) {\n attrs.push(`ratio=${(w / h).toFixed(2)}`);\n }\n break;\n }\n\n case 'ellipse': {\n const rx = parseFloat(element.getAttribute('rx') ?? '0');\n const ry = parseFloat(element.getAttribute('ry') ?? '0');\n if (rx > 0 && ry > 0) {\n attrs.push(`ratio=${(rx / ry).toFixed(2)}`);\n }\n break;\n }\n\n case 'line': {\n const x1 = parseFloat(element.getAttribute('x1') ?? '0');\n const y1 = parseFloat(element.getAttribute('y1') ?? '0');\n const x2 = parseFloat(element.getAttribute('x2') ?? '0');\n const y2 = parseFloat(element.getAttribute('y2') ?? '0');\n const angle = Math.atan2(y2 - y1, x2 - x1);\n attrs.push(`angle=${angle.toFixed(2)}`);\n break;\n }\n }\n\n return this.simpleHash(attrs.join(';'));\n }\n\n /**\n * Simple hash function (matching SvgFingerprinter)\n */\n private simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(16).padStart(8, '0');\n }\n}\n","import type { Constraint } from '../types';\n\n/**\n * Evaluates and applies constraints to candidates\n * Following SPECIFICATION.md §13.4\n */\nexport class ConstraintsEvaluator {\n /**\n * Applies a single constraint to candidates\n * @param candidates - Current candidate elements\n * @param constraint - Constraint to apply\n * @returns Filtered candidates\n */\n applyConstraint(candidates: Element[], constraint: Constraint): Element[] {\n switch (constraint.type) {\n case 'text-proximity':\n return this.applyTextProximity(\n candidates,\n constraint.params as { reference: string; maxDistance: number },\n );\n\n case 'position':\n return this.applyPosition(\n candidates,\n constraint.params as { strategy: string },\n );\n\n case 'uniqueness':\n default:\n // Uniqueness doesn't filter, it's handled by the resolver\n return candidates;\n }\n }\n\n /**\n * Applies text proximity constraint using Levenshtein distance\n */\n private applyTextProximity(\n candidates: Element[],\n params: { reference: string; maxDistance: number },\n ): Element[] {\n return candidates.filter((el) => {\n const text = el.textContent?.trim() ?? '';\n const distance = this.levenshteinDistance(text, params.reference);\n return distance <= params.maxDistance;\n });\n }\n\n /**\n * Applies position constraint\n */\n private applyPosition(\n candidates: Element[],\n params: { strategy: string },\n ): Element[] {\n if (candidates.length <= 1) return candidates;\n\n switch (params.strategy) {\n case 'first-in-dom':\n return [candidates[0]];\n\n case 'top-most':\n return [this.getTopMost(candidates)];\n\n case 'left-most':\n return [this.getLeftMost(candidates)];\n\n default:\n return [candidates[0]];\n }\n }\n\n /**\n * Gets the top-most element by bounding rect\n */\n private getTopMost(elements: Element[]): Element {\n return elements.reduce((top, el) => {\n try {\n const topRect = top.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n return elRect.top < topRect.top ? el : top;\n } catch {\n return top;\n }\n });\n }\n\n /**\n * Gets the left-most element by bounding rect\n */\n private getLeftMost(elements: Element[]): Element {\n return elements.reduce((left, el) => {\n try {\n const leftRect = left.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n return elRect.left < leftRect.left ? el : left;\n } catch {\n return left;\n }\n });\n }\n\n /**\n * Calculates Levenshtein distance between two strings\n */\n private levenshteinDistance(a: string, b: string): number {\n // Early exit for identical strings\n if (a === b) return 0;\n\n // Early exit for empty strings\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Use single-row optimization for memory efficiency\n const row: number[] = Array.from({ length: b.length + 1 }, (_, i) => i);\n\n for (let i = 1; i <= a.length; i++) {\n let prev = i;\n for (let j = 1; j <= b.length; j++) {\n const current =\n a[i - 1] === b[j - 1]\n ? row[j - 1]\n : Math.min(row[j - 1], prev, row[j]) + 1;\n row[j - 1] = prev;\n prev = current;\n }\n row[b.length] = prev;\n }\n\n return row[b.length];\n }\n}\n","import type { ElementIdentity, ElementSemantics, ResolveResult } from '../types';\nimport { CssGenerator } from './css-generator';\nimport { normalizeText } from '../utils/text-normalizer';\n\n/**\n * Handles resolution fallback scenarios\n * Following SPECIFICATION.md §13.5\n */\nexport class FallbackHandler {\n private cssGenerator: CssGenerator;\n\n constructor() {\n this.cssGenerator = new CssGenerator();\n }\n\n /**\n * Handles fallback when resolution fails\n * @param eid - Element Identity being resolved\n * @param dom - Document or element to search in\n * @returns Fallback resolution result\n */\n handleFallback(eid: ElementIdentity, dom: Document | Element): ResolveResult {\n const { onMissing } = eid.fallback;\n\n switch (onMissing) {\n case 'anchor-only':\n return this.fallbackToAnchor(eid, dom);\n\n case 'strict':\n return {\n status: 'error',\n elements: [],\n warnings: ['Element not found (strict mode)'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'strict-not-found' },\n };\n\n case 'none':\n default:\n return {\n status: 'error',\n elements: [],\n warnings: ['Element not found'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'not-found' },\n };\n }\n }\n\n /**\n * Attempts to find and return the anchor element as fallback\n */\n private fallbackToAnchor(\n eid: ElementIdentity,\n dom: Document | Element,\n ): ResolveResult {\n const anchorSelector = this.cssGenerator.buildAnchorSelector(eid);\n const root = dom instanceof Document ? dom : (dom.ownerDocument ?? dom);\n\n try {\n const anchor = root.querySelector(anchorSelector);\n\n if (anchor) {\n return {\n status: 'degraded-fallback',\n elements: [anchor],\n warnings: ['Target not found, returning anchor'],\n confidence: eid.meta.confidence * 0.3,\n meta: { degraded: true, degradationReason: 'anchor-fallback' },\n };\n }\n } catch (error) {\n // Log selector error for debugging\n const message =\n error instanceof Error ? error.message : 'Unknown selector error';\n return {\n status: 'error',\n elements: [],\n warnings: [`Invalid anchor selector: ${message}`],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'invalid-anchor-selector' },\n };\n }\n\n return {\n status: 'error',\n elements: [],\n warnings: ['Anchor also not found'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'anchor-not-found' },\n };\n }\n\n /**\n * Handles ambiguous results (multiple matches)\n * @param elements - All matching elements\n * @param eid - Original Element Identity\n * @returns Resolution result based on fallback rules\n */\n handleAmbiguous(elements: Element[], eid: ElementIdentity): ResolveResult {\n const { onMultiple } = eid.fallback;\n\n switch (onMultiple) {\n case 'first':\n return {\n status: 'success',\n elements: [elements[0]],\n warnings: ['Multiple matches, returning first'],\n confidence: eid.meta.confidence * 0.7,\n meta: { degraded: true, degradationReason: 'first-of-multiple' },\n };\n\n case 'best-score':\n return this.selectBestScoring(elements, eid);\n\n case 'allow-multiple':\n default:\n return {\n status: 'ambiguous',\n elements,\n warnings: [`Multiple matches: ${elements.length}`],\n confidence: eid.meta.confidence * 0.5,\n meta: { degraded: true, degradationReason: 'multiple-matches' },\n };\n }\n }\n\n /**\n * Selects the best-scoring element from candidates\n * Re-scores each element based on semantic match quality\n */\n private selectBestScoring(\n elements: Element[],\n eid: ElementIdentity,\n ): ResolveResult {\n const targetSemantics = eid.target.semantics;\n let bestElement = elements[0];\n let bestScore = -1;\n\n for (const element of elements) {\n const score = this.scoreElementMatch(element, targetSemantics);\n if (score > bestScore) {\n bestScore = score;\n bestElement = element;\n }\n }\n\n return {\n status: 'success',\n elements: [bestElement],\n warnings: [\n `Multiple matches (${elements.length}), selected best-scoring element`,\n ],\n confidence: eid.meta.confidence * (0.7 + bestScore * 0.2),\n meta: { degraded: true, degradationReason: 'best-of-multiple' },\n };\n }\n\n /**\n * Scores how well an element matches the target semantics\n * @returns Score from 0 to 1\n */\n private scoreElementMatch(\n element: Element,\n targetSemantics: ElementSemantics,\n ): number {\n let score = 0;\n let maxScore = 0;\n\n // ID match (highest value)\n if (targetSemantics.id) {\n maxScore += 0.3;\n if (element.id === targetSemantics.id) {\n score += 0.3;\n }\n }\n\n // Class match\n if (targetSemantics.classes && targetSemantics.classes.length > 0) {\n maxScore += 0.25;\n const elementClasses = Array.from(element.classList);\n const matchCount = targetSemantics.classes.filter((cls) =>\n elementClasses.includes(cls),\n ).length;\n score += (matchCount / targetSemantics.classes.length) * 0.25;\n }\n\n // Attribute match\n if (targetSemantics.attributes) {\n const attrs = Object.entries(targetSemantics.attributes);\n if (attrs.length > 0) {\n maxScore += 0.2;\n let matchCount = 0;\n for (const [key, value] of attrs) {\n if (element.getAttribute(key) === value) {\n matchCount++;\n }\n }\n score += (matchCount / attrs.length) * 0.2;\n }\n }\n\n // Role match\n if (targetSemantics.role) {\n maxScore += 0.15;\n if (element.getAttribute('role') === targetSemantics.role) {\n score += 0.15;\n }\n }\n\n // Text match\n if (targetSemantics.text) {\n maxScore += 0.1;\n const elementText = normalizeText(element.textContent);\n if (elementText === targetSemantics.text.normalized) {\n score += 0.1;\n } else if (elementText.includes(targetSemantics.text.normalized)) {\n score += 0.05;\n }\n }\n\n // Normalize score if maxScore > 0\n return maxScore > 0 ? score / maxScore : 0;\n }\n}\n","import type { ElementIdentity, ResolveResult, ResolverOptions } from '../types';\nimport { CssGenerator } from './css-generator';\nimport { SemanticsMatcher } from './semantics-matcher';\nimport { ConstraintsEvaluator } from './constraints-evaluator';\nimport { FallbackHandler } from './fallback-handler';\nimport { DEFAULT_RESOLVER_OPTIONS } from '../utils/constants';\n\n/**\n * Resolves Element Identity back to DOM element(s)\n * Following SPECIFICATION.md §13 (5-phase algorithm)\n *\n * @param eid - Element Identity to resolve\n * @param dom - Document or root element to search in\n * @param options - Resolver options\n * @returns Resolution result with matched elements\n */\nexport function resolve(\n eid: ElementIdentity,\n dom: Document | Element,\n options: ResolverOptions = {},\n): ResolveResult {\n const opts = { ...DEFAULT_RESOLVER_OPTIONS, ...options };\n\n const cssGenerator = new CssGenerator();\n const semanticsMatcher = new SemanticsMatcher();\n const constraintsEvaluator = new ConstraintsEvaluator();\n const fallbackHandler = new FallbackHandler();\n\n // Phase 1: CSS Narrowing\n // Build a base selector. We don't use ensureUnique: true here because\n // we want to gather all potential candidates and prioritize them in later phases\n // (e.g., prioritization by visibility).\n const root = dom instanceof Document ? dom : (dom.ownerDocument ?? dom);\n const selector = cssGenerator.buildSelector(eid, {\n ensureUnique: false,\n root,\n });\n\n let candidates: Element[];\n try {\n candidates = Array.from(root.querySelectorAll(selector));\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown selector error';\n return {\n status: 'error',\n elements: [],\n warnings: [\n `Invalid CSS selector: ${selector}`,\n `Error: ${errorMessage}`,\n ],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'invalid-selector' },\n };\n }\n\n // Limit candidates for performance\n if (candidates.length > opts.maxCandidates) {\n candidates = candidates.slice(0, opts.maxCandidates);\n }\n\n // Phase 2: Semantics Filtering\n const filtered = semanticsMatcher.match(candidates, eid.target.semantics);\n\n // Phase 3: Uniqueness Check\n if (filtered.length === 1) {\n return {\n status: 'success',\n elements: filtered,\n warnings: [],\n confidence: eid.meta.confidence,\n meta: { degraded: false },\n };\n }\n\n // No matches found\n if (filtered.length === 0) {\n if (opts.enableFallback) {\n return fallbackHandler.handleFallback(eid, root);\n }\n return {\n status: 'error',\n elements: [],\n warnings: ['No matching elements found'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'not-found' },\n };\n }\n\n // Phase 4: Constraints Application\n let constrained = filtered;\n const sortedConstraints = sortByPriority(eid.constraints);\n\n for (const constraint of sortedConstraints) {\n constrained = constraintsEvaluator.applyConstraint(constrained, constraint);\n\n // Found unique match\n if (constrained.length === 1) {\n return {\n status: 'success',\n elements: constrained,\n warnings: [],\n confidence: eid.meta.confidence * 0.9,\n meta: { degraded: false },\n };\n }\n\n // All candidates eliminated\n if (constrained.length === 0) {\n if (opts.enableFallback) {\n return fallbackHandler.handleFallback(eid, root);\n }\n return {\n status: 'error',\n elements: [],\n warnings: ['Constraints eliminated all candidates'],\n confidence: 0,\n meta: { degraded: true, degradationReason: 'over-constrained' },\n };\n }\n }\n\n // Phase 5: Handle Ambiguous Result\n if (opts.strictMode) {\n return {\n status: 'ambiguous',\n elements: constrained,\n warnings: [`Non-unique resolution: ${constrained.length} matches`],\n confidence: eid.meta.confidence * 0.7,\n meta: { degraded: true, degradationReason: 'ambiguous' },\n };\n }\n\n // Apply fallback rules for multiple matches\n return fallbackHandler.handleAmbiguous(constrained, eid);\n}\n\n/**\n * Sorts constraints by priority (descending)\n */\nfunction sortByPriority(\n constraints: ElementIdentity['constraints'],\n): ElementIdentity['constraints'] {\n return [...constraints].sort((a, b) => b.priority - a.priority);\n}\n","import type { ElementIdentity, ValidationResult } from '../types';\n\n/**\n * Validates EID structure for correctness\n * @param eid - The Element Identity to validate\n * @returns Validation result with errors and warnings\n */\nexport function validateEID(eid: ElementIdentity): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Version check\n if (!eid.version) {\n errors.push('Missing version field');\n } else if (eid.version !== '1.0') {\n warnings.push(`Unknown version: ${eid.version}`);\n }\n\n // Anchor check\n if (!eid.anchor) {\n errors.push('Missing anchor field');\n } else {\n if (!eid.anchor.tag) {\n errors.push('Anchor missing tag');\n }\n if (typeof eid.anchor.score !== 'number') {\n errors.push('Anchor missing score');\n }\n if (!eid.anchor.semantics) {\n errors.push('Anchor missing semantics');\n }\n }\n\n // Target check\n if (!eid.target) {\n errors.push('Missing target field');\n } else {\n if (!eid.target.tag) {\n errors.push('Target missing tag');\n }\n if (typeof eid.target.score !== 'number') {\n errors.push('Target missing score');\n }\n if (!eid.target.semantics) {\n errors.push('Target missing semantics');\n }\n }\n\n // Path check\n if (!Array.isArray(eid.path)) {\n errors.push('Path must be an array');\n } else {\n for (let i = 0; i < eid.path.length; i++) {\n const node = eid.path[i];\n if (!node.tag) {\n errors.push(`Path node ${i} missing tag`);\n }\n if (!node.semantics) {\n errors.push(`Path node ${i} missing semantics`);\n }\n }\n }\n\n // Meta check\n if (!eid.meta) {\n errors.push('Missing meta field');\n } else {\n if (typeof eid.meta.confidence !== 'number') {\n warnings.push('Missing confidence score');\n }\n if (!eid.meta.generatedAt) {\n warnings.push('Missing generatedAt timestamp');\n }\n }\n\n // Constraints check\n if (!Array.isArray(eid.constraints)) {\n warnings.push('Constraints should be an array');\n }\n\n // Fallback check\n if (!eid.fallback) {\n warnings.push('Missing fallback rules');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Checks if a value is a valid ElementIdentity object\n * @param value - Value to check\n * @returns True if valid ElementIdentity structure\n */\nexport function isEID(value: unknown): value is ElementIdentity {\n if (!value || typeof value !== 'object') return false;\n\n const obj = value as Record<string, unknown>;\n\n return (\n typeof obj.version === 'string' &&\n typeof obj.anchor === 'object' &&\n Array.isArray(obj.path) &&\n typeof obj.target === 'object'\n );\n}\n","import type { ElementIdentity, AnchorNode, PathNode, TargetNode, ElementSemantics, GeneratorOptions, ResolverOptions } from '../types';\nimport { generateEID as generateEIDInternal } from '../generator';\nimport { resolve as resolveInternal } from '../resolver';\nimport { filterStableClasses } from './class-classifier';\nimport { ATTRIBUTE_PRIORITY, IGNORED_ATTRIBUTES } from './constants';\nimport { cleanAttributeValue } from './attribute-cleaner';\nimport { ID_REFERENCE_ATTRIBUTES, hasDynamicIdReference } from './id-validator';\n\n/**\n * Options for SEQL Selector stringification\n */\nexport interface StringifyOptions {\n /** Maximum number of classes to include per node (default: 2) */\n maxClasses?: number;\n /** Maximum number of attributes to include per node (default: 5) */\n maxAttributes?: number;\n /** Include text content as pseudo-attribute (default: true) */\n includeText?: boolean;\n /** Maximum text length to include (default: 50) */\n maxTextLength?: number;\n /** Simplify target node by removing redundant info (default: true) */\n simplifyTarget?: boolean;\n /** Include resolution constraints in SEQL Selector (default: true) */\n includeConstraints?: boolean;\n}\n\n/**\n * Default stringify options\n */\nconst DEFAULT_STRINGIFY_OPTIONS: Required<StringifyOptions> = {\n maxClasses: 2,\n maxAttributes: 5,\n includeText: true,\n maxTextLength: 50,\n simplifyTarget: true,\n includeConstraints: true,\n};\n\n/**\n * Gets attribute priority for sorting\n */\nfunction getAttributePriority(attrName: string): number {\n // ID is highest priority for SEQL Selector\n if (attrName === 'id') return 101;\n\n // Exact match\n if (ATTRIBUTE_PRIORITY[attrName] !== undefined) {\n return ATTRIBUTE_PRIORITY[attrName];\n }\n\n // data-* wildcard\n if (attrName.startsWith('data-')) {\n return ATTRIBUTE_PRIORITY['data-*'];\n }\n\n // aria-* wildcard\n if (attrName.startsWith('aria-')) {\n return ATTRIBUTE_PRIORITY['aria-*'];\n }\n\n return 0;\n}\n\n/**\n * Checks if attribute is considered unique identifier\n */\nfunction isUniqueAttribute(attrName: string): boolean {\n return ['id', 'data-testid', 'data-qa', 'data-cy', 'href', 'text', 'role'].includes(attrName);\n}\n\n/**\n * Simple PII detection for text content\n */\nfunction isTextPII(text: string): boolean {\n // Email pattern\n if (/@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/.test(text)) return true;\n\n // Phone pattern (basic)\n if (/(\\+?\\d{1,3}[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/.test(text)) return true;\n\n // Credit card pattern (basic)\n if (/\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}/.test(text)) return true;\n\n return false;\n}\n\n/**\n * Converts EID to canonical SEQL Selector string representation\n *\n * Requirements (per SEQL_SPECIFICATION_v1.0.md):\n * - Deterministic (same EID → same SEQL Selector)\n * - Canonical (one EID → one SEQL Selector)\n * - Versioned (includes protocol version)\n * - PII-safe (no personal data)\n * - Sorted attributes and classes\n *\n * @param eid - Element Identity Descriptor\n * @param options - Optional stringify options\n * @returns SEQL Selector (canonical string)\n *\n * @example\n * ```typescript\n * const eid = generateEID(element);\n * const selector = stringifySEQL(eid);\n * // \"v1: footer :: ul.menu > li#3 > a[href=\"/contact\"]\"\n * ```\n */\nexport function stringifySEQL(eid: ElementIdentity, options?: StringifyOptions): string {\n const opts = { ...DEFAULT_STRINGIFY_OPTIONS, ...options };\n\n const version = `v${eid.version}`;\n const anchor = stringifyNode(eid.anchor, false, opts);\n const path = eid.path.length > 0\n ? eid.path.map(node => stringifyNode(node, false, opts)).join(' > ') + ' > '\n : '';\n const target = stringifyNode(eid.target, true, opts); // Pass isTarget=true\n\n // Constraints are optional\n const constraints = opts.includeConstraints ? stringifyConstraints(eid) : '';\n\n return `${version}: ${anchor} :: ${path}${target}${constraints}`;\n}\n\n/**\n * Parses SEQL Selector back to EID structure\n *\n * @param selector - SEQL Selector string (similar to CSS Selector)\n * @returns Element Identity Descriptor\n * @throws {Error} if SEQL Selector is malformed or version unsupported\n *\n * @example\n * ```typescript\n * const selector = \"v1: footer :: ul > li#3 > a[href='/contact']\";\n * const eid = parseSEQL(selector);\n * const elements = resolve(eid, document);\n * ```\n */\nexport function parseSEQL(selector: string): ElementIdentity {\n // Trim whitespace\n selector = selector.trim();\n\n // Parse version\n const versionMatch = selector.match(/^v(\\d+(?:\\.\\d+)?)\\s*:\\s*/);\n if (!versionMatch) {\n throw new Error('Invalid SEQL Selector: missing version prefix (expected \"v1:\")');\n }\n\n const version = versionMatch[1];\n if (version !== '1.0' && version !== '1') {\n throw new Error(`Unsupported SEQL Selector version: v${version} (only v1.0 is supported)`);\n }\n\n // Remove version prefix\n let remaining = selector.slice(versionMatch[0].length);\n\n // Parse anchor (up to ::)\n const anchorMatch = remaining.match(/^(.+?)\\s*::\\s*/);\n if (!anchorMatch) {\n throw new Error('Invalid SEQL Selector: missing anchor separator \"::\"');\n }\n\n const anchorStr = anchorMatch[1].trim();\n remaining = remaining.slice(anchorMatch[0].length);\n\n // Parse path and target (split by >)\n // Need to handle constraints {} at the end\n const constraintsMatch = remaining.match(/\\s*\\{([^}]+)\\}\\s*$/);\n let constraintsStr = '';\n if (constraintsMatch) {\n constraintsStr = constraintsMatch[1];\n remaining = remaining.slice(0, constraintsMatch.index);\n }\n\n // Split by > to get path nodes and target\n const nodes = remaining.split(/\\s*>\\s*/).map((n: string) => n.trim()).filter((n: string) => n);\n\n if (nodes.length === 0) {\n throw new Error('Invalid SEQL Selector: missing target node');\n }\n\n // Last node is target, rest are path\n const targetStr = nodes[nodes.length - 1];\n const pathStrs = nodes.slice(0, -1);\n\n // Parse nodes\n const anchor = parseNode(anchorStr, true) as AnchorNode;\n const path = pathStrs.map((str: string) => parseNode(str, false) as PathNode);\n const target = parseNode(targetStr, false) as TargetNode;\n\n // Parse constraints\n const constraints = parseConstraints(constraintsStr);\n\n // Build EID structure\n const eid: ElementIdentity = {\n version: '1.0',\n anchor,\n path,\n target,\n constraints,\n fallback: {\n onMultiple: 'best-score',\n onMissing: 'anchor-only',\n maxDepth: 10,\n },\n meta: {\n confidence: 0.7,\n generatedAt: new Date().toISOString(),\n generator: `seql-parser@1.0`,\n source: 'seql-string',\n degraded: false,\n },\n };\n\n return eid;\n}\n\n/**\n * Stringify a single node (anchor, path, or target)\n */\nfunction stringifyNode(\n node: AnchorNode | PathNode | TargetNode,\n isTarget: boolean = false,\n options: Required<StringifyOptions> = DEFAULT_STRINGIFY_OPTIONS\n): string {\n const { tag, semantics } = node;\n let result = tag;\n\n // 1. Prepare Attributes (including ID and Role)\n const attrStrings: string[] = [];\n const rawAttributes = { ...semantics.attributes };\n\n // In SEQL Selector, ID is just another attribute [id=\"...\"]\n if (semantics.id) {\n rawAttributes.id = semantics.id;\n }\n\n // Include Role if present in semantics but not in attributes\n if (semantics.role && !rawAttributes.role) {\n rawAttributes.role = semantics.role;\n }\n\n const processedAttrs = Object.entries(rawAttributes)\n .map(([name, value]) => {\n const priority = getAttributePriority(name);\n const cleanedValue = (name === 'href' || name === 'src')\n ? cleanAttributeValue(name, value)\n : value;\n return { name, value: cleanedValue, priority };\n })\n .filter(attr => {\n // Filter out truly ignored attributes (style, xmlns, etc)\n const trulyIgnored = ['style', 'xmlns', 'tabindex', 'contenteditable'];\n if (trulyIgnored.includes(attr.name)) return false;\n\n // Filter out ID-reference attributes with dynamic values\n if (ID_REFERENCE_ATTRIBUTES.has(attr.name) && hasDynamicIdReference(attr.value)) {\n return false;\n }\n\n // Keep if priority > 0 or it's a role or id\n return attr.priority > 0 || attr.name === 'role' || attr.name === 'id';\n });\n\n // Sort by priority (desc) to pick the best ones\n processedAttrs.sort((a, b) => b.priority - a.priority);\n\n // Pick top N attributes\n const topAttrs = processedAttrs.slice(0, options.maxAttributes);\n\n // Sort selected attributes ALPHABETICALLY for SEQL Selector canonical format\n topAttrs.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const { name, value } of topAttrs) {\n attrStrings.push(`${name}=\"${escapeAttributeValue(value)}\"`);\n }\n\n // Add text as pseudo-attribute if enabled\n if (options.includeText && semantics.text && !isTextPII(semantics.text.normalized)) {\n const text = semantics.text.normalized;\n if (text.length > 0 && text.length <= options.maxTextLength) {\n attrStrings.push(`text=\"${escapeAttributeValue(text)}\"`);\n }\n }\n\n // Prepare finalAttrs for later use\n let finalAttrs = attrStrings;\n if (attrStrings.length > 0) {\n // Advanced simplification for target node\n if (isTarget && options.simplifyTarget && semantics.id) {\n // If we have ID, we can afford to be more selective,\n // but we MUST keep important semantic info like href, text, data-testid\n finalAttrs = attrStrings.filter(s => {\n const name = s.split('=')[0];\n const priority = getAttributePriority(name);\n // Keep high priority attributes (id, data-testid, href, src, role, text)\n return priority >= 60 || name === 'text' || name === 'id' || name === 'role';\n });\n }\n\n if (finalAttrs.length > 0) {\n // Final alphabetical sort for the attributes in the bracket\n finalAttrs.sort((a, b) => a.localeCompare(b));\n }\n }\n\n // 2. Add stable classes FIRST (before attributes)\n if (semantics.classes && semantics.classes.length > 0) {\n const stableClasses = filterStableClasses(semantics.classes);\n\n // If simplifying target and we have strong identifiers, we can skip classes\n const hasStrongIdentifier = !!semantics.id ||\n attrStrings.some(s => s.startsWith('href=') || s.startsWith('data-testid=') || s.startsWith('text=') || s.startsWith('role='));\n const skipClasses = isTarget && options.simplifyTarget && hasStrongIdentifier;\n\n if (!skipClasses && stableClasses.length > 0) {\n const limitedClasses = stableClasses\n .sort() // Alphabetical for determinism\n .slice(0, options.maxClasses);\n\n result += limitedClasses.map(c => `.${c}`).join('');\n }\n }\n\n // 3. Add attributes SECOND (after classes)\n if (finalAttrs.length > 0) {\n result += `[${finalAttrs.join(',')}]`;\n }\n\n // 4. Add position (nth-child)\n if ('nthChild' in node && node.nthChild) {\n // SEQL Selector position is #N\n const hasStrongIdentifier = !!semantics.id ||\n (semantics.attributes && Object.keys(semantics.attributes).some(isUniqueAttribute));\n\n const skipPosition = isTarget && options.simplifyTarget && hasStrongIdentifier;\n\n if (!skipPosition) {\n result += `#${node.nthChild}`;\n }\n }\n\n return result;\n}\n\n/**\n * Stringify constraints\n */\nfunction stringifyConstraints(eid: ElementIdentity): string {\n if (!eid.constraints || eid.constraints.length === 0) {\n return '';\n }\n\n // Convert constraints to key=value pairs\n const pairs: string[] = [];\n\n for (const constraint of eid.constraints) {\n switch (constraint.type) {\n case 'uniqueness':\n pairs.push('unique=true');\n break;\n case 'position':\n if (constraint.params && constraint.params.strategy) {\n pairs.push(`pos=${constraint.params.strategy}`);\n }\n break;\n case 'text-proximity':\n if (constraint.params && constraint.params.reference) {\n const escaped = escapeAttributeValue(String(constraint.params.reference));\n pairs.push(`text=\"${escaped}\"`);\n }\n break;\n }\n }\n\n if (pairs.length === 0) {\n return '';\n }\n\n return ` {${pairs.join(',')}}`;\n}\n\n/**\n * Parse a single node string into node structure\n */\nfunction parseNode(nodeStr: string, isAnchor: boolean): AnchorNode | PathNode {\n // Pattern: tag(.class)*[attr=value,attr=value]#position\n\n let remaining = nodeStr;\n const semantics: ElementSemantics = {};\n\n // Extract tag (required)\n const tagMatch = remaining.match(/^([a-z][a-z0-9-]*)/);\n if (!tagMatch) {\n throw new Error(`Invalid node: missing tag name in \"${nodeStr}\"`);\n }\n const tag = tagMatch[1];\n remaining = remaining.slice(tag.length);\n\n // Extract classes\n const classes: string[] = [];\n let classMatch;\n while ((classMatch = remaining.match(/^\\.([a-zA-Z][a-zA-Z0-9-_]*)/))) {\n classes.push(classMatch[1]);\n remaining = remaining.slice(classMatch[0].length);\n }\n if (classes.length > 0) {\n semantics.classes = classes;\n }\n\n // Extract attributes [...]\n const attrMatch = remaining.match(/^\\[([^\\]]+)\\]/);\n if (attrMatch) {\n const attrsStr = attrMatch[1];\n const attributes: Record<string, string> = {};\n\n // Split by comma (but not inside quotes)\n const attrPairs = splitAttributes(attrsStr);\n\n for (const pair of attrPairs) {\n // Match attribute with escaped quotes: key=\"value with \\\" inside\"\n const eqMatch = pair.match(/^([a-z][a-z0-9-]*)(?:=|~=)\"((?:[^\"\\\\]|\\\\.)*)\"/);\n if (eqMatch) {\n const [, key, value] = eqMatch;\n attributes[key] = unescapeAttributeValue(value);\n }\n }\n\n if (Object.keys(attributes).length > 0) {\n // Special handling for pseudo-attributes in SEQL Selector\n if (attributes.text) {\n semantics.text = {\n raw: attributes.text,\n normalized: attributes.text\n };\n delete attributes.text;\n }\n\n if (attributes.id) {\n semantics.id = attributes.id;\n delete attributes.id;\n }\n\n if (attributes.role) {\n semantics.role = attributes.role;\n delete attributes.role;\n }\n\n if (Object.keys(attributes).length > 0) {\n semantics.attributes = attributes;\n }\n }\n\n remaining = remaining.slice(attrMatch[0].length);\n }\n\n // Extract position #N\n let nthChild: number | undefined;\n const posMatch = remaining.match(/^#(\\d+)/);\n if (posMatch) {\n nthChild = parseInt(posMatch[1], 10);\n remaining = remaining.slice(posMatch[0].length);\n }\n\n // Check for remaining unparsed content\n if (remaining.trim()) {\n throw new Error(`Invalid node: unexpected content \"${remaining}\" in \"${nodeStr}\"`);\n }\n\n // Build node\n if (isAnchor) {\n return {\n tag,\n semantics,\n score: 0.7,\n degraded: false,\n };\n } else {\n return {\n tag,\n semantics,\n score: 0.7,\n nthChild,\n };\n }\n}\n\n/**\n * Parse constraints string\n */\nfunction parseConstraints(constraintsStr: string): any[] {\n if (!constraintsStr.trim()) {\n return [];\n }\n\n const constraints: any[] = [];\n const pairs = constraintsStr.split(',').map(p => p.trim());\n\n for (const pair of pairs) {\n const [key, value] = pair.split('=').map(s => s.trim());\n\n switch (key) {\n case 'unique':\n if (value === 'true') {\n constraints.push({\n type: 'uniqueness',\n params: {\n mode: 'strict',\n },\n priority: 90,\n });\n }\n break;\n case 'pos':\n constraints.push({\n type: 'position',\n params: {\n strategy: value,\n },\n priority: 70,\n });\n break;\n case 'text':\n // Remove quotes\n const text = value.replace(/^\"(.*)\"$/, '$1');\n constraints.push({\n type: 'text-proximity',\n params: {\n reference: unescapeAttributeValue(text),\n maxDistance: 5,\n },\n priority: 60,\n });\n break;\n }\n }\n\n return constraints;\n}\n\n/**\n * Split attribute string by comma (respecting quotes)\n */\nfunction splitAttributes(attrsStr: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n\n for (let i = 0; i < attrsStr.length; i++) {\n const char = attrsStr[i];\n\n if (char === '\"' && (i === 0 || attrsStr[i - 1] !== '\\\\')) {\n inQuotes = !inQuotes;\n current += char;\n } else if (char === ',' && !inQuotes) {\n if (current.trim()) {\n result.push(current.trim());\n }\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n result.push(current.trim());\n }\n\n return result;\n}\n\n/**\n * Escape attribute value for SEQL Selector\n */\nfunction escapeAttributeValue(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\') // Backslash\n .replace(/\"/g, '\\\\\"') // Quote\n .replace(/>/g, '\\\\>') // Greater-than\n .replace(/:/g, '\\\\:'); // Colon\n}\n\n/**\n * Unescape attribute value from SEQL Selector\n * Must process in reverse order to avoid double-unescaping\n */\nfunction unescapeAttributeValue(value: string): string {\n return value\n .replace(/\\\\\\\\/g, '\\x00') // Temporary placeholder for backslash\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\>/g, '>')\n .replace(/\\\\:/g, ':')\n .replace(/\\x00/g, '\\\\'); // Restore backslash\n}\n\n// ============================================================================\n// Facade Functions\n// ============================================================================\n\n/**\n * Generate SEQL Selector directly from DOM element\n *\n * This is a convenience function that combines generateEID() and stringifySEQL().\n *\n * @param element - Target DOM element\n * @param generatorOptions - Optional generation options\n * @param stringifyOptions - Optional stringify options\n * @returns SEQL Selector (canonical string)\n *\n * @example\n * ```typescript\n * const button = document.querySelector('.submit-button');\n * const selector = generateSEQL(button);\n * // \"v1: form :: div.actions > button[type=\"submit\",text=\"Submit Order\"]\"\n *\n * // Send to analytics\n * gtag('event', 'click', { element_selector: selector });\n * ```\n */\nexport function generateSEQL(\n element: Element,\n generatorOptions?: GeneratorOptions,\n stringifyOptions?: StringifyOptions\n): string | null {\n const eid = generateEIDInternal(element, generatorOptions);\n if (!eid) {\n return null;\n }\n return stringifySEQL(eid, stringifyOptions);\n}\n\n/**\n * Resolve SEQL Selector directly to DOM elements\n *\n * This is a convenience function that combines parseSEQL() and resolve().\n *\n * @param selector - SEQL Selector string\n * @param root - Root element or document to search in\n * @param options - Optional resolver options\n * @returns Array of matched elements (empty if not found)\n *\n * @example\n * ```typescript\n * // Parse SEQL Selector from analytics\n * const selector = \"v1: form :: button.submit\";\n * const elements = resolveSEQL(selector, document);\n *\n * if (elements.length > 0) {\n * highlightElement(elements[0]);\n * }\n * ```\n */\nexport function resolveSEQL(\n selector: string,\n root: Document | Element,\n options?: ResolverOptions\n): Element[] {\n try {\n const eid = parseSEQL(selector);\n const result = resolveInternal(eid, root, options);\n return result.elements || [];\n } catch (error) {\n console.error('Failed to resolve SEQL Selector:', error);\n return [];\n }\n}\n","import type { ElementIdentity, GeneratorOptions } from '../types';\nimport { generateEID } from '../generator';\nimport type { EIDCache } from './eid-cache';\nimport { getGlobalCache } from './eid-cache';\nimport { isDynamicId } from './id-validator';\n\n/**\n * Elements to skip during batch generation\n */\nconst SKIP_TAGS = new Set([\n 'script',\n 'style',\n 'noscript',\n 'meta',\n 'link',\n 'head',\n 'title',\n]);\n\n/**\n * Options for batch EID generation\n */\nexport interface BatchGeneratorOptions {\n /** Root element to search from (default: document.body) */\n root?: Element | Document;\n /** CSS selector to filter elements (default: '*') */\n filter?: string;\n /** Maximum number of elements to process (default: Infinity) */\n limit?: number;\n /** Progress callback: (current, total) => void */\n onProgress?: (current: number, total: number) => void;\n /** Interval for calling onProgress (default: 100) */\n progressInterval?: number;\n /** Skip elements without semantic features (default: true) */\n skipNonSemantic?: boolean;\n /** Generator options */\n generatorOptions?: GeneratorOptions;\n /** Optional cache instance */\n cache?: EIDCache;\n /** AbortController for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Result of batch generation\n */\nexport interface BatchResult {\n /** Successfully generated Element Identities */\n results: Array<{\n element: Element;\n eid: ElementIdentity;\n generationTimeMs: number;\n }>;\n /** Elements that failed to generate EID */\n failed: Array<{\n element: Element;\n error: string;\n }>;\n /** Statistics */\n stats: {\n totalElements: number;\n successful: number;\n failed: number;\n skipped: number;\n totalTimeMs: number;\n avgTimePerElementMs: number;\n cacheHitRate: number;\n };\n}\n\n/**\n * Element priority for optimized processing order\n */\nenum ElementPriority {\n HIGH = 3, // Elements with ID\n MEDIUM = 2, // Elements with semantic attributes\n LOW = 1, // Other elements\n}\n\n/**\n * Get element priority for processing order\n */\nfunction getElementPriority(element: Element): ElementPriority {\n // High priority: elements with stable ID\n if (element.id && !isDynamicId(element.id)) {\n return ElementPriority.HIGH;\n }\n\n // Medium priority: elements with semantic attributes\n if (\n element.hasAttribute('role') ||\n element.hasAttribute('aria-label') ||\n element.hasAttribute('aria-labelledby') ||\n element.hasAttribute('data-testid') ||\n element.hasAttribute('data-qa') ||\n element.hasAttribute('data-test')\n ) {\n return ElementPriority.MEDIUM;\n }\n\n return ElementPriority.LOW;\n}\n\n/**\n * Check if element should be skipped\n */\nfunction shouldSkipElement(element: Element, skipNonSemantic: boolean): boolean {\n const tag = element.tagName.toLowerCase();\n\n // Skip certain tags\n if (SKIP_TAGS.has(tag)) {\n return true;\n }\n\n // Skip non-semantic elements if requested\n if (skipNonSemantic) {\n const priority = getElementPriority(element);\n if (priority === ElementPriority.LOW) {\n // Check if it's a semantic tag\n const semanticTags = [\n 'form',\n 'main',\n 'nav',\n 'section',\n 'article',\n 'footer',\n 'header',\n 'button',\n 'a',\n 'input',\n 'label',\n 'select',\n 'textarea',\n ];\n if (!semanticTags.includes(tag)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Sort elements by priority for optimized processing\n */\nfunction sortElementsByPriority(elements: Element[]): Element[] {\n return [...elements].sort((a, b) => {\n const priorityA = getElementPriority(a);\n const priorityB = getElementPriority(b);\n return priorityB - priorityA; // Higher priority first\n });\n}\n\n\n/**\n * Generate EID for all elements matching criteria\n */\nexport function generateEIDBatch(\n options: BatchGeneratorOptions = {},\n): BatchResult {\n const startTime = performance.now();\n const {\n root = typeof document !== 'undefined' ? document.body : undefined,\n filter = '*',\n limit = Infinity,\n onProgress,\n progressInterval = 100,\n skipNonSemantic = true,\n generatorOptions = {},\n cache,\n signal,\n } = options;\n\n if (!root) {\n throw new Error('Root element or document is required');\n }\n\n const cacheInstance = cache ?? getGlobalCache();\n const mergedOptions = { ...generatorOptions, cache: cacheInstance };\n\n // Get all elements\n let allElements: Element[];\n try {\n if (root instanceof Document) {\n allElements = Array.from(root.querySelectorAll(filter));\n } else {\n allElements = Array.from(root.querySelectorAll(filter));\n }\n } catch (error) {\n return {\n results: [],\n failed: [],\n stats: {\n totalElements: 0,\n successful: 0,\n failed: 0,\n skipped: 0,\n totalTimeMs: 0,\n avgTimePerElementMs: 0,\n cacheHitRate: 0,\n },\n };\n }\n\n // Filter out skipped elements\n const filteredElements = allElements.filter(\n (el) => !shouldSkipElement(el, skipNonSemantic),\n );\n\n // Sort by priority for optimized processing\n const sortedElements = sortElementsByPriority(filteredElements);\n\n // Apply limit\n const elementsToProcess = sortedElements.slice(0, limit);\n\n const results: BatchResult['results'] = [];\n const failed: BatchResult['failed'] = [];\n let skipped = 0;\n\n const totalElements = elementsToProcess.length;\n let lastProgressCall = 0;\n\n // Process elements\n for (let i = 0; i < elementsToProcess.length; i++) {\n // Check for cancellation\n if (signal?.aborted) {\n break;\n }\n\n const element = elementsToProcess[i];\n\n // Check cache first\n const cachedEID = cacheInstance.getEID(element);\n if (cachedEID) {\n results.push({\n element,\n eid: cachedEID,\n generationTimeMs: 0, // Cached, no generation time\n });\n } else {\n // Generate EID\n const genStart = performance.now();\n try {\n const eid = generateEID(element, mergedOptions);\n const genTime = performance.now() - genStart;\n\n if (eid) {\n results.push({\n element,\n eid,\n generationTimeMs: genTime,\n });\n } else {\n skipped++;\n }\n } catch (error) {\n failed.push({\n element,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Call progress callback\n if (onProgress && i - lastProgressCall >= progressInterval) {\n onProgress(i + 1, totalElements);\n lastProgressCall = i;\n }\n }\n\n // Final progress call\n if (onProgress) {\n onProgress(totalElements, totalElements);\n }\n\n const totalTime = performance.now() - startTime;\n\n // Calculate cache hit rate\n const cacheStats = cacheInstance.getStats();\n const totalCacheOps =\n cacheStats.eidHits + cacheStats.eidMisses + cacheStats.selectorHits + cacheStats.selectorMisses;\n const totalCacheHits = cacheStats.eidHits + cacheStats.selectorHits;\n const cacheHitRate =\n totalCacheOps > 0 ? totalCacheHits / totalCacheOps : 0;\n\n return {\n results,\n failed,\n stats: {\n totalElements,\n successful: results.length,\n failed: failed.length,\n skipped,\n totalTimeMs: totalTime,\n avgTimePerElementMs:\n results.length > 0 ? totalTime / results.length : 0,\n cacheHitRate,\n },\n };\n}\n\n/**\n * Generate EID for specific elements\n */\nexport function generateEIDForElements(\n elements: Element[],\n options: Omit<BatchGeneratorOptions, 'root' | 'filter'> = {},\n): BatchResult {\n const startTime = performance.now();\n const {\n limit = Infinity,\n onProgress,\n progressInterval = 100,\n skipNonSemantic = true,\n generatorOptions = {},\n cache,\n signal,\n } = options;\n\n const cacheInstance = cache ?? getGlobalCache();\n const mergedOptions = { ...generatorOptions, cache: cacheInstance };\n\n // Filter out skipped elements\n const filteredElements = elements.filter(\n (el) => !shouldSkipElement(el, skipNonSemantic),\n );\n\n // Sort by priority\n const sortedElements = sortElementsByPriority(filteredElements);\n\n // Apply limit\n const elementsToProcess = sortedElements.slice(0, limit);\n\n const results: BatchResult['results'] = [];\n const failed: BatchResult['failed'] = [];\n let skipped = 0;\n\n const totalElements = elementsToProcess.length;\n let lastProgressCall = 0;\n\n // Process elements\n for (let i = 0; i < elementsToProcess.length; i++) {\n // Check for cancellation\n if (signal?.aborted) {\n break;\n }\n\n const element = elementsToProcess[i];\n\n // Check cache first\n const cachedEID = cacheInstance.getEID(element);\n if (cachedEID) {\n results.push({\n element,\n eid: cachedEID,\n generationTimeMs: 0,\n });\n } else {\n // Generate EID\n const genStart = performance.now();\n try {\n const eid = generateEID(element, mergedOptions);\n const genTime = performance.now() - genStart;\n\n if (eid) {\n results.push({\n element,\n eid,\n generationTimeMs: genTime,\n });\n } else {\n skipped++;\n }\n } catch (error) {\n failed.push({\n element,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Call progress callback\n if (onProgress && i - lastProgressCall >= progressInterval) {\n onProgress(i + 1, totalElements);\n lastProgressCall = i;\n }\n }\n\n // Final progress call\n if (onProgress) {\n onProgress(totalElements, totalElements);\n }\n\n const totalTime = performance.now() - startTime;\n\n // Calculate cache hit rate\n const cacheStats = cacheInstance.getStats();\n const totalCacheOps =\n cacheStats.eidHits + cacheStats.eidMisses + cacheStats.selectorHits + cacheStats.selectorMisses;\n const totalCacheHits = cacheStats.eidHits + cacheStats.selectorHits;\n const cacheHitRate =\n totalCacheOps > 0 ? totalCacheHits / totalCacheOps : 0;\n\n return {\n results,\n failed,\n stats: {\n totalElements,\n successful: results.length,\n failed: failed.length,\n skipped,\n totalTimeMs: totalTime,\n avgTimePerElementMs:\n results.length > 0 ? totalTime / results.length : 0,\n cacheHitRate,\n },\n };\n}\n"],"names":["CONFIDENCE_WEIGHTS","ANCHOR_SCORE","PATH_SCORE","SEMANTIC_ANCHOR_TAGS","ROLE_ANCHOR_VALUES","SEMANTIC_TAGS","SVG_CHILD_ELEMENTS","SEMANTIC_ATTRIBUTES","ATTRIBUTE_PRIORITY","IGNORED_ATTRIBUTES","DEFAULT_GENERATOR_OPTIONS","DEFAULT_RESOLVER_OPTIONS","isDynamicId","id","ID_REFERENCE_ATTRIBUTES","hasDynamicIdReference","value","AnchorFinder","options","cache","target","cached","current","depth","bestCandidate","rawScore","score","tier","candidate","result","element","tag","role","penalty","DYNAMIC_CLASS_PATTERNS","UTILITY_CLASS_PATTERNS","SEMANTIC_CLASS_PATTERNS","isDynamicClass","className","pattern","isUtilityClass","isSemanticClass","isStableClass","filterStableClasses","classes","cls","scoreClass","filterClasses","semantic","utility","isUtilityClassFromClassifier","getClassScore","scoreClassFromClassifier","cssEscape","str","PathBuilder","anchor","extractor","rawPath","depthOverflow","filteredPath","el","parent","nthChild","index","selector","doc","matches","skippedNodes","node","testPath","testSelector","testMatches","path","nodeIndex","insertIndex","i","parts","elements","attr","normalizeText","text","DEFAULT_OPTIONS","isDynamicHash","hash","p","cleanUrlValue","isAbsolute","baseWithQuery","base","query","cleaned","cleanAttributeValue","attrName","opts","ARIA_STABLE_ATTRIBUTES","ARIA_STATE_ATTRIBUTES","DATA_STATE_ATTRIBUTES","LIBRARY_DATA_PREFIXES","DATA_ID_PATTERNS","HTML_STABLE_ATTRIBUTES","HTML_STATE_ATTRIBUTES","GENERATED_ID_PATTERNS","isStableAttribute","name","prefix","SemanticExtractor","semantics","attrs","rawText","normalized","maxLength","truncatedRaw","truncatedNorm","texts","trimmed","SvgFingerprinter","shape","fingerprint","d","title","cmd","match","w","h","rx","ry","x1","y1","x2","y2","angle","style","char","calculateConfidence","eid","uniquenessBonus","anchorScore","avgPathScore","sum","n","targetScore","confidence","degradationPenalty","calculateElementScore","semanticsCount","hasId","hasRole","LRUCache","maxSize","key","firstKey","EIDCache","results","_element","total","totalHits","totalMisses","createEIDCache","globalCache","getGlobalCache","resetGlobalCache","generateEID","cachedEID","anchorFinder","pathBuilder","semanticExtractor","svgFingerprinter","anchorResult","anchorElement","anchorDegraded","anchorTag","anchorParent","anchorNthChild","anchorSemantics","anchorNode","pathResult","targetSemantics","isSvgElement","targetParent","targetNthChild","targetNode","constraints","fallback","isDegraded","degradationReason","getDegradationReason","CssGenerator","anchorSelector","nodeSelector","targetBaseSelector","isSvgChild","hasSvgInPath","baseSelector","svgIndexInPath","svgIndexInParts","beforeAndIncludingSvg","afterSvgBeforeTarget","baseSelectorMatches","fullPathSelector","root","maxClasses","targetTag","currentSelector","extraClassesAdded","usedNthOfType","availableTargetClasses","targetElement","anchors","targetCandidates","scoredCandidates","a","b","pathSelector","eidPath","domPath","minLength","domEl","eidNode","matchingClasses","matchingAttrs","lengthDiff","elText","attrValue","pathNode","fullPath","parentWithAttrs","baseCandidates","attrSelector","attrCandidates","stableClasses","parentWithClass","classSelector","classCandidates","s","pathElements","strategies","meaningfulTags","simplePath","targetSelector","simplePathWithSemantics","pathNodeMap","eidPathIndex","disambiguated","tempSelector","matchingPathEl","targetEl","strategy","c","candidates","normalizedText","selectorWithClass","sortedAttrs","cleanedValue","selectorWithAttr","selectorWithNth","allAnchors","matchingAnchor","nthIndex","siblings","attributes","classesToAdd","escaped","SemanticsMatcher","textToMatch","ConstraintsEvaluator","constraint","params","top","topRect","left","leftRect","row","_","prev","j","FallbackHandler","dom","onMissing","error","message","onMultiple","bestElement","bestScore","maxScore","elementClasses","matchCount","elementText","resolve","cssGenerator","semanticsMatcher","constraintsEvaluator","fallbackHandler","errorMessage","filtered","constrained","sortedConstraints","sortByPriority","validateEID","errors","warnings","isEID","obj","DEFAULT_STRINGIFY_OPTIONS","getAttributePriority","isUniqueAttribute","isTextPII","stringifySEQL","version","stringifyNode","stringifyConstraints","parseSEQL","versionMatch","remaining","anchorMatch","anchorStr","constraintsMatch","constraintsStr","nodes","targetStr","pathStrs","parseNode","parseConstraints","isTarget","attrStrings","rawAttributes","processedAttrs","priority","topAttrs","escapeAttributeValue","finalAttrs","hasStrongIdentifier","limitedClasses","pairs","nodeStr","isAnchor","tagMatch","classMatch","attrMatch","attrsStr","attrPairs","splitAttributes","pair","eqMatch","unescapeAttributeValue","posMatch","inQuotes","generateSEQL","generatorOptions","stringifyOptions","generateEIDInternal","resolveSEQL","resolveInternal","SKIP_TAGS","getElementPriority","shouldSkipElement","skipNonSemantic","sortElementsByPriority","priorityA","generateEIDBatch","startTime","filter","limit","onProgress","progressInterval","signal","cacheInstance","mergedOptions","allElements","filteredElements","elementsToProcess","failed","skipped","totalElements","lastProgressCall","genStart","genTime","totalTime","cacheStats","totalCacheOps","totalCacheHits","cacheHitRate","generateEIDForElements"],"mappings":"+NAiBO,MAAMA,EAAqB,CAChC,OAAQ,GACR,KAAM,GACN,OAAQ,GACR,WAAY,EACd,EAMaC,EAAe,CAC1B,aAAc,GACd,KAAM,GACN,WAAY,GACZ,UAAW,GACX,YAAa,IACb,wBAAyB,EACzB,qBAAsB,IACtB,eAAgB,EAClB,EAKaC,GAAa,CACxB,wBAAyB,EAC3B,EAMaC,EAAuB,CAClC,OACA,OACA,MACA,UACA,UACA,SACA,QACF,EAMaC,EAAqB,CAChC,OACA,aACA,OACA,SACA,cACA,gBACA,SACA,QACF,EAMaC,GAAgB,CAE3B,UACA,QACA,UACA,aACA,SACA,SACA,SACA,OACA,OACA,MACA,UACA,UACA,OAEA,SACA,WACA,WACA,OACA,QACA,QACA,SACA,QACA,WACA,SACA,SACA,WACA,SACA,WAEA,IACA,QACA,QACA,SACA,SACA,OAEA,aACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,IACA,MACA,KACA,KACA,KACA,KACA,KACA,KAEA,UACA,MACA,WACA,QACA,QACA,KACA,QACA,KACA,QACA,KAEA,MACA,OACA,SACA,OACA,OACA,WACA,UACA,UACA,IACA,OACA,KACF,EAMaC,GAAqB,CAChC,OACA,OACA,SACA,OACA,WACA,UACA,UACA,IACA,OACA,MACA,OACA,WACA,MACF,EAKaC,GAAsB,CACjC,aACA,kBACA,mBACA,OACA,OACA,cACA,UACA,YACA,OACA,QACA,cACA,KACF,EAMaC,EAA6C,CAExD,cAAe,IACf,UAAW,GACX,UAAW,GACX,YAAa,GACb,eAAgB,GAGhB,aAAc,GACd,kBAAmB,GACnB,mBAAoB,GAGpB,KAAQ,GACR,KAAQ,GACR,IAAO,GACP,KAAQ,GACR,KAAQ,GACR,IAAO,GACP,MAAS,GACT,IAAO,GACP,YAAe,GAGf,SAAU,GAGV,SAAU,EACZ,EAKaC,OAAyB,IAAI,CACxC,KACA,QACA,QACA,QACA,WACA,iBACF,CAAC,EAMYC,GAAyG,CACpH,aAAc,GACd,qBAAsB,GACtB,oBAAqB,GACrB,eAAgB,GAChB,sBAAuB,GACvB,OAA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"}
|