From 0330f61cef1c88291755beb373c81bef5f15be70 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 19:46:15 +0000 Subject: [PATCH] Sync generated provider output --- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .grok/skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .kiro/skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .pi/skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .qoder/skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .../skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .trae/skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- .vibe/skills/impeccable/scripts/hook-lib.mjs | 13 +- .../detector/browser/injected/index.mjs | 400 ++++++++++++++++- .../impeccable/scripts/detector/cli/main.mjs | 32 +- .../detector/detect-antipatterns-browser.js | 424 +++++++++++++++++- .../detector/engines/browser/detect-url.mjs | 4 +- .../engines/static-html/detect-html.mjs | 4 +- .../impeccable/scripts/detector/findings.mjs | 11 +- .../detector/registry/antipatterns.mjs | 7 +- .../scripts/detector/rules/checks.mjs | 22 +- plugin/skills/impeccable/scripts/hook-lib.mjs | 13 +- 144 files changed, 13680 insertions(+), 992 deletions(-) diff --git a/.agents/skills/impeccable/scripts/detector/browser/injected/index.mjs b/.agents/skills/impeccable/scripts/detector/browser/injected/index.mjs index febf7f297..76fc558e5 100644 --- a/.agents/skills/impeccable/scripts/detector/browser/injected/index.mjs +++ b/.agents/skills/impeccable/scripts/detector/browser/injected/index.mjs @@ -1228,14 +1228,17 @@ if (IS_BROWSER) { isHidden: isElementHidden(el), findings: findings.map(f => { const ap = ANTIPATTERNS.find(a => a.id === (f.type || f.id)); + const severity = f.severity || ap?.severity || 'warning'; return { type: f.type || f.id, category: ap ? ap.category : 'quality', - severity: f.severity || ap?.severity || 'warning', + severity, // Advisory findings (em-dash overuse, etc.) are surfaced but never // treated as failures; carry the flag so the overlay/extension can // render them with the mildest affordance and consumers can filter. - advisory: (ap && ap.advisory === true) || f.advisory === true, + // Per-finding promotions override the registry default, so derive + // this strictly from the effective severity. + advisory: severity === 'advisory', detail: f.detail || f.snippet, ignoreValue: f.ignoreValue || f.value || '', name: ap ? ap.name : (f.type || f.id), @@ -1277,6 +1280,381 @@ if (IS_BROWSER) { else groupMap.set(el, [...kept]); } + function pseudoElementHostSelector(selector) { + const raw = String(selector || ''); + const legacyNames = new Set(['before', 'after', 'first-letter', 'first-line']); + const isNameChar = char => /[a-zA-Z0-9_-]/.test(char || ''); + const consumeFunction = (start) => { + let depth = 0; + let quote = ''; + for (let i = start; i < raw.length; i += 1) { + const char = raw[i]; + if (char === '\\') { + i += 1; + continue; + } + if (quote) { + if (char === quote) quote = ''; + continue; + } + if (char === '"' || char === "'") { + quote = char; + continue; + } + if (char === '(') depth += 1; + if (char === ')' && --depth === 0) return i + 1; + } + return raw.length; + }; + + let output = ''; + let found = false; + for (let i = 0; i < raw.length;) { + const char = raw[i]; + if (char === '\\') { + output += raw.slice(i, Math.min(raw.length, i + 2)); + i += 2; + continue; + } + if (char === '"' || char === "'") { + const quote = char; + const start = i; + i += 1; + while (i < raw.length) { + if (raw[i] === '\\') { + i += 2; + continue; + } + const value = raw[i]; + i += 1; + if (value === quote) break; + } + output += raw.slice(start, i); + continue; + } + if (char !== ':') { + output += char; + i += 1; + continue; + } + + let end = i + 1; + let isPseudoElement = false; + if (raw[end] === ':') { + end += 1; + const nameStart = end; + while (isNameChar(raw[end])) end += 1; + isPseudoElement = end > nameStart; + } else { + const nameStart = end; + while (isNameChar(raw[end])) end += 1; + isPseudoElement = legacyNames.has(raw.slice(nameStart, end).toLowerCase()); + } + if (!isPseudoElement) { + output += char; + i += 1; + continue; + } + if (raw[end] === '(') end = consumeFunction(end); + found = true; + if (!output || /[\s>+~,]/.test(output[output.length - 1])) output += '*'; + i = end; + } + if (!found) return null; + return output.trim().replace(/,\s*(?=,|$)/g, ''); + } + + function selectorNodesForLiveDom(root, selector) { + const raw = String(selector || '').trim(); + if (!raw) return null; + const fallback = pseudoElementHostSelector(raw); + if (fallback == null) { + // An empty result from a valid full selector is authoritative. In + // particular, do not broaden inactive :hover/:focus/:not() rules to + // their host element by stripping pseudo-classes. + try { return Array.from(root.querySelectorAll(raw)); } + catch { return null; } + } + + // Resolve pseudo-elements to their originating live elements. An attached + // pseudo-element (`.card::before`) belongs to the element before it, while + // a hostless pseudo-element after a combinator (`main > ::before`) belongs + // to a matching element at that position (`main > *`). Replacing every + // pseudo indiscriminately with an empty string leaves the latter as the + // invalid selector `main >` and makes absent hosts indistinguishable from + // selectors the DOM API cannot parse. + if (!fallback || /^[,\s]*$/.test(fallback)) return null; + try { return Array.from(root.querySelectorAll(fallback)); } + catch { return null; } + } + + let containerProbeSequence = 0; + + function isContainerCssRule(rule) { + return rule?.constructor?.name === 'CSSContainerRule' + || /^\s*@container\b/i.test(rule?.cssText || ''); + } + + function styleRuleAppliesToLiveMatches(rule, matches) { + const style = rule?.style; + if (!style || !matches?.length || typeof getComputedStyle !== 'function') return false; + const sequence = ++containerProbeSequence; + const property = `--impeccable-container-probe-${sequence}-${Math.random().toString(36).slice(2)}`; + const value = `impeccable-container-active-${sequence}`; + const previousValue = style.getPropertyValue(property); + const previousPriority = style.getPropertyPriority(property); + try { + style.setProperty(property, value, 'important'); + } catch { + return false; + } + + const pseudoElements = [...new Set( + String(rule.selectorText || '').match(/::[a-zA-Z-]+(?:\([^)]*\))?/g) || [], + )]; + try { + return matches.some(el => [null, ...pseudoElements].some(pseudo => { + try { + const computed = pseudo ? getComputedStyle(el, pseudo) : getComputedStyle(el); + return computed.getPropertyValue(property).trim() === value; + } catch { + return false; + } + })); + } finally { + if (previousValue) style.setProperty(property, previousValue, previousPriority); + else style.removeProperty(property); + } + } + + function conditionalCssRuleIsActive(rule) { + const type = Number(rule?.type); + const constructorName = rule?.constructor?.name || ''; + if (constructorName === 'CSSMediaRule' || type === 4) { + const condition = rule.conditionText || rule.media?.mediaText || ''; + if (!condition || typeof window.matchMedia !== 'function') return true; + try { return window.matchMedia(condition).matches; } + catch { return true; } + } + if (constructorName === 'CSSSupportsRule' || type === 12) { + const condition = rule.conditionText || ''; + if (!condition || typeof CSS === 'undefined' || typeof CSS.supports !== 'function') return true; + try { return CSS.supports(condition); } + catch { return true; } + } + return true; + } + + function splitCssCommaList(value) { + const parts = []; + let current = ''; + let quote = ''; + let escaped = false; + for (const char of String(value || '')) { + if (escaped) { + current += char; + escaped = false; + continue; + } + if (char === '\\') { + current += char; + escaped = true; + continue; + } + if (quote) { + current += char; + if (char === quote) quote = ''; + continue; + } + if (char === '"' || char === "'") { + quote = char; + current += char; + continue; + } + if (char === ',') { + parts.push(current); + current = ''; + continue; + } + current += char; + } + parts.push(current); + return parts; + } + + function normalizeAnimationName(value) { + const name = String(value || '').trim(); + if (name.length >= 2 && name[0] === name[name.length - 1] && (name[0] === '"' || name[0] === "'")) { + return name.slice(1, -1); + } + return name; + } + + function animationNamesDeclaredByRule(rule) { + const style = rule?.style; + if (!style) return []; + let value = ''; + try { + value = style.animationName + || style.getPropertyValue?.('animation-name') + || style.webkitAnimationName + || style.getPropertyValue?.('-webkit-animation-name') + || ''; + } catch { + return []; + } + return splitCssCommaList(value) + .map(normalizeAnimationName) + .filter(name => name && name.toLowerCase() !== 'none'); + } + + function keyframesRuleName(rule, cssText) { + const constructorName = rule?.constructor?.name || ''; + const type = Number(rule?.type); + const isKeyframes = constructorName === 'CSSKeyframesRule' + || constructorName === 'WebKitCSSKeyframesRule' + || type === 7 + || /^\s*@(?:-webkit-)?keyframes\b/i.test(cssText); + if (!isKeyframes) return ''; + const match = String(cssText || '').match(/^\s*@(?:-webkit-)?keyframes\s+([^\s{]+)/i); + return normalizeAnimationName(rule?.name || match?.[1] || ''); + } + + function cssPropertyName(property) { + if (property.startsWith('--')) return property; + return property.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`); + } + + function resolvedAnimationKeyframes(candidateNames) { + if (typeof document.getAnimations !== 'function') return null; + let animations; + try { animations = document.getAnimations(); } + catch { return null; } + + const resolved = new Map(); + const metadata = new Set(['offset', 'computedOffset', 'easing', 'composite']); + for (const animation of animations) { + const name = normalizeAnimationName(animation?.animationName || ''); + if (!name || !candidateNames.has(name) || resolved.has(name)) continue; + let frames; + try { frames = animation.effect?.getKeyframes?.() || []; } + catch { continue; } + const blocks = []; + for (const frame of frames) { + const rawOffset = Number.isFinite(frame.computedOffset) ? frame.computedOffset : frame.offset; + if (!Number.isFinite(rawOffset)) continue; + const offset = Math.round(rawOffset * 1000000) / 10000; + const declarations = Object.entries(frame) + .filter(([property, value]) => !metadata.has(property) && value != null && value !== '') + .map(([property, value]) => `${cssPropertyName(property)}: ${value};`); + const easing = String(frame.easing || '').trim(); + if (easing && easing.toLowerCase() !== 'linear') { + declarations.push(`animation-timing-function: ${easing};`); + } + if (declarations.length === 0) continue; + blocks.push(`${offset}% { ${declarations.join(' ')} }`); + } + if (blocks.length > 0) resolved.set(name, `@keyframes ${name} { ${blocks.join(' ')} }`); + } + return resolved; + } + + // Read CSS that is absent from document.outerHTML. Inline