{"_id":"@a11yeco/core","_rev":"5-1b0f606c481592f8d2870e68d1c0ba5d","name":"@a11yeco/core","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@a11yeco/core","version":"0.1.0","_id":"@a11yeco/core@0.1.0","maintainers":[{"name":"amber2209","email":"amberanand.work@gmail.com"}],"dist":{"shasum":"0af772c4e0fd9eaf34acea4cc224143e6dbfb52f","tarball":"https://registry.npmjs.org/@a11yeco/core/-/core-0.1.0.tgz","fileCount":49,"integrity":"sha512-mCB5PZDFvzx7TIbH8MFEK+VpbbJf14WIxytSW9VaX2EjdkxZ/3YDZA7NAgnH67Z5XY7RFIooCZXCNlWzHSt4ew==","signatures":[{"sig":"MEUCIG6tD7VQceuKiORXFKSHJZkiwBPKqBEyEVKNmknzG9DuAiEAty/TwFID0NS0YvpmO/gQYX+jl+gYYRj0cpDMgsVN7PU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":95324},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"5fe6a1bb98cf0ee35549d8a74b33cf94c917e7a6","scripts":{"dev":"tsc -p tsconfig.json --watch","test":"npm run build && node --test dist/__tests__/*.test.js","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"amber2209","email":"amberanand.work@gmail.com"},"_npmVersion":"11.7.0","description":"A11yEco core — platform-agnostic WCAG scanning, scoring, diffing, and analytics","directories":{},"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/core_0.1.0_1779300738223_0.5795590029172673","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@a11yeco/core","version":"0.1.1","_id":"@a11yeco/core@0.1.1","maintainers":[{"name":"amber2209","email":"amberanand.work@gmail.com"}],"dist":{"shasum":"a7f40197bbb38159409ebd31e3dbda313f822c8f","tarball":"https://registry.npmjs.org/@a11yeco/core/-/core-0.1.1.tgz","fileCount":50,"integrity":"sha512-uI+v+opykOc9XhGUrDkF0k0t7TFY1Pqcx9VONPorQ+CBHRvzv5GtUjszFBw9X8ByeCiC5oP6i1IAQrvjBRvywA==","signatures":[{"sig":"MEUCIFYRPNQnzpCn21iTVu6Cgn5I8gLbuapr1yKr4nnxAc4AAiEA61FAcXos7nsMNiuIJ0GMXhUVYpDeczxDLfxFRCTWWXg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":100704},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"98b09c8d155e0b9ef6590a0b5c63ccd7e31d468d","scripts":{"dev":"tsc -p tsconfig.json --watch","test":"npm run build && node --test dist/__tests__/*.test.js","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"amber2209","email":"amberanand.work@gmail.com"},"_npmVersion":"11.7.0","description":"A11yEco core — platform-agnostic WCAG scanning, scoring, diffing, and analytics","directories":{},"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/core_0.1.1_1785253855429_0.2354146683622338","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@a11yeco/core","version":"0.2.0","_id":"@a11yeco/core@0.2.0","maintainers":[{"name":"amber2209","email":"amberanand.work@gmail.com"}],"dist":{"shasum":"1269926c55269da82182959ebf99a8d5f9fee535","tarball":"https://registry.npmjs.org/@a11yeco/core/-/core-0.2.0.tgz","fileCount":58,"integrity":"sha512-AWnn+P81Rdg+GAwjzFYUbszAXz5A7R+7QZLLzXh2jOlvrWYECY5q4Y61xtlBZKbRicSuIJqdUjLLiZI34fJZpg==","signatures":[{"sig":"MEUCIQCPGTOF7kF4jZj79sy1tWFbgb40haXs1p7z3LbavqdDrgIgY4xyT6dYKRhsDGv+gcPoSXWKH5jBWn0QCWuewcsje6c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":128776},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"06569fc16a86d9c084f59f958dec01fb90304b34","scripts":{"dev":"tsc -p tsconfig.json --watch","test":"npm run build && node --test dist/__tests__/*.test.js","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"amber2209","email":"amberanand.work@gmail.com"},"_npmVersion":"11.7.0","description":"A11yEco core — platform-agnostic WCAG scanning, scoring, diffing, and analytics","directories":{},"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/core_0.2.0_1786733267649_0.09648788974387679","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@a11yeco/core","version":"0.3.0","_id":"@a11yeco/core@0.3.0","maintainers":[{"name":"amber2209","email":"amberanand.work@gmail.com"}],"dist":{"shasum":"2080125a9ecf235423ec8d114fb38ab29676d2f7","tarball":"https://registry.npmjs.org/@a11yeco/core/-/core-0.3.0.tgz","fileCount":66,"integrity":"sha512-aVk5kQnTeXn4E59LbHwunqY8J01pBMMkt2unyA7v2ZjgLNOsRErTVxgq8fzg3mP5JlxPAs4XBBhBk8w7q3DnKw==","signatures":[{"sig":"MEQCIFSBHipN7CUDuaezEHquXVemWgH6oAsurTRxz9fQVPF9AiAx84HiOH9dbzPe+fokNGO3btCdWQ+yk1EbaR9VN5kxeQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":161901},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"c898ead4d70b201bafac1a8537225d81a500bbfa","scripts":{"dev":"tsc -p tsconfig.json --watch","test":"npm run build && node --test dist/__tests__/*.test.js","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"amber2209","email":"amberanand.work@gmail.com"},"_npmVersion":"11.7.0","description":"A11yEco core — platform-agnostic WCAG scanning, scoring, diffing, and analytics","directories":{},"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/core_0.3.0_1786991066700_0.7433132784048919","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@a11yeco/core","version":"0.4.0","description":"A11yEco core — platform-agnostic WCAG scanning, scoring, diffing, and analytics","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","dev":"tsc -p tsconfig.json --watch","test":"npm run build && node --test dist/__tests__/*.test.js"},"devDependencies":{"typescript":"^5.6.3"},"gitHead":"ffaa2f0b6eaa746ef34d0c92f6675c2c950e915b","_id":"@a11yeco/core@0.4.0","_nodeVersion":"24.8.0","_npmVersion":"11.7.0","dist":{"integrity":"sha512-YHcveopZuVp0rcdqmWCX7eIGvKmuAp4UFUqC1X7juA0fkZvRbMDvAeas9X57Y/An7qxMZD1H1yt5FsPyAZUCnQ==","shasum":"936ec808cef442123f02ce56bc1921d60eda2b32","tarball":"https://registry.npmjs.org/@a11yeco/core/-/core-0.4.0.tgz","fileCount":66,"unpackedSize":162309,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICATZp4l6pu03oxYiaZWBt2K8Ik7vaaM5y72M7e0GtW1AiEAtSDneLaxcoUk71MKWX1/saBEcVbTPEQ8Dj1UegYYLVI="}]},"_npmUser":{"name":"amber2209","email":"amberanand.work@gmail.com"},"directories":{},"maintainers":[{"name":"amber2209","email":"amberanand.work@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/core_0.4.0_1787168202293_0.2627944883608089"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-20T18:12:18.109Z","modified":"2026-08-19T19:36:42.652Z","0.1.0":"2026-05-20T18:12:18.390Z","0.1.1":"2026-07-28T15:50:55.575Z","0.2.0":"2026-08-14T18:47:48.014Z","0.3.0":"2026-08-17T18:24:26.861Z","0.4.0":"2026-08-19T19:36:42.465Z"},"description":"A11yEco core — platform-agnostic WCAG scanning, scoring, diffing, and analytics","maintainers":[{"name":"amber2209","email":"amberanand.work@gmail.com"}],"readme":"# @a11yeco/core\n\n**Platform-agnostic WCAG 2.2 scanning, scoring, diffing, and analytics** — the shared engine\npowering every A11yEco tool (Chrome extension, CLI, VS Code extension).\n\n```\nnpm install @a11yeco/core\n```\n\nZero runtime dependencies. Works in Node.js, browsers, and editors.\n\n📖 **Website:** [a11yeco.com](https://a11yeco.com)\n\n---\n\n## API overview\n\n```ts\nimport {\n  // Types\n  type ScanResult, type ScanIssue, type Severity, type WcagLevel,\n  type ScanProfile, DEFAULT_PROFILE,\n\n  // Scoring\n  computeScore,\n\n  // Contrast analysis\n  contrastRatio, classifyContrast, parseColor, relativeLuminance,\n\n  // Fingerprinting & diffing\n  fingerprintNode, diffScans,\n  type IssueRef, type ScanDiff,\n\n  // Dashboard analytics\n  computePourHealth, computeUserGroupImpact, computePriorityQueue,\n  computeDebt, computeWhatChanged,\n  type PourHealth, type UserGroupImpact,\n  type PriorityItem, type DebtSummary, type DebtTrendPoint,\n\n  // URL normalisation\n  normalizeUrl,\n} from \"@a11yeco/core\";\n```\n\n---\n\n## Scoring\n\n```ts\nimport { computeScore } from \"@a11yeco/core\";\n\nconst score = computeScore(scanResult);\n// 0–100.  100 = perfect, 0 = maximum penalty.\n```\n\nEach unique violation rule contributes its impact weight once, regardless of how many\nDOM nodes match it. Weights:\n\n| Impact    | Penalty |\n|-----------|---------|\n| critical  | −12     |\n| serious   | −8      |\n| moderate  | −4      |\n| minor     | −2      |\n\n---\n\n## Contrast\n\n```ts\nimport { contrastRatio, classifyContrast } from \"@a11yeco/core\";\n\nconst ratio = contrastRatio(\"#ccc\", \"#fff\");\n// → 3.26...\n\nconst pass = classifyContrast(ratio);\n// → { aaNormal: false, aaLarge: true, aaaNormal: false, aaaLarge: false }\n```\n\nSupported colour formats: hex (`#RGB`, `#RRGGBB`, `#ARGB`, `#AARRGGBB`)\nand `rgb()` / `rgba()`.\n\n---\n\n## Fingerprinting\n\nStable fingerprints let you track the same issue across rescans, so you can tell\nwhether a violation is genuinely fixed or just shifted.\n\n```ts\nimport { fingerprintNode } from \"@a11yeco/core\";\n\nconst fp = fingerprintNode(issue, node);\n// djb2 hash of: ruleId | canonical-selector | normalised-html\n```\n\nAttribute values that churn between renders (`id-XXXX`, `data-reactid`, `nonce`, …)\nare stripped before hashing so they don't break fingerprint stability.\n\n---\n\n## Diffing\n\nCompare two scans of the same URL to get added, resolved, and unchanged issues:\n\n```ts\nimport { diffScans } from \"@a11yeco/core\";\n\nconst diff = diffScans({ before, after });\n// → { added: [...], resolved: [...], unchanged: [...],\n//     severityChanges: [...], scoreDelta: -4,\n//     totals: { addedCount: 2, resolvedCount: 5, ... } }\n```\n\nDiffing is per-node-fingerprint, not per-rule-id. Fixing 3 of 5 instances of\n`color-contrast` shows up as 3 resolved and 2 unchanged — not \"no change\".\n\n---\n\n## Analytics\n\nPure functions over saved scan history. Bring your own storage — pass in arrays\nof `ScanRecord` and `IssueMetaRecord`.\n\n### POUR health\n\n```ts\nimport { computePourHealth } from \"@a11yeco/core\";\n\nconst health = computePourHealth(allScans);\n// → {\n//     perceivable: { score: 88, openIssueCount: 3 },\n//     operable:    { score: 96, openIssueCount: 1 },\n//     ...\n//   }\n```\n\n### User-group impact\n\n```ts\nimport { computeUserGroupImpact } from \"@a11yeco/core\";\n\nconst impact = computeUserGroupImpact(allScans);\n// → { \"screen-reader\": 4, \"low-vision\": 2, ... }\n```\n\n### Priority queue\n\nRanks open issues by severity × pages affected × age:\n\n```ts\nimport { computePriorityQueue } from \"@a11yeco/core\";\n\nconst queue = computePriorityQueue(allScans, issueMeta, 25);\n// → PriorityItem[] sorted by score descending\n```\n\n### Accessibility debt\n\nTrend of open issues over time, with mean-time-to-fix:\n\n```ts\nimport { computeDebt } from \"@a11yeco/core\";\n\nconst debt = computeDebt(allScans);\n// → { trend: [{ ts, openCount }, ...], medianDaysToFix: 4.2, ... }\n```\n\n### What changed\n\n```ts\nimport { computeWhatChanged } from \"@a11yeco/core\";\n\nconst changed = computeWhatChanged(allScans);\n// → { totalAdded: 3, totalResolved: 7, perUrl: [...] }\n```\n\n---\n\n## URL normalisation\n\nStrips fragments and tracking query parameters (`utm_*`, `fbclid`, `gclid`, `ref`, …)\nso the same page visited via different campaigns maps to the same record:\n\n```ts\nimport { normalizeUrl } from \"@a11yeco/core\";\n\nnormalizeUrl(\"https://site.com/page?utm_source=twitter&ref=home#section\");\n// → \"https://site.com/page\"\n```\n\n---\n\n## Types\n\nKey types exported:\n\n```ts\ntype WcagLevel = \"A\" | \"AA\" | \"AAA\";\ntype Severity = \"minor\" | \"moderate\" | \"serious\" | \"critical\";\ntype IssueGroup = \"violations\" | \"incomplete\" | \"passes\" | \"inapplicable\";\n\ntype ScanProfile = {\n  wcagLevels: WcagLevel[];\n  bestPractices: boolean;\n  experimental: boolean;\n  scanIframes: boolean;\n  ignoredRules: string[];\n};\n\ntype ScanResult = {\n  url: string;\n  timestamp: number;\n  engineVersion: string;\n  profile: ScanProfile;\n  counts: Record<IssueGroup, number>;\n  issues: Record<IssueGroup, ScanIssue[]>;\n};\n```\n\nAlso exported: `ScanRecord`, `IssueMetaRecord`, `IssueRef`, `ScanDiff`,\n`ContrastSample`, `ContrastIssue`, `HeadingNode`, `LandmarkNode`,\n`FocusableNode`, `AriaNodeView`, `ReadingOrderItem`, and several analytics\ninput/output types.\n\n---\n\n## Development\n\n```bash\n# Build\nnpm run build\n\n# Type-check (no emit)\nnpm run typecheck\n\n# Watch mode\nnpm run dev\n\n# Test\nnpm test\n```\n","readmeFilename":"README.md"}