llynt 0.2.4 → 0.2.5
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 -2
- package/cli.js +102 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ export LLYNT_API_KEY=<your-key>
|
|
|
21
21
|
npx llynt check https://your-app.dev
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Same command.
|
|
24
|
+
Same command. 26 rules, SARIF output, PR gate ready.
|
|
25
25
|
|
|
26
26
|
## What it checks
|
|
27
27
|
|
|
@@ -29,7 +29,7 @@ Llynt checks what the browser actually renders -- computed layout, accessibility
|
|
|
29
29
|
|
|
30
30
|
**Anonymous (7 rules):** contrast, hit targets, overflow, broken assets, text overflow, overlap, rendering
|
|
31
31
|
|
|
32
|
-
**Free account (
|
|
32
|
+
**Free account (26 rules):** adds layout drift, focus order, occlusion, clipping, visual order, tab order, readability, and more
|
|
33
33
|
|
|
34
34
|
## How it works
|
|
35
35
|
|
package/cli.js
CHANGED
|
@@ -1,51 +1,128 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var qe=Object.create;var ue=Object.defineProperty;var We=Object.getOwnPropertyDescriptor;var Ye=Object.getOwnPropertyNames;var Je=Object.getPrototypeOf,Ke=Object.prototype.hasOwnProperty;var z=(e,r)=>()=>(e&&(r=e(e=0)),r);var Ge=(e,r)=>{for(var t in r)ue(e,t,{get:r[t],enumerable:!0})},Xe=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of Ye(r))!Ke.call(e,o)&&o!==t&&ue(e,o,{get:()=>r[o],enumerable:!(s=We(r,o))||s.enumerable});return e};var A=(e,r,t)=>(t=e!=null?qe(Je(e)):{},Xe(r||!e||!e.__esModule?ue(t,"default",{value:e,enumerable:!0}):t,e));function Ae(e){let r=e.trim();if(!r)throw new Error("LLYNT_API_URL is empty");return r.includes("/v1/check")?r:r.replace(/\/+$/g,"")+"/v1/check"}function at(e){return new Promise((r,t)=>{oe.gzip(e,(s,o)=>{s?t(s):r(o)})})}function lt(e){return new Promise((r,t)=>{oe.brotliCompress(e,(s,o)=>{s?t(s):r(o)})})}function ct(e,r,t,s){return new Promise((o,d)=>{let i=(e.protocol==="https:"?it:nt).request({...r,protocol:e.protocol,hostname:e.hostname,port:e.port,path:e.pathname+e.search},l=>{let f=[];l.on("data",y=>f.push(y)),l.on("end",()=>{o({statusCode:l.statusCode??0,body:Buffer.concat(f)})})});i.on("error",l=>d(l)),i.setTimeout(s,()=>{i.destroy(new Error(`Hosted API request timed out after ${s}ms`))}),i.end(t)})}async function Ce(e,r){let t=new URL(e.apiUrl),s=e.timeoutMs??3e4,o=Buffer.from(JSON.stringify(r),"utf8"),d=e.compression??"gzip",m=d==="identity"?o:d==="br"?await lt(o):await at(o),i={"content-type":"application/json",accept:"application/json","user-agent":"llynt-hosted-client/0","content-length":String(m.length)};d!=="identity"&&(i["content-encoding"]=d),e.apiKey&&(i.authorization=`Bearer ${e.apiKey}`);let{statusCode:l,body:f}=await ct(t,{method:"POST",headers:i},m,s),y=f.toString("utf8");if(l<200||l>=300)throw new Error(`Hosted API error (${l}): ${y.slice(0,500)}`);let u=JSON.parse(y);if(!u||u._schemaVersion!=="llynt-hosted-check-result@0"||!Array.isArray(u.issues))throw new Error("Hosted API response schema mismatch");return u}var nt,it,oe,de=z(()=>{"use strict";nt=A(require("http")),it=A(require("https")),oe=A(require("zlib"))});async function Re(e,r,t){let s=t?.maxDepth??gt,o=t?.maxNodes??yt,d=typeof t?.includeComputedStyles=="boolean"?t.includeComputedStyles?"all":"none":t?.computedStyleMode??"essential",m=Array.isArray(t?.computedStyleProps)?t.computedStyleProps:void 0,i=t?.internComputedStyles??!0,l=t?.includeUrlAttributes??!1,f=t?.redactUrlAttributes??!0,y=await e.evaluate(({selector:P,maxDepth:L,maxNodes:j,computedStyleMode:N,computedStyleProps:g,essentialProps:b,internComputedStyles:v,includeUrlAttributes:_,redactUrlAttributes:H})=>{function C(a){let c=2166136261;for(let h=0;h<a.length;h++)c^=a.charCodeAt(h),c=Math.imul(c,16777619);return(c>>>0).toString(36)}function M(a){let c=Object.keys(a).sort(),h=[];for(let n of c)h.push(`${n}=${a[n]}`);return h.join(";")}function le(a){try{let c=new URL(a,document.baseURI);return c.protocol==="http:"||c.protocol==="https:"?`${c.protocol}//${c.host}${c.pathname}`:c.protocol==="data:"?"data:[redacted]":c.protocol==="blob:"?"blob:[redacted]":`${c.protocol}[redacted]`}catch{return"[redacted]"}}function X(a,c){let h=[],n=a;for(;n&&n!==c;){let p=n.parentElement??null;if(!p)break;let w=n.tagName.toLowerCase(),S=Array.from(p.children).filter(V=>String(V?.tagName??"").toLowerCase()===w),O=Math.max(0,S.indexOf(n));h.push(`${w}:${O}`),n=p}return h.push(c.tagName.toLowerCase()),h.reverse().join("/")}function K(a,c){let h=[],n=a;for(;n&&n!==c;){let p=n.parentElement??null;if(!p)break;let w=n.tagName.toLowerCase(),S=n.id;if(S){h.push(`#${CSS.escape(S)}`);break}let O=Array.from(p.children);if(O.filter(D=>String(D?.tagName??"").toLowerCase()===w).length===1)h.push(w);else{let D=O.indexOf(n)+1;h.push(`${w}:nth-child(${D})`)}n=p}return h.length?h.reverse().join(" > "):c.tagName.toLowerCase()}function Q(a){return!!(a.getAttribute("data-figma-id")||a.getAttribute("data-llynt-node-id")||a.getAttribute("data-component")||a.getAttribute("data-testid")||a.id)}function Z(a,c){let h=a.getAttribute("data-figma-id");if(h)return h;let n=a.getAttribute("data-llynt-node-id");if(n)return n;let p=a.getAttribute("data-component");if(p)return p;let w=a.getAttribute("data-testid");if(w)return w;let S=a.id;if(S)return S;let O=X(a,c),V=C(O);return`llynt-${a.tagName.toLowerCase()}-${V}`}function F(a){let c={};for(let h of Array.from(a.attributes)){let n=h.name;if(n==="id"||n==="class"||n==="role"||n==="alt"||n==="type"||n==="width"||n==="height"||n==="target"||n==="rel"||n==="loading"||n==="name"||n==="autocomplete"||n==="disabled"||n==="required"||n==="placeholder"||n==="for"||n==="tabindex"||n==="lang"||n==="style"||n.startsWith("aria-")||n==="data-figma-id"||n==="data-component"||n==="data-testid"||n==="data-primary"||n.startsWith("data-llynt-")){let p=h.value;typeof p=="string"&&(c[n]=p)}if(n==="href"||n==="src"){let p=h.value;typeof p=="string"&&(p===""||p==="#"||p.startsWith("javascript:")?c[n]=p:p.length>0&&(c[n]=_&&!H?p:le(p)))}}return Object.keys(c).length>0?c:void 0}function ee(a){if(N==="none")return;let c=window.getComputedStyle(a),h={},n=Array.isArray(g)&&g.length?g:N==="essential"?b:null;if(n){for(let p of n){let w=c.getPropertyValue(p);typeof w=="string"&&w.trim()&&(h[p]=w.trim())}return Object.keys(h).length>0?h:void 0}for(let p=0;p<c.length;p++){let w=c[p],S=c.getPropertyValue(w);typeof S=="string"&&S.trim()&&(h[w]=S.trim())}return Object.keys(h).length>0?h:void 0}function k(a){let c=a.getBoundingClientRect();return{left:c.left,top:c.top,width:c.width,height:c.height}}let R=document.querySelector(P||"body")??document.body,G=0,me=!1,he=new Set,te={};function fe(a,c,h){if(G+=1,G>j)return me=!0,null;let n=Q(a),p=Z(a,R);if(he.has(p)){let U=C(p+":"+G);p=`${p}-${U}`}if(he.add(p),!n)try{a.setAttribute("data-llynt-node-id",p)}catch{}let w=ee(a),S;if(v&&w&&Object.keys(w).length){let U=M(w);S=`cs-${C(U)}`,te[S]||(te[S]=w)}let O=K(a,R),V=(a.textContent?.trim()??"").length,D,$=a;($.scrollWidth>$.clientWidth||$.scrollHeight>$.clientHeight)&&(D={scrollWidth:$.scrollWidth,clientWidth:$.clientWidth,scrollHeight:$.scrollHeight,clientHeight:$.clientHeight});let E={nodeId:p,parentId:h,tagName:a.tagName.toLowerCase(),attributes:F(a),bbox:k(a),computed:S?void 0:w,computedRef:S,cssSelector:O,textLen:V>0?V:void 0,scrollOverflow:D,children:[]};if(a.tagName==="IMG"){let U=a;E.naturalWidth=U.naturalWidth,E.naturalHeight=U.naturalHeight}if(c>=L)return E;for(let U of Array.from(a.children)){let ce=fe(U,c+1,p);if(!ce)break;E.children.push(ce)}return E.children.length===0&&delete E.children,E.parentId||delete E.parentId,E.attributes||delete E.attributes,E.computed||delete E.computed,E.computedRef||delete E.computedRef,E}let ge=fe(R,0);if(!ge)return{tree:{nodeId:Z(R,R),tagName:R.tagName.toLowerCase(),bbox:k(R)},stats:{nodeCount:Math.min(G,j),maxDepth:L,maxNodes:j,truncated:!0}};let ze=document.documentElement.getAttribute("lang")||void 0;return{tree:ge,computedStyles:Object.keys(te).length?te:void 0,stats:{nodeCount:Math.min(G,j),maxDepth:L,maxNodes:j,truncated:me},htmlLang:ze}},{selector:r.selector??"body",maxDepth:s,maxNodes:o,computedStyleMode:d,computedStyleProps:m,essentialProps:[...bt],internComputedStyles:i,includeUrlAttributes:l,redactUrlAttributes:f}),u={_schemaVersion:"dom-blob@0",_extractedAt:new Date().toISOString(),target:{url:r.url,selector:r.selector,viewport:r.viewport},tree:y.tree,computedStyles:y.computedStyles,stats:y.stats,htmlLang:y.htmlLang};if(u.stats){u.stats.coverage={completeness:u.stats.truncated?"truncated":"complete",reason:u.stats.truncated?"maxNodes":void 0},u.stats.confidence={extractionCompleteness:u.stats.truncated?"low":"high"};let P={...u,stats:{...u.stats}};delete P.stats.bytesRaw,delete P.stats.bytesGzip,delete P.stats.computedStyleMode,delete P.stats.urlAttributes;let L=Buffer.from(JSON.stringify(P),"utf8"),j=Pe.gzipSync(L);u.stats.bytesRaw=L.length,u.stats.bytesGzip=j.length,u.stats.computedStyleMode=d,u.stats.urlAttributes=l?f?"redacted":"raw":"omitted"}return u}var Pe,gt,yt,bt,Ie=z(()=>{"use strict";Pe=A(require("zlib")),gt=10,yt=1e4,bt=["display","position","overflow","overflow-x","overflow-y","visibility","opacity","width","height","min-width","min-height","max-width","max-height","margin-top","margin-right","margin-bottom","margin-left","padding-top","padding-right","padding-bottom","padding-left","gap","flex-basis","flex-shrink","flex-wrap","align-items","z-index","pointer-events","cursor","font-size","font-family","font-weight","line-height","letter-spacing","text-decoration","text-overflow","white-space","color","background-color","border-width","border-color","border-top-color","border-top-width","border-radius","box-shadow","outline","outline-style","transition-duration","transition-property","aspect-ratio","padding"]});function wt(){let e=ie.platform(),r=ie.homedir();if(e==="darwin")return q.join(r,"Library","Caches","llynt","playwright");if(e==="win32"){let o=process.env.LOCALAPPDATA||process.env.APPDATA||q.join(r,"AppData","Local");return q.join(o,"llynt","playwright")}let t=process.env.XDG_CACHE_HOME,s=t&&t.trim().length>0?t:q.join(r,".cache");return q.join(s,"llynt","playwright")}function Le(){return{source:"llynt",browsersPath:wt()}}function Ne(e){process.env.PLAYWRIGHT_BROWSERS_PATH=e.browsersPath}var ie,q,_e=z(()=>{"use strict";ie=A(require("os")),q=A(require("path"))});async function kt(){return ae||(ae=vt().then(e=>e.chromium).catch(e=>{throw ae=null,e})),ae}async function vt(){try{let{createRequire:e}=await import("module");return e(process.cwd()+"/package.json")("playwright")}catch{return await import("playwright")}}function xt(){return["* {"," animation: none !important;"," transition: none !important;"," scroll-behavior: auto !important;","}","html {"," -webkit-text-size-adjust: 100% !important;","}"].join(`
|
|
3
|
-
`)}async function
|
|
4
|
-
`)}async function Be(e,r,t){let s=At(r||"body",t?.maxNodes??1e4);await e.addInitScript(s),await e.evaluate(s)}var Te=z(()=>{"use strict"});var Ve={};Ge(Ve,{runHostedThinClientChecks:()=>Ct});async function Ct(e){let r=e.repoRoot??process.cwd(),t=e.artifactDir??"tools/artifacts/llynt",s=e.timeoutMs??3e4,o=e.computedStyleMode??"essential",d=e.internComputedStyles??!0,m=e.maxNodes,i=!!e.allowTruncated,l=!!(e.traceOnFailure??process.env.LLYNT_TRACE_ON_FAILURE==="1"),f=null;try{f=await Oe();let y=[];for(let u of e.targets){let P=u.selector??"body",L=u.viewports?.length?u.viewports:[{width:360,height:720},{width:768,height:720}],j=await $e(f),N=j.context(),g=!1,b;try{if(l)try{await N.tracing.start({screenshots:!0,snapshots:!0,sources:!1}),g=!0}catch{g=!1}j.setDefaultTimeout(s),await j.goto(u.url,{waitUntil:"domcontentloaded"});let v=[];for(let C of L){await j.setViewportSize(C),await j.waitForTimeout(50),await Be(j,P);let M=await Re(j,{url:u.url,selector:P,viewport:C},{computedStyleMode:o,internComputedStyles:d,...typeof m=="number"?{maxNodes:m}:{}});if(!i&&M.stats?.truncated)throw new Error(`Extraction truncated for ${u.url} at ${C.width}x${C.height}. Narrow the selector scope or increase limits (free-tier should be component-scoped).`);v.push(M)}let _=await Ce(e.client,{_schemaVersion:"llynt-hosted-check@0",blobs:v,rules:{overlap:!0,viewport:!0}}),H=_.issues.some(C=>C.severity==="error");if(g&&H){let C=u.url.toLowerCase().replace(/https?:\/\//g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)||"target";b=J.resolve(r,J.join(t,`trace-hosted-${C}.zip`))}y.push({url:u.url,selector:P,viewports:L,blobs:v,response:_,tracePath:b})}finally{if(g)try{b?(Me.mkdirSync(J.dirname(b),{recursive:!0}),await N.tracing.stop({path:b})):await N.tracing.stop()}catch{}await N.close().catch(()=>{})}}return y}finally{await f?.close().catch(()=>{})}}var Me,J,De=z(()=>{"use strict";Me=A(require("fs")),J=A(require("path"));Ie();Ue();de();Te()});var ye={name:"llynt",version:"0.1.2",private:!0,description:"Llynt \u2013 UI integrity engine (token/scale drift, SARIF, evidence packs)",license:"UNLICENSED",type:"commonjs",main:"dist/src/index.js",bin:{llynt:"dist/src/cli/index.js"},files:[],scripts:{build:`tsc -p tsconfig.json && node -e "const f='dist/src/cli/index.js';const c=require('fs').readFileSync(f,'utf8');if(!c.startsWith('#!'))require('fs').writeFileSync(f,'#!/usr/bin/env node\\n'+c)"`,typecheck:"tsc -p tsconfig.json --noEmit","build:cli":"npx esbuild src/cli/cli-thin.ts --bundle --platform=node --target=node18 --format=cjs --minify --tree-shaking=true --external:playwright --outfile=dist/npm/cli.js --banner:js='#!/usr/bin/env node' --legal-comments=none","build:lambda":"npx esbuild src/platform/hosted/lambda-handler.ts --bundle --platform=node --target=node22 --outfile=dist/lambda/handler.js --format=cjs --external:@aws-sdk/client-dynamodb --external:@aws-sdk/client-s3 --external:@aws-sdk/lib-dynamodb",cli:"node dist/src/cli/index.js","bundle:action":"npm run build && ncc build dist/src/cli/action.js -o dist-action --license licenses.txt","test:smoke:core":"node scripts/run-core-smoke-strict.js","routine:run":"npm run build && node dist/src/cli/index.js workflow routine-run --routine onboarding --dry-run --execution-class scaffolded_external","demo:no-figma":"npm run build && tsc -p tsconfig.tests.json && node dist/src/cli/index.js validate --repo-root . --artifact-dir tools/artifacts/evidence-lab/solidcopy --css-vars tools/evidence-lab/fixtures/instances/tokens.css --dtcg tools/evidence-lab/fixtures/instances/tokens.dtcg.json --tailwind-json tools/evidence-lab/fixtures/instances/tailwind.theme.json --include-json tools/evidence-lab/fixtures/instances/include.json && node dist-tests/tools/smoke/tests/runtime-smoke.js && node dist/tools/internal/run.js standardize --snapshot-dir tools/artifacts/evidence-lab/solidcopy","demo:evidence":"npm run demo:no-figma","demo:bundle":"node scripts/bundle-artifacts.js","demo:scoreboard":"node scripts/demo-scoreboard.js","demo:storybook":"node scripts/public-storybook-harness.js --dry-run","demo:verify":"node scripts/compare-ground-truth.js","demo:gallery":"node scripts/gallery-build.js --link","demos:build":"node scripts/demos-build.js","onboarding:setup-repo":"node tools/internal/scripts/setup-onboarding-repo.js","demo318:setup-repo":"npm run onboarding:setup-repo","demo:harvest":"node scripts/harvest-storybook.js","battle:bootstrap:external":"node tools/evidence-lab/scripts/bootstrap-external-corpus-truth.js","hosted:preflight":"node tools/internal/scripts/hosted-preflight.js","browser:preflight":"node dist/tools/internal/run.js browser preflight --json",doctor:"node dist/tools/internal/run.js doctor --json","hosted:smoke:remote":"node tools/internal/scripts/hosted-remote-smoke.js","check:capabilities":"node tools/internal/scripts/check-capability-registry.js","docs:ip-check":"node scripts/check-docs-publish-boundary.js","docs:reference":"npm run build && node scripts/generate-reference-docs.js","docs:site:prepare":"npm run docs:reference && node scripts/sync-reference-docs.js && npm run docs:ip-check","docs:dev":"npm run docs:site:prepare && vitepress dev docs/site","docs:build":"npm run docs:site:prepare && vitepress build docs/site","docs:preview":"vitepress preview docs/site","gate:real-external":"node tools/internal/scripts/verify-execution-class.js --manifest tools/artifacts/routines/latest-manifest.json --required real_external","test:routine-runtime-gates":"node dist-tests/tools/smoke/tests/routine-runtime-gates-smoke.js","gate:real-external-proof":"node tools/internal/scripts/require-real-external-proof.js","test:routine-real-external":"npm run build && node dist/src/cli/index.js workflow routine-run --routine onboarding --dry-run --execution-class real_external --out tools/artifacts/routines/latest-manifest.real_external.json && npm run gate:real-external-proof","test:onboarding-auth-mode":"node dist-tests/tools/smoke/tests/onboarding-auth-mode-smoke.js","test:hosted-runs-api-smoke":"node dist-tests/tools/smoke/tests/hosted-runs-api-smoke.js","ide:surface-pack":"node scripts/ide-surface-pack-build.js","ide:host-runtime-proof:capture":"node scripts/ide-host-runtime-proof-capture.js","ide:host-runtime-proof-lint":"node tools/internal/scripts/ide-host-runtime-proof-lint.js","github:agentic-workflow-pack":"node scripts/github-agentic-workflow-pack.js","github:agentic-runtime-proof:capture":"node scripts/github-agentic-runtime-proof-capture.js","github:agentic-runtime-proof-lint":"node tools/internal/scripts/github-agentic-runtime-proof-lint.js","test:ide-surface-pack-smoke":"node dist-tests/tools/smoke/tests/ide-surface-pack-smoke.js","test:ide-host-parity-audit-smoke":"node dist-tests/tools/smoke/tests/ide-host-parity-audit-smoke.js","test:ide-host-runtime-proof-capture-smoke":"node dist-tests/tools/smoke/tests/ide-host-runtime-proof-capture-smoke.js","test:ide-host-runtime-proof-lint-smoke":"node dist-tests/tools/smoke/tests/ide-host-runtime-proof-lint-smoke.js","test:github-agentic-workflow-pack-smoke":"node dist-tests/tools/smoke/tests/github-agentic-workflow-pack-smoke.js","test:github-agentic-runtime-proof-capture-smoke":"node dist-tests/tools/smoke/tests/github-agentic-runtime-proof-capture-smoke.js","test:onboarding-key-ops-contract":"node dist-tests/tools/smoke/tests/onboarding-key-ops-contract-smoke.js","test:routine-choreography":"node dist-tests/tools/smoke/tests/routine-choreography-smoke.js","test:onboarding-scorecard":"node tools/internal/scripts/onboarding-scorecard-build.js --enforce","test:real-external-scorecard":"node tools/internal/scripts/real-external-scorecard-build.js --enforce --min-passes 3","real-external:scorecard":"node tools/internal/scripts/real-external-scorecard-build.js","real-external:init-run":"node scripts/real-external-run-init.js","onboarding:scorecard":"node tools/internal/scripts/onboarding-scorecard-build.js","test:fixture-archetype-catalog":"node dist-tests/tools/smoke/tests/fixture-archetype-catalog-smoke.js","test:fixture-archetype-runtime":"node dist-tests/tools/smoke/tests/fixture-archetype-runtime-smoke.js","test:mcp-tier-guidance":"node dist-tests/tools/smoke/tests/mcp-tier-guidance-smoke.js","test:mcp-tier-guidance-retry":"node dist-tests/tools/smoke/tests/mcp-tier-guidance-retry-smoke.js","test:collections-runtime":"node dist-tests/tools/smoke/tests/collections-runtime-smoke.js","test:onboarding-preflight-runtime":"node dist-tests/tools/smoke/tests/onboarding-preflight-runtime-smoke.js","test:mcp-init-bootstrap-contract-smoke":"node dist-tests/tools/smoke/tests/mcp-init-bootstrap-contract-smoke.js","test:beta-scenario-coverage":"node tools/internal/scripts/beta-scenario-coverage-gate.js --enforce","beta:scenario-coverage":"node tools/internal/scripts/beta-scenario-coverage-gate.js","beta:closure-report":"node tools/internal/scripts/beta-recovery-closure-report.js","test:non-curated-pack-runner":"node dist-tests/tools/smoke/tests/non-curated-pack-runner-smoke.js","non-curated:plan":"node tools/evidence-lab/scripts/non-curated-pack-runner.js","routine:gate-decision":"node dist/src/cli/index.js workflow gate-decision","pr:execution-contract":"node dist/src/cli/index.js workflow pr-execution-contract","pr:impact-stateid-policy":"node dist/tools/internal/release/run.js pr-impact-stateid-policy","non-curated:metrics":"node tools/evidence-lab/scripts/non-curated-metrics-build.js","test:real-external-proof-lint":"node tools/internal/scripts/real-external-proof-lint.js --enforce","real-external:proof-lint":"node tools/internal/scripts/real-external-proof-lint.js","real-external:finalize-run":"node scripts/real-external-run-finalize.js","test:real-external-finalize":"node dist-tests/tools/smoke/tests/real-external-finalize-smoke.js","beta:next-actions":"node tools/internal/scripts/beta-recovery-next-actions.js","beta:status":"node tools/internal/scripts/beta-recovery-status.js","beta:refresh":"node tools/internal/scripts/beta-recovery-refresh.js","test:beta-refresh":"node dist-tests/tools/smoke/tests/beta-recovery-refresh-smoke.js","real-external:validate-run":"node scripts/real-external-run-validate.js","test:real-external-validate":"node dist-tests/tools/smoke/tests/real-external-run-validate-smoke.js","non-curated:init-run":"node tools/evidence-lab/scripts/non-curated-init-run.js","real-external:guide":"node scripts/real-external-run-guide.js","onboarding:attempt-import":"node scripts/onboarding-attempt-import.js","test:onboarding-attempt-import":"node dist-tests/tools/smoke/tests/onboarding-attempt-import-smoke.js","real-external:import-onboarding-attempt":"node scripts/real-external-import-onboarding-attempt.js","test:real-external-import-onboarding-attempt":"node dist-tests/tools/smoke/tests/real-external-import-onboarding-attempt-smoke.js","test:action-plan-rollup":"node dist-tests/tools/smoke/tests/action-plan-rollup-smoke.js","test:blocker-remediation-coverage-smoke":"node dist-tests/tools/smoke/tests/blocker-remediation-coverage-smoke.js","test:mcp-onboarding-contract-errors-smoke":"node dist-tests/tools/smoke/tests/mcp-onboarding-contract-errors-smoke.js","test:mcp-redaction-smoke":"node dist-tests/tools/smoke/tests/mcp-redaction-smoke.js","test:profile-surface-smoke":"node dist-tests/tools/smoke/tests/profile-surface-smoke.js","probabilistic:harness":"node scripts/run-probabilistic-agent-harness.js","mutation:audit":"node tools/evidence-lab/scripts/mutation-intent-audit.js","mutation:reconcile":"node tools/evidence-lab/scripts/mutation-truth-reconcile.js","correlation:root-cause":"node tools/evidence-lab/scripts/correlated-cluster-build.js","state-graph:build":"node tools/evidence-lab/scripts/state-graph-build.js","actionset:trace":"node tools/evidence-lab/scripts/actionset-trace-build.js","planner:seed-reduction":"node tools/evidence-lab/scripts/render-predicate-planner-build.js","discovery:flow-graph":"node tools/evidence-lab/scripts/discovery-flow-graph-build.js","l2:macro-coverage":"node dist/tools/internal/release/run.js l2-macro-coverage","l2:component-category-coverage":"node dist/tools/internal/release/run.js l2-component-category-coverage","l2:surface-bundle-map":"node dist/tools/internal/release/run.js l2-surface-bundle-map","network:scenario-runner":"node tools/evidence-lab/scripts/network-scenario-runner.js","seed:design-kit-harvest":"node tools/evidence-lab/scripts/design-kit-seed-harvester.js","chaos:mutation-sim":"node tools/evidence-lab/scripts/chaos-mutation-simulator.js","gauntlet:factory":"node tools/evidence-lab/scripts/gauntlet-factory.js","react:intake-normalize":"node tools/evidence-lab/scripts/real-react-intake-normalize.js","gallery:corpus-build":"node tools/evidence-lab/scripts/gallery-corpus-build.js","evidence-lab":"npx tsx tools/evidence-lab/pipeline.ts run","evidence-lab:status":"npx tsx tools/evidence-lab/pipeline.ts status","evidence-lab:gallery":"npx tsx tools/evidence-lab/pipeline.ts run --stage 4","evidence-lab:chaos":"npx tsx tools/evidence-lab/pipeline.ts run --stage 3","v0:operator-usability":"node tools/evidence-lab/scripts/v0-operator-usability-loop.js","audit:smoke-truth":"node scripts/smoke-truth-audit.js","audit:smoke-disposition":"node scripts/smoke-disposition-audit.js","replay:recipes":"node scripts/replay-recipe-build.js","replay:repro-gate":"node tools/internal/scripts/replay-repro-gate.js","core:quality-floor":"node dist/tools/internal/run.js core-quality-floor","invariant:coverage-gap":"node dist/tools/internal/run.js invariant-coverage-gap","test:core-invariant-quality-floor":"node dist/tools/internal/run.js core-quality-floor --enforce","test:invariant-coverage-gap":"node dist/tools/internal/run.js invariant-coverage-gap --enforce","test:l2-macro-profile-coverage-smoke":"node dist-tests/tools/smoke/tests/l2-macro-profile-coverage-smoke.js","test:l2-component-category-coverage-smoke":"node dist-tests/tools/smoke/tests/l2-component-category-coverage-smoke.js","test:l2-surface-bundle-map-smoke":"node dist-tests/tools/smoke/tests/l2-surface-bundle-map-smoke.js","test:network-scenario-runner-smoke":"node dist-tests/tools/smoke/tests/network-scenario-runner-smoke.js","test:chaos-mutation-simulator-smoke":"node dist-tests/tools/smoke/tests/chaos-mutation-simulator-smoke.js","test:gauntlet-factory-smoke":"node dist-tests/tools/smoke/tests/gauntlet-factory-smoke.js","test:real-react-intake-normalize-smoke":"node dist-tests/tools/smoke/tests/real-react-intake-normalize-smoke.js","test:gallery-corpus-build-smoke":"node dist-tests/tools/smoke/tests/gallery-corpus-build-smoke.js","test:replay-repro-gate-smoke":"node dist-tests/tools/smoke/tests/replay-repro-gate-smoke.js","test:pr-execution-contract-smoke":"node dist-tests/tools/smoke/tests/pr-execution-contract-smoke.js","test:pr-impact-stateid-policy-smoke":"node dist-tests/tools/smoke/tests/pr-impact-stateid-policy-smoke.js","onboarding:accuracy":"node tools/internal/scripts/onboarding-accuracy-eval.js","test:onboarding-accuracy":"node tools/internal/scripts/onboarding-accuracy-eval.js --enforce","onboarding:harness-matrix:run":"node tools/internal/scripts/onboarding-harness-matrix-run.js","onboarding:harness-matrix:eval":"node tools/internal/scripts/onboarding-harness-matrix-eval.js","test:onboarding-harness-matrix":"node dist-tests/tools/smoke/tests/onboarding-harness-matrix-smoke.js","evidence:freshness":"node dist/tools/internal/run.js evidence-freshness","test:evidence-freshness":"node dist/tools/internal/run.js evidence-freshness --enforce","canary:run":"node scripts/real-target-canary-run.js","canary:drift-gate":"node tools/internal/scripts/real-target-canary-drift-gate.js --enforce","canary:nightly":"npm run canary:run -- --enforce && npm run canary:drift-gate","benchmark:wall-time":"node tools/internal/scripts/wall-time-benchmark.js","test:concurrent-pr-isolation":"node tools/internal/scripts/concurrent-pr-isolation-test.js --enforce","test:real-target-canary-run-smoke":"node dist-tests/tools/smoke/tests/real-target-canary-run-smoke.js","gate:required-r1-claims":"node tools/internal/scripts/required-r1-claim-gate.js --enforce","test:unit":"vitest run","test:check-command-smoke":"npx tsc --target ES2022 --module commonjs --moduleResolution node --esModuleInterop --resolveJsonModule --skipLibCheck --types node --outDir dist-tests --rootDir . tools/smoke/tests/check-command-smoke.ts && node dist-tests/tools/smoke/tests/check-command-smoke.js","test:hosted-signup-smoke":"npx tsc --target ES2022 --module commonjs --moduleResolution node --esModuleInterop --resolveJsonModule --skipLibCheck --types node --outDir dist-tests --rootDir . tools/smoke/tests/hosted-signup-smoke.ts && node dist-tests/tools/smoke/tests/hosted-signup-smoke.js","site:llynt-dev:build":"npm --prefix src/brand/logo-animation run build",adversarial:"npm run build && npx tsx tools/evidence-lab/adversarial/run-all.ts"},dependencies:{"@actions/core":"^2.0.3","@modelcontextprotocol/sdk":"^1.26.0",commander:"^12.1.0",minimatch:"^9.0.5",playwright:"^1.45.0",zod:"^4.3.6"},devDependencies:{"@aws-sdk/client-dynamodb":"^3.1004.0","@aws-sdk/client-s3":"^3.1004.0","@aws-sdk/lib-dynamodb":"^3.1004.0","@types/node":"^20.11.30","@vercel/ncc":"^0.38.1",esbuild:"^0.27.3",typescript:"^5.4.5","vite-tsconfig-paths":"^4.3.2",vitepress:"^1.6.4",vitest:"^3.2.4"},overrides:{qs:"^6.14.2"}};function be(e){let r={};for(let t=0;t<e.length;t++){let s=e[t];if(!s.startsWith("--"))continue;let o=s.slice(2),d=e[t+1];!d||d.startsWith("--")?r[o]=!0:(r[o]=d,t++)}return r}function re(e){if(e==null)return!1;if(typeof e=="boolean")return e;let r=String(e).trim().toLowerCase();return!(r===""||r==="0"||r==="false"||r==="no"||r==="off")}function we(e){return e.replace(/\s+/g," ").trim()}function Ze(e){return e.replace(/\bllynt-[a-z]+-[a-z0-9]+\b/g,r=>{let t=r.split("-");return t.length>=2?`<${t[1]}>`:r})}function ke(e){let r=we(String(e.message??""));return r||`${we(String(e.ruleId??"unknown.rule"))} violation`}function et(e){return Ze(ke(e))}function tt(e){return e.length?e.map(r=>`${r.width}px`).join(", "):""}function ve(e){let r=e.issues.filter(l=>l.severity==="error"),t=e.issues.filter(l=>l.severity==="warning"),s=e.issues.filter(l=>l.severity==="info"),o=e.issues.length,d=e.viewports.length,m=tt(e.viewports),i=[];if(i.push(`llynt check ${e.url}`),i.push(""),e.rulesChecked!=null&&e.rulesAvailable!=null&&i.push(` ${e.rulesChecked} of ${e.rulesAvailable} rules checked${e.isAnonymous?" (anonymous)":""}`),i.push(` Capturing... ${d} viewport${d!==1?"s":""}${m?` (${m})`:""}`),i.push(` Analyzing... ${Math.max(0,Math.floor(e.nodeCount))} nodes, ${Math.max(0,Math.floor(e.costUnits))} cost units`),e.truncated&&i.push(" Note: Capture was truncated due to DOM limits."),i.push(""),o===0?i.push(" 0 issues found"):(i.push(` ${o} issues across ${d} viewport${d!==1?"s":""}`),i.push(` ${r.length} errors \xB7 ${t.length} warnings \xB7 ${s.length} info`)),i.push(""),e.reportUrl){let l=e.reportExpiresAt?` (expires ${e.reportExpiresAt.slice(0,10)})`:"";i.push(` Report: ${e.reportUrl}${l}`),i.push("")}if(o>0){let l=new Set,f=[];for(let y of[...r,...t,...s]){let u=et(y);!l.has(u)&&f.length<5&&(l.add(u),f.push(u))}if(f.length>0){i.push(" Top issues:");for(let u of f)i.push(` ${u}`);let y=o-f.length;y>0&&i.push(` ... and ${y} more (see report)`),i.push("")}e.gatedIssueCount&&e.gatedIssueCount>0&&(i.push(` ${e.gatedIssueCount} more issues found with full analysis \u2014 sign up at llynt.dev`),i.push(""))}return e.isAnonymous?(i.push(" Sign up free at https://llynt.dev for 28 rules, 2 viewports, 70 checks/week."),i.push(" Then: export LLYNT_API_KEY=<your-key>"),i.push("")):o>0&&(i.push(" Want full SARIF evidence + agent-ready fixes?"),i.push(" Set up the PR gate \u2014 your agent can do it:"),i.push(" npx llynt init --github")),i.join(`
|
|
5
|
-
`).trimEnd()}function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`)}
|
|
9
|
-
Usage: llynt check <url>`),
|
|
10
|
-
`)}try{let{createRequire:
|
|
2
|
+
"use strict";var Zt=Object.create;var Ct=Object.defineProperty;var Qt=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var ee=Object.getPrototypeOf,ne=Object.prototype.hasOwnProperty;var tt=(t,n)=>()=>(t&&(n=t(t=0)),n);var re=(t,n)=>{for(var e in n)Ct(t,e,{get:n[e],enumerable:!0})},oe=(t,n,e,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of te(n))!ne.call(t,r)&&r!==e&&Ct(t,r,{get:()=>n[r],enumerable:!(o=Qt(n,r))||o.enumerable});return t};var P=(t,n,e)=>(e=t!=null?Zt(ee(t)):{},oe(n||!t||!t.__esModule?Ct(e,"default",{value:t,enumerable:!0}):e,t));function kt(t){let n=t.trim();if(!n)throw new Error("LLYNT_API_URL is empty");return n.includes("/v1/check")?n:n.replace(/\/+$/g,"")+"/v1/check"}function fe(t){return new Promise((n,e)=>{ft.gzip(t,(o,r)=>{o?e(o):n(r)})})}function ye(t){return new Promise((n,e)=>{ft.brotliCompress(t,(o,r)=>{o?e(o):n(r)})})}function we(t,n,e,o){return new Promise((r,u)=>{let s=(t.protocol==="https:"?ge:he).request({...n,protocol:t.protocol,hostname:t.hostname,port:t.port,path:t.pathname+t.search},i=>{let g=[];i.on("data",m=>g.push(m)),i.on("end",()=>{r({statusCode:i.statusCode??0,body:Buffer.concat(g)})})});s.on("error",i=>u(i)),s.setTimeout(o,()=>{s.destroy(new Error(`Hosted API request timed out after ${o}ms`))}),s.end(e)})}async function Et(t,n){let e=new URL(t.apiUrl),o=t.timeoutMs??3e4,r=Buffer.from(JSON.stringify(n),"utf8"),u=t.compression??"gzip",l=u==="identity"?r:u==="br"?await ye(r):await fe(r),s={"content-type":"application/json",accept:"application/json","user-agent":"llynt-hosted-client/0","content-length":String(l.length)};u!=="identity"&&(s["content-encoding"]=u),t.apiKey&&(s.authorization=`Bearer ${t.apiKey}`);let{statusCode:i,body:g}=await we(e,{method:"POST",headers:s},l,o),m=g.toString("utf8");if(i<200||i>=300)throw new Error(`Hosted API error (${i}): ${m.slice(0,500)}`);let w=JSON.parse(m);if(!w||w._schemaVersion!=="llynt-hosted-check-result@0"||!Array.isArray(w.issues))throw new Error("Hosted API response schema mismatch");return w}var he,ge,ft,At=tt(()=>{"use strict";he=P(require("http")),ge=P(require("https")),ft=P(require("zlib"))});async function $t(t,n,e){let o=e?.maxDepth??Ce,r=e?.maxNodes??Se,u=typeof e?.includeComputedStyles=="boolean"?e.includeComputedStyles?"all":"none":e?.computedStyleMode??"essential",l=Array.isArray(e?.computedStyleProps)?e.computedStyleProps:void 0,s=e?.internComputedStyles??!0,i=e?.includeUrlAttributes??!1,g=e?.redactUrlAttributes??!0,m=e?.pierceShadowRoots??!1,w=await t.evaluate(({selector:p,maxDepth:A,maxNodes:h,computedStyleMode:L,computedStyleProps:b,essentialProps:x,internComputedStyles:T,includeUrlAttributes:M,redactUrlAttributes:D,pierceShadowRoots:U})=>{function B(d){let f=2166136261;for(let S=0;S<d.length;S++)f^=d.charCodeAt(S),f=Math.imul(f,16777619);return(f>>>0).toString(36)}function ut(d){let f=Object.keys(d).sort(),S=[];for(let c of f)S.push(`${c}=${d[c]}`);return S.join(";")}function G(d){try{let f=new URL(d,document.baseURI);return f.protocol==="http:"||f.protocol==="https:"?`${f.protocol}//${f.host}${f.pathname}`:f.protocol==="data:"?"data:[redacted]":f.protocol==="blob:"?"blob:[redacted]":`${f.protocol}[redacted]`}catch{return"[redacted]"}}function W(d,f){let S=[],c=d;for(;c&&c!==f;){let y=c.parentElement??null;if(!y)break;let I=c.tagName.toLowerCase(),E=Array.from(y.children).filter(Y=>String(Y?.tagName??"").toLowerCase()===I),V=Math.max(0,E.indexOf(c));S.push(`${I}:${V}`),c=y}return S.push(f.tagName.toLowerCase()),S.reverse().join("/")}function $(d,f){let S=[],c=d;for(;c&&c!==f;){let y=c.parentElement??null;if(!y)break;let I=c.tagName.toLowerCase(),E=c.id;if(E){S.push(`#${CSS.escape(E)}`);break}let V=Array.from(y.children);if(V.filter(q=>String(q?.tagName??"").toLowerCase()===I).length===1)S.push(I);else{let q=V.indexOf(c)+1;S.push(`${I}:nth-child(${q})`)}c=y}return S.length?S.reverse().join(" > "):f.tagName.toLowerCase()}function F(d){return!!(d.getAttribute("data-figma-id")||d.getAttribute("data-llynt-node-id")||d.getAttribute("data-component")||d.getAttribute("data-testid")||d.id)}function X(d,f){let S=d.getAttribute("data-figma-id");if(S)return S;let c=d.getAttribute("data-llynt-node-id");if(c)return c;let y=d.getAttribute("data-component");if(y)return y;let I=d.getAttribute("data-testid");if(I)return I;let E=d.id;if(E)return E;let V=W(d,f),Y=B(V);return`llynt-${d.tagName.toLowerCase()}-${Y}`}function ct(d){let f={};for(let S of Array.from(d.attributes)){let c=S.name;if(c==="id"||c==="class"||c==="role"||c==="alt"||c==="type"||c==="width"||c==="height"||c==="target"||c==="rel"||c==="loading"||c==="name"||c==="autocomplete"||c==="disabled"||c==="required"||c==="placeholder"||c==="for"||c==="tabindex"||c==="lang"||c==="style"||c.startsWith("aria-")||c==="data-figma-id"||c==="data-component"||c==="data-testid"||c==="data-primary"||c.startsWith("data-llynt-")){let y=S.value;typeof y=="string"&&(f[c]=y)}if(c==="href"||c==="src"){let y=S.value;typeof y=="string"&&(y===""||y==="#"||y.startsWith("javascript:")?f[c]=y:y.length>0&&(f[c]=M&&!D?y:G(y)))}}return Object.keys(f).length>0?f:void 0}function lt(d){if(L==="none")return;let f=window.getComputedStyle(d),S={},c=Array.isArray(b)&&b.length?b:L==="essential"?x:null;if(c){for(let y of c){let I=f.getPropertyValue(y);typeof I=="string"&&I.trim()&&(S[y]=I.trim())}return Object.keys(S).length>0?S:void 0}for(let y=0;y<f.length;y++){let I=f[y],E=f.getPropertyValue(I);typeof E=="string"&&E.trim()&&(S[I]=E.trim())}return Object.keys(S).length>0?S:void 0}function pt(d){let f=d.getBoundingClientRect();return{left:f.left,top:f.top,width:f.width,height:f.height}}let H=document.querySelector(p||"body")??document.body,j=0,dt=!1,mt=new Set,z={};function Z(d,f,S){if(j+=1,j>h)return dt=!0,null;let c=F(d),y=X(d,H);if(mt.has(y)){let N=B(y+":"+j);y=`${y}-${N}`}if(mt.add(y),!c)try{d.setAttribute("data-llynt-node-id",y)}catch{}let I=lt(d),E;if(T&&I&&Object.keys(I).length){let N=ut(I);E=`cs-${B(N)}`,z[E]||(z[E]=I)}let V=$(d,H),Y=(d.textContent?.trim()??"").length,q,K=d;(K.scrollWidth>K.clientWidth||K.scrollHeight>K.clientHeight)&&(q={scrollWidth:K.scrollWidth,clientWidth:K.clientWidth,scrollHeight:K.scrollHeight,clientHeight:K.clientHeight});let R={nodeId:y,parentId:S,tagName:d.tagName.toLowerCase(),attributes:ct(d),bbox:pt(d),computed:E?void 0:I,computedRef:E,cssSelector:V,textLen:Y>0?Y:void 0,scrollOverflow:q,children:[]};if(d.tagName==="IMG"){let N=d;R.naturalWidth=N.naturalWidth,R.naturalHeight=N.naturalHeight}if(f>=A)return R;for(let N of Array.from(d.children)){let Q=Z(N,f+1,y);if(!Q)break;R.children.push(Q)}if(U&&d.shadowRoot)for(let N of Array.from(d.shadowRoot.children)){let Q=Z(N,f+1,y);if(!Q)break;R.children.push(Q)}return R.children.length===0&&delete R.children,R.parentId||delete R.parentId,R.attributes||delete R.attributes,R.computed||delete R.computed,R.computedRef||delete R.computedRef,R}let C=Z(H,0);if(!C)return{tree:{nodeId:X(H,H),tagName:H.tagName.toLowerCase(),bbox:pt(H)},stats:{nodeCount:Math.min(j,h),maxDepth:A,maxNodes:h,truncated:!0}};let _=document.documentElement.getAttribute("lang")||void 0;return{tree:C,computedStyles:Object.keys(z).length?z:void 0,stats:{nodeCount:Math.min(j,h),maxDepth:A,maxNodes:h,truncated:dt},htmlLang:_}},{selector:n.selector??"body",maxDepth:o,maxNodes:r,computedStyleMode:u,computedStyleProps:l,essentialProps:[...Ae],internComputedStyles:s,includeUrlAttributes:i,redactUrlAttributes:g,pierceShadowRoots:m}),a={_schemaVersion:"dom-blob@0",_extractedAt:new Date().toISOString(),target:{url:n.url,selector:n.selector,viewport:n.viewport},tree:w.tree,computedStyles:w.computedStyles,stats:w.stats,htmlLang:w.htmlLang};if(a.stats){a.stats.coverage={completeness:a.stats.truncated?"truncated":"complete",reason:a.stats.truncated?"maxNodes":void 0},a.stats.confidence={extractionCompleteness:a.stats.truncated?"low":"high"};let p={...a,stats:{...a.stats}};delete p.stats.bytesRaw,delete p.stats.bytesGzip,delete p.stats.computedStyleMode,delete p.stats.urlAttributes;let A=Buffer.from(JSON.stringify(p),"utf8"),h=_t.gzipSync(A);a.stats.bytesRaw=A.length,a.stats.bytesGzip=h.length,a.stats.computedStyleMode=u,a.stats.urlAttributes=i?g?"redacted":"raw":"omitted"}return a}var _t,Ce,Se,Ae,Tt=tt(()=>{"use strict";_t=P(require("zlib")),Ce=10,Se=1e4,Ae=["display","position","overflow","overflow-x","overflow-y","visibility","opacity","width","height","min-width","min-height","max-width","max-height","margin-top","margin-right","margin-bottom","margin-left","padding-top","padding-right","padding-bottom","padding-left","gap","flex-basis","flex-shrink","flex-wrap","align-items","z-index","pointer-events","cursor","font-size","font-family","font-weight","line-height","letter-spacing","text-decoration","text-overflow","white-space","color","background-color","border-width","border-color","border-top-color","border-top-width","border-radius","box-shadow","outline","outline-style","transition-duration","transition-property","animation-name","animation-play-state","aspect-ratio","padding"]});function ve(){let t=yt.platform(),n=yt.homedir();if(t==="darwin")return rt.join(n,"Library","Caches","llynt","playwright");if(t==="win32"){let r=process.env.LOCALAPPDATA||process.env.APPDATA||rt.join(n,"AppData","Local");return rt.join(r,"llynt","playwright")}let e=process.env.XDG_CACHE_HOME,o=e&&e.trim().length>0?e:rt.join(n,".cache");return rt.join(o,"llynt","playwright")}function Mt(){return{source:"llynt",browsersPath:ve()}}function Nt(t){process.env.PLAYWRIGHT_BROWSERS_PATH=t.browsersPath}var yt,rt,Ot=tt(()=>{"use strict";yt=P(require("os")),rt=P(require("path"))});async function Ie(){return wt||(wt=xe().then(t=>t.chromium).catch(t=>{throw wt=null,t})),wt}async function xe(){try{let{createRequire:t}=await import("module");return t(process.cwd()+"/package.json")("playwright")}catch{return await import("playwright")}}function Pe(){return["* {"," animation: none !important;"," transition: none !important;"," scroll-behavior: auto !important;","}","html {"," -webkit-text-size-adjust: 100% !important;","}"].join(`
|
|
3
|
+
`)}async function Bt(){if(ot&&ot.isConnected())return ot;if(st)try{let t=await st;if(t.isConnected())return t}catch{}return st=ke().then(t=>(ot=t,t.on("disconnected",()=>{ot===t&&(ot=null,st=null)}),t)).catch(t=>{throw st=null,t}),st}async function ke(){let t=Mt();Nt(t);let n=await Ie(),e=["--no-sandbox","--disable-gpu","--disable-dev-shm-usage","--allow-file-access-from-files","--allow-file-access","--disable-lcd-text","--font-render-hinting=none"];try{return await n.launch({headless:!0,args:e})}catch{return await new Promise(o=>setTimeout(o,250)),await n.launch({headless:!0,args:e})}}async function Ee(t){try{await t.addStyleTag({content:Pe()})}catch(n){console.warn("Failed to apply deterministic CSS:",n)}}async function Ht(t,n,e){let r=await(await t.newContext({viewport:n||{width:1280,height:720},...e||{}})).newPage();return await Ee(r),r}var ot,st,wt,Dt=tt(()=>{"use strict";Ot();ot=null,st=null,wt=null});function Le(t,n=1e4){let e=[];return e.push("(function(){"),e.push(`const selector = ${JSON.stringify(t)};`),e.push(`const maxNodes = ${JSON.stringify(n)};`),e.push("function fnv1aBase36(input){"),e.push(" let hash = 0x811c9dc5;"),e.push(" for (let i=0;i<input.length;i++){"),e.push(" hash ^= input.charCodeAt(i);"),e.push(" hash = Math.imul(hash, 0x01000193);"),e.push(" }"),e.push(" return (hash >>> 0).toString(36);"),e.push("}"),e.push("function domPathSignature(el, root){"),e.push(" const parts=[];"),e.push(" let cur=el;"),e.push(" while(cur && cur!==root){"),e.push(" const parent=cur.parentElement;"),e.push(" if(!parent) break;"),e.push(" const tag=cur.tagName.toLowerCase();"),e.push(" const siblings=Array.from(parent.children).filter(c=>c.tagName.toLowerCase()===tag);"),e.push(" const idx=Math.max(0,siblings.indexOf(cur));"),e.push(" parts.push(`${tag}:${idx}`);"),e.push(" cur=parent;"),e.push(" }"),e.push(" parts.push(root.tagName.toLowerCase());"),e.push(' return parts.reverse().join("/");'),e.push("}"),e.push("function hasStableIdentity(el){"),e.push(" return Boolean("),e.push(' el.getAttribute("data-figma-id") ||'),e.push(' el.getAttribute("data-llynt-node-id") ||'),e.push(' el.getAttribute("data-component") ||'),e.push(' el.getAttribute("data-testid") ||'),e.push(" el.id"),e.push(" );"),e.push("}"),e.push("function assignIds(){"),e.push(" const root = document.querySelector(selector) || document.body;"),e.push(" if(!root) return 0;"),e.push(" const used=new Set();"),e.push(" let assigned=0;"),e.push(" const walker=document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);"),e.push(" let node=walker.currentNode;"),e.push(" let seen=0;"),e.push(" while(node){"),e.push(" const el=node;"),e.push(" if(el && el instanceof Element){"),e.push(" seen++;"),e.push(" if(seen>maxNodes) break;"),e.push(" if(!hasStableIdentity(el)){"),e.push(" const sig=domPathSignature(el, root);"),e.push(" const hash=fnv1aBase36(sig);"),e.push(" const tag=el.tagName.toLowerCase();"),e.push(" let id=`llynt-${tag}-${hash}`;"),e.push(' if(used.has(id)){ id = `${id}-${fnv1aBase36(id+":"+seen)}`; }'),e.push(" used.add(id);"),e.push(' el.setAttribute("data-llynt-node-id", id);'),e.push(" assigned++;"),e.push(" } else {"),e.push(' const existing = el.getAttribute("data-llynt-node-id") || el.getAttribute("data-figma-id") || el.getAttribute("data-component") || el.getAttribute("data-testid") || el.id;'),e.push(" if(existing) used.add(existing);"),e.push(" }"),e.push(" }"),e.push(" node = walker.nextNode();"),e.push(" }"),e.push(" return assigned;"),e.push("}"),e.push("try { assignIds(); } catch {}"),e.push("})();"),e.join(`
|
|
4
|
+
`)}async function Ut(t,n,e){let o=Le(n||"body",e?.maxNodes??1e4);await t.addInitScript(o),await t.evaluate(o)}var Ft=tt(()=>{"use strict"});var Kt={};re(Kt,{runHostedThinClientChecks:()=>Re});async function Re(t){let n=t.repoRoot??process.cwd(),e=t.artifactDir??"tools/artifacts/llynt",o=t.timeoutMs??3e4,r=t.computedStyleMode??"essential",u=t.internComputedStyles??!0,l=t.maxNodes,s=!!t.allowTruncated,i=!!(t.traceOnFailure??process.env.LLYNT_TRACE_ON_FAILURE==="1"),g=null;try{g=await Bt();let m=[];for(let w of t.targets){let a=Date.now(),p=w.selector??"body",A=w.viewports?.length?w.viewports:[{width:360,height:720},{width:768,height:720}],h=await Ht(g),L=h.context(),b=!1,x;try{if(i)try{await L.tracing.start({screenshots:!0,snapshots:!0,sources:!1}),b=!0}catch{b=!1}h.setDefaultTimeout(o);let T=Date.now();await h.goto(w.url,{waitUntil:"domcontentloaded"});let M=[];for(let $ of A){await h.setViewportSize($),await h.waitForTimeout(50),await Ut(h,p);let F=await $t(h,{url:w.url,selector:p,viewport:$},{computedStyleMode:r,internComputedStyles:u,...typeof l=="number"?{maxNodes:l}:{}});if(!s&&F.stats?.truncated)throw new Error(`Extraction truncated for ${w.url} at ${$.width}x${$.height}. Narrow the selector scope or increase limits (free-tier should be component-scoped).`);M.push(F)}let D=Date.now()-T,U=Date.now(),B=await Et(t.client,{_schemaVersion:"llynt-hosted-check@0",blobs:M,rules:{overlap:!0,viewport:!0}}),ut=Date.now()-U,G=Date.now()-a,W=B.issues.some($=>$.severity==="error");if(b&&W){let $=w.url.toLowerCase().replace(/https?:\/\//g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)||"target";x=it.resolve(n,it.join(e,`trace-hosted-${$}.zip`))}m.push({url:w.url,selector:p,viewports:A,blobs:M,response:B,timings:{captureMs:D,analyzeMs:ut,totalMs:G},tracePath:x})}finally{if(b)try{x?(Vt.mkdirSync(it.dirname(x),{recursive:!0}),await L.tracing.stop({path:x})):await L.tracing.stop()}catch{}await L.close().catch(()=>{})}}return m}finally{await g?.close().catch(()=>{})}}var Vt,it,Wt=tt(()=>{"use strict";Vt=P(require("fs")),it=P(require("path"));Tt();Dt();At();Ft()});function et(t){let n={};for(let e=0;e<t.length;e++){let o=t[e];if(!o.startsWith("--"))continue;let r=o.slice(2),u=t[e+1];!u||u.startsWith("--")?n[r]=!0:(n[r]=u,e++)}return n}function ht(t){if(t==null)return!1;if(typeof t=="boolean")return t;let n=String(t).trim().toLowerCase();return!(n===""||n==="0"||n==="false"||n==="no"||n==="off")}var se=new Set(["AccordionDisclosure","Tabs","Search","Form","ModalDialog"]);function gt(t){return t.replace(/\s+/g," ").trim()}function ie(t){return t.replace(/\bllynt-[a-z]+-[a-z0-9]+\b/g,n=>{let e=n.split("-");return e.length>=2?`<${e[1]}>`:n})}function ae(t){let n=gt(String(t.message??""));return n||`${gt(String(t.ruleId??"unknown.rule"))} violation`}function ue(t){return t.length?t.map(n=>`${n.width}px`).join(", "):""}function St(t){return!Number.isFinite(t)||t<=0?"0ms":t<1e3?`${Math.round(t)}ms`:`${(t/1e3).toFixed(2)}s`}function ce(t){return t.replace(/Low contrast \((\d+(?:\.\d+)?)\):/g,"Low contrast ratio $1:1:")}function le(t){return ce(ie(gt(String(t??""))))}function It(t){let n=t.issues.filter(i=>i.severity==="error"),e=t.issues.filter(i=>i.severity==="warning"),o=t.issues.filter(i=>i.severity==="info"),r=t.issues.length,u=t.viewports.length,l=ue(t.viewports),s=[];if(s.push(`llynt check ${t.url}`),s.push(""),t.rulesChecked!=null&&t.rulesAvailable!=null&&s.push(` ${t.rulesChecked} of ${t.rulesAvailable} rules checked${t.isAnonymous?" (anonymous)":""}`),s.push(` Capturing... ${u} viewport${u!==1?"s":""}${l?` (${l})`:""}`),s.push(` Analyzing... ${Math.max(0,Math.floor(t.nodeCount))} nodes, ${Math.max(0,Math.floor(t.costUnits))} cost units`),t.timings&&s.push(` Timing... capture ${St(t.timings.captureMs)} \xB7 hosted ${St(t.timings.analyzeMs)} \xB7 total ${St(t.timings.totalMs)}`),t.truncated&&s.push(" Note: Capture was truncated due to DOM limits."),s.push(""),r===0)s.push(" 0 issues found");else{let i=t.incidentCount!=null?t.incidentCount:r,m=` ${i} ${i===1?"incident":"incidents"} (${r} evidence points) across ${u} viewport${u!==1?"s":""}`;s.push(m),s.push(` ${n.length} errors \xB7 ${e.length} warnings \xB7 ${o.length} info`)}if(s.push(""),t.compositional&&t.compositional.componentCount>0){let i=t.compositional,g=i.componentSummaries.filter(w=>w.confidence==="high"&&w.missingParts.length>0&&se.has(w.type)).length;(i.routeStatus==="FAIL"||i.componentsWithIssues>0||g>0||i.pageLayoutSummary.severityCounts.error>0)&&(s.push(` ${i.componentCount} components analyzed, ${i.componentsWithIssues} with issues`),g>0&&s.push(` ${g} high-confidence structural gaps`),s.push(` Page Geometry: ${i.pageLayoutSummary.issueCount} findings`),s.push(""))}if(t.reportUrl&&(s.push(" Open full report:"),s.push(` ${t.reportUrl}`),t.reportExpiresAt&&s.push(` Expires ${t.reportExpiresAt.slice(0,10)}`),s.push("")),r>0&&t.showDetails){if(t.incidents?.length){let i=t.incidents.filter(a=>a.severity!=="info").slice(0,5);if(s.push(" Major findings:"),i.length===0)s.push(" No error/warning findings in this run.");else for(let a of i){let p=le(a.message);s.push(` ${a.count}x [${a.ruleId}] ${p}`)}s.push("");let g=new Map,m={error:0,warning:1,info:2};for(let a of t.incidents){if(a.severity==="info")continue;let p=(a.category||"Other").trim()||"Other",A=g.get(p);A?(A.count+=a.count,A.ruleIds.add(a.ruleId),m[a.severity]<m[A.severity]&&(A.severity=a.severity)):g.set(p,{severity:a.severity,count:a.count,ruleIds:new Set([a.ruleId])})}let w=Array.from(g.entries()).map(([a,p])=>({category:a,...p})).sort((a,p)=>{let A=m[a.severity]-m[p.severity];return A!==0?A:p.count-a.count}).slice(0,5);if(w.length>0){s.push(" Finding families:");for(let a of w){let p=Array.from(a.ruleIds).slice(0,2).join(", ");s.push(` ${a.count} evidence \xB7 ${a.category} (${p})`)}s.push("")}}if(t.actions?.length){s.push(" Top actions:");for(let i of t.actions.slice(0,5))s.push(` [${i.severity}/${i.confidence}] ${gt(i.title)}`);s.push("")}}return r>0&&!t.showDetails&&(t.incidents?.length||t.actions?.length)&&(s.push(" Details are in the report link above."),s.push(" For machine-readable findings, re-run with --json."),s.push("")),r>0&&(t.gatedIncidentCount&&t.gatedIncidentCount>0?(s.push(` ${t.gatedIncidentCount} more incidents found with full analysis \u2014 sign up at llynt.dev`),s.push("")):t.gatedIssueCount&&t.gatedIssueCount>0&&(s.push(` ${t.gatedIssueCount} more issues found with full analysis \u2014 sign up at llynt.dev`),s.push(""))),t.isAnonymous?(s.push(" Sign up free at https://llynt.dev for 26 rules, 2 viewports, 100 checks/day."),s.push(" Then: export LLYNT_API_KEY=<your-key>"),s.push("")):r>0&&(s.push(" Want full SARIF evidence + agent-ready fixes?"),s.push(" Set up the PR gate \u2014 your agent can do it:"),s.push(" npx llynt init --github")),s.join(`
|
|
5
|
+
`).trimEnd()}function xt(t){let n={error:0,warning:1,info:2},e=t.issues.map(l=>({severity:l.severity,ruleId:l.ruleId,message:ae(l)})).sort((l,s)=>(n[l.severity]??3)-(n[s.severity]??3)),o=e.filter(l=>l.severity==="error").length,r=e.filter(l=>l.severity==="warning").length,u=e.length-o-r;return{url:t.url,viewports:t.viewports,nodeCount:Math.max(0,Math.floor(t.nodeCount)),costUnits:Math.max(0,Math.floor(t.costUnits)),issueCount:e.length,...t.incidentCount!=null?{incidentCount:t.incidentCount}:{},...t.gatedIncidentCount!=null?{gatedIncidentCount:Math.max(0,Math.floor(t.gatedIncidentCount))}:{},errors:o,warnings:r,infos:u,truncated:!!t.truncated,issues:e,gatedIssueCount:Math.max(0,Math.floor(t.gatedIssueCount??0)),...t.reportUrl?{reportUrl:t.reportUrl}:{},...t.reportExpiresAt?{reportExpiresAt:t.reportExpiresAt}:{},...t.rulesChecked!=null?{rulesChecked:t.rulesChecked,rulesAvailable:t.rulesAvailable}:{},...t.incidents?.length?{incidents:t.incidents}:{},...t.actions?.length?{actions:t.actions}:{},...t.compositional?{compositional:t.compositional}:{},...t.timings?{timings:t.timings}:{}}}var pe=P(require("http")),de=P(require("https"));function nt(t){let n=t.trim();if(!n)throw new Error("LLYNT_API_URL is empty");let e=new URL(n),o=e.pathname.replace(/\/+$/g,"");o.endsWith("/v1/check")&&(o=o.slice(0,o.length-9)),e.pathname=o||"/",e.search="",e.hash="";let r=e.pathname==="/"?"":e.pathname.replace(/\/+$/g,"");return`${e.origin}${r}`}function me(t,n,e,o){return new Promise((r,u)=>{let s=(t.protocol==="https:"?de:pe).request({...n,protocol:t.protocol,hostname:t.hostname,port:t.port,path:t.pathname+t.search},i=>{let g=[];i.on("data",m=>g.push(m)),i.on("end",()=>r({statusCode:i.statusCode??0,body:Buffer.concat(g)}))});s.on("error",u),s.setTimeout(o,()=>s.destroy(new Error(`Report upload timed out after ${o}ms`))),s.end(e)})}async function Pt(t,n,e){try{let o=nt(t),r=new URL(`${o}/reports`),u=1e4,l=Buffer.from(JSON.stringify(e),"utf8"),s=await me(r,{method:"POST",headers:{"content-type":"application/json",accept:"application/json","content-length":String(l.length),"user-agent":"llynt-check-report-upload/0",...n?{authorization:`Bearer ${n}`}:{}}},l,u);if(s.statusCode<200||s.statusCode>=300)return null;let i=JSON.parse(s.body.toString("utf8"));return!i||typeof i.reportUrl!="string"||typeof i.expiresAt!="string"?null:i}catch{return null}}At();var Lt=/^llynt_(?:key|free|paid|pro|enterprise|internal)_[A-Za-z0-9_-]{16,}$/;var be=["LLYNT_API_KEY","LLYNT_PRO_API_KEY","LLYNT_INTERNAL_API_KEY"];function Rt(t=process.env){for(let n of be){let e=t[n];if(typeof e=="string"&&e.trim())return e.trim()}}var _e=0,$e=2,k=3,jt=[{width:360,height:720},{width:768,height:720}],Te=[{width:360,height:720}];function Me(){try{let t=require("node:fs"),e=require("node:path").resolve(__dirname,"package.json");if(!t.existsSync(e))return"0.0.0";let o=JSON.parse(t.readFileSync(e,"utf8"));return typeof o.version!="string"?"0.0.0":o.version.trim()||"0.0.0"}catch{return"0.0.0"}}function Ne(t){for(let n=0;n<t.length;n++){let e=t[n];if(!e.startsWith("--"))return e;let o=t[n+1];o&&!o.startsWith("--")&&(n+=1)}}function Oe(t){if(typeof t!="string"||!t.trim())return jt;let n=t.split(",").map(e=>e.trim()).filter(Boolean).map(e=>{let o=e.match(/^(\d{2,5})x(\d{2,5})$/i);if(!o)throw new Error(`Invalid viewport "${e}". Expected format WIDTHxHEIGHT.`);return{width:Number(o[1]),height:Number(o[2])}});return n.length?n:jt}function Be(t,n){return t}function He(t){let n=t instanceof Error?t.message:String(t);return n.includes("Cannot find module 'playwright'")||n.includes("Cannot find package 'playwright'")||n.includes("ERR_MODULE_NOT_FOUND")}function De(t,n){let e=n instanceof Error?n.message:String(n);return e.includes("ERR_CONNECTION_REFUSED")?`Error: Could not reach ${t}
|
|
6
|
+
Is your dev server running?`:e.includes("ERR_NAME_NOT_RESOLVED")?`Error: Could not resolve hostname for ${t}`:e.toLowerCase().includes("timed out")?`Error: Page load timed out after 30s: ${t}`:e.includes("eval is disabled")?`Error: This page blocks runtime script evaluation via CSP (eval disabled)
|
|
7
|
+
Try a different route, narrower selector, or run on a controlled preview environment.`:null}function v(t){process.stdout.write(t.trimEnd()+`
|
|
8
|
+
`)}function Ue(t){let n=t.match(/Hosted API error \((\d{3})\):\s*([\s\S]+)$/);if(!n)return null;let e=Number(n[1]),o=n[2]?.trim();if(!Number.isFinite(e))return null;if(!o)return{status:e};try{let r=JSON.parse(o);return{status:e,body:r}}catch{return{status:e}}}function Fe(t){let n=new Map;for(let o of t)if(Array.isArray(o))for(let r of o){if(!r||typeof r.ruleId!="string"||typeof r.message!="string")continue;let u=r.severity==="error"||r.severity==="warning"||r.severity==="info"?r.severity:"warning",l=`${r.ruleId}::${u}::${r.message}`,s=n.get(l);s?s.count+=Math.max(1,Math.floor(r.count||0)):n.set(l,{ruleId:r.ruleId,severity:u,count:Math.max(1,Math.floor(r.count||0)),message:r.message,category:typeof r.category=="string"&&r.category.trim()?r.category.trim():void 0})}if(n.size===0)return;let e={error:0,warning:1,info:2};return Array.from(n.values()).sort((o,r)=>{let u=e[o.severity]-e[r.severity];return u!==0?u:r.count-o.count})}function Ve(t){let n=new Map;for(let o of t)if(Array.isArray(o))for(let r of o){if(!r||typeof r.actionId!="string"||typeof r.title!="string")continue;let u=r.severity==="error"||r.severity==="warning"||r.severity==="info"?r.severity:"warning",l=r.confidence==="high"||r.confidence==="medium"||r.confidence==="low"?r.confidence:"low",s=r.actionId.trim()||`${r.title}::${u}`,i=n.get(s);if(i){let g=new Set([...i.incidentRuleIds,...r.incidentRuleIds.filter(Boolean)]);i.incidentRuleIds=Array.from(g).sort()}else n.set(s,{actionId:s,title:r.title,why:r.why,severity:u,confidence:l,incidentRuleIds:[...new Set(r.incidentRuleIds.filter(Boolean))].sort(),expectedImpact:r.expectedImpact})}if(n.size===0)return;let e={error:0,warning:1,info:2};return Array.from(n.values()).sort((o,r)=>{let u=e[o.severity]-e[r.severity];return u!==0?u:r.incidentRuleIds.length-o.incidentRuleIds.length})}function Ke(t){let n=t.filter(a=>!!a);if(n.length===0)return;let e=new Map,o=0,r=0,u={error:0,warning:0,info:0},l=[],s="PASS",i=new Set;for(let a of n){o=Math.max(o,Math.max(0,Math.floor(a.componentCount||0))),i.add(a.routePolicy),a.routeStatus==="FAIL"&&(s="FAIL"),r+=Math.max(0,Math.floor(a.pageLayoutSummary.issueCount||0)),u.error+=Math.max(0,Math.floor(a.pageLayoutSummary.severityCounts.error||0)),u.warning+=Math.max(0,Math.floor(a.pageLayoutSummary.severityCounts.warning||0)),u.info+=Math.max(0,Math.floor(a.pageLayoutSummary.severityCounts.info||0)),l.push(...a.pageLayoutSummary.topFindings);for(let p of a.componentSummaries){let A=`${p.type}:${p.rootId}`,h=e.get(A);h?(h.issueCount+=Math.max(0,Math.floor(p.issueCount||0)),h.severityCounts.error+=Math.max(0,Math.floor(p.severityCounts.error||0)),h.severityCounts.warning+=Math.max(0,Math.floor(p.severityCounts.warning||0)),h.severityCounts.info+=Math.max(0,Math.floor(p.severityCounts.info||0)),h.topFindings=[...new Set([...h.topFindings,...p.topFindings])].slice(0,3),h.presentParts=[...new Set([...h.presentParts,...p.presentParts])],h.missingParts=[...new Set([...h.missingParts,...p.missingParts])]):e.set(A,{...p,severityCounts:{...p.severityCounts},topFindings:[...p.topFindings],requiredParts:[...p.requiredParts],presentParts:[...p.presentParts],missingParts:[...p.missingParts]})}}let g=Array.from(e.values()).sort((a,p)=>a.severityCounts.error!==p.severityCounts.error?p.severityCounts.error-a.severityCounts.error:a.issueCount!==p.issueCount?p.issueCount-a.issueCount:a.type.localeCompare(p.type)),m=g.filter(a=>a.issueCount>0).length,w=g.filter(a=>a.missingParts.length>0).length;return{componentCount:Math.max(o,g.length),componentsWithIssues:m,componentsWithMissingParts:w,componentSummaries:g,pageLayoutSummary:{severityCounts:u,issueCount:r,topFindings:[...new Set(l.filter(Boolean))].slice(0,3)},routeStatus:s,routePolicy:Array.from(i).join(" | ")}}async function We(t,n){let e=await import("http"),o=await import("https"),r=new URL(`${t}/signup`),u=Buffer.from(JSON.stringify({email:n}),"utf8");return new Promise(l=>{let i=(r.protocol==="https:"?o:e).request({method:"POST",protocol:r.protocol,hostname:r.hostname,port:r.port,path:r.pathname,headers:{"content-type":"application/json","content-length":String(u.length),"user-agent":"llynt-cli-signup/0"}},g=>{let m=[];g.on("data",w=>m.push(w)),g.on("end",()=>{try{if(g.statusCode&&g.statusCode>=200&&g.statusCode<300){let w=JSON.parse(Buffer.concat(m).toString("utf8")),a=typeof w.apiKey=="string"?w.apiKey.trim():"";l(Lt.test(a)?a:null)}else l(null)}catch{l(null)}})});i.on("error",()=>l(null)),i.setTimeout(1e4,()=>{i.destroy(),l(null)}),i.end(u)})}async function zt(t){let n=et(t),e=Ne(t);if(!e)return v(`Error: Missing URL.
|
|
9
|
+
Usage: llynt check <url>`),k;let o=(typeof n.key=="string"?String(n.key):Rt()??"").trim(),r=ht(n.signup),u=typeof n.email=="string"?String(n.email).trim():"",l=!o&&!r;if(r&&!o){if(!u)return v("Error: --signup requires --email <your@email.com>"),k;let h=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),L=nt(h);v(` Signing up ${u}...`);let b=await We(L,u);if(!b)return v(" Error: Signup failed. Try at https://llynt.dev"),k;o=b,v(` API key: ${b}`),v(` Save it: export LLYNT_API_KEY=${b}
|
|
10
|
+
`)}try{let{createRequire:h}=await import("module"),L=h(process.cwd()+"/package.json");try{L.resolve("playwright")}catch{await import("playwright")}}catch(h){return He(h)?(v(`llynt check ${e}
|
|
11
11
|
|
|
12
12
|
Error: Playwright not found.
|
|
13
|
-
Install it: npm install -D playwright && npx playwright install chromium`),
|
|
13
|
+
Install it: npm install -D playwright && npx playwright install chromium`),k):(v(`llynt check ${e}
|
|
14
14
|
|
|
15
|
-
Error: Failed to load Playwright runtime.`),
|
|
15
|
+
Error: Failed to load Playwright runtime.`),k)}let s=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),i=kt(s),g=nt(s),m=(typeof n.selector=="string"?String(n.selector):"body").trim()||"body",w=typeof n.profile=="string"?String(n.profile):void 0,a=ht(n.json),p=ht(n.verbose),A;try{l?A=Te:A=Oe(n.viewports)}catch(h){return v(`llynt check ${e}
|
|
16
16
|
|
|
17
|
-
Error: ${
|
|
17
|
+
Error: ${h instanceof Error?h.message:String(h)}`),k}try{let h=Date.now(),{runHostedThinClientChecks:L}=await Promise.resolve().then(()=>(Wt(),Kt)),b=await L({targets:[{url:e,selector:m,viewports:A}],timeoutMs:3e4,traceOnFailure:!1,computedStyleMode:"essential",internComputedStyles:!0,maxNodes:1e4,allowTruncated:!0,client:{apiUrl:i,apiKey:o||void 0,timeoutMs:3e4,compression:"gzip"}}),x=b.flatMap(C=>C.response.issues),T=Be(x,w),M=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.response.meta.nodeCount??0)),0),D=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.response.meta.costUnits??0)),0),U=b.some(C=>!!C.response.meta.truncated),B=b.reduce((C,_)=>C+(_.response.meta.gatedIssueCount??0),0),ut=b.reduce((C,_)=>C+(_.response.meta.gatedIncidentCount??0),0)||void 0,G=b.flatMap(C=>C.viewports),W=b[0]?.response.meta.rulesChecked,$=b[0]?.response.meta.rulesAvailable,F=b.reduce((C,_)=>C+(_.response.meta.incidentCount??0),0)||void 0,X=Fe(b.map(C=>C.response.meta.incidents)),ct=Ve(b.map(C=>C.response.meta.actions)),lt=Ke(b.map(C=>C.response.meta.compositional)),pt=b[0]?.response.meta.tier,H=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.timings?.captureMs??0)),0),j=b.reduce((C,_)=>C+Math.max(0,Math.floor(_.timings?.analyzeMs??0)),0),dt=Date.now()-h,mt={url:e,viewports:G,issueCount:T.length,issues:T.map(C=>({severity:C.severity,ruleId:C.ruleId,message:C.message.replace(/\s+/g," ").trim()})),meta:{nodeCount:M,costUnits:D,truncated:U,capturedAt:new Date().toISOString(),clientVersion:Me(),...pt?{tier:pt}:{},...F!=null?{incidentCount:F}:{},...X?{incidents:X}:{},...ct?{actions:ct}:{},...lt?{compositional:lt}:{},...W!=null?{rulesChecked:W,rulesAvailable:$}:{}}},z=await Pt(g,o||"",mt),Z={issues:T,url:e,viewports:G,nodeCount:M,costUnits:D,truncated:U,reportUrl:z?.reportUrl,reportExpiresAt:z?.expiresAt,gatedIssueCount:B,gatedIncidentCount:ut,isAnonymous:l,rulesChecked:W,rulesAvailable:$,incidentCount:F,incidents:X,actions:ct,compositional:lt,showDetails:p,timings:{captureMs:H,analyzeMs:j,totalMs:dt}};if(a){let C=xt(Z);v(JSON.stringify(C,null,2))}else p&&v(`llynt check ${e}
|
|
18
18
|
|
|
19
|
-
API: ${
|
|
19
|
+
API: ${i}`),v(It(Z));return T.length>0?$e:_e}catch(h){let L=De(e,h);if(L)return v(`llynt check ${e}
|
|
20
20
|
|
|
21
|
-
${
|
|
22
|
-
`)}`),
|
|
21
|
+
${L.replace(/\n/g,`
|
|
22
|
+
`)}`),k;let b=h instanceof Error?h.message:String(h),x=Ue(b);if(x){let T=typeof x.body?.hint=="string"?x.body.hint:"",M=typeof x.body?.error=="string"?x.body.error:"";if(x.status===401)return v(`llynt check ${e}
|
|
23
23
|
|
|
24
|
-
Error:
|
|
24
|
+
Error: Invalid API key. Sign up at https://llynt.dev`),k;if(x.status===403)return v(`llynt check ${e}
|
|
25
25
|
|
|
26
|
-
Error:
|
|
26
|
+
Error: API key revoked. Sign up at https://llynt.dev for a new key.`),k;if(x.status===413)return v(`llynt check ${e}
|
|
27
27
|
|
|
28
|
-
Error:
|
|
28
|
+
Error: Page payload exceeds current check limits.
|
|
29
|
+
${T||"Tighten selector, reduce viewports, or run with a key on a smaller scope."}`),k;if(x.status===429)return v(`llynt check ${e}
|
|
29
30
|
|
|
30
|
-
Error: Rate limit exceeded
|
|
31
|
+
Error: ${M||"Rate limit exceeded"}.
|
|
32
|
+
${T||(l?"Sign up at https://llynt.dev for higher limits.":"Try again later or upgrade at https://llynt.dev")}`),k;let D=T||`Hosted API returned ${x.status}`;return v(`llynt check ${e}
|
|
31
33
|
|
|
32
|
-
Error: Hosted API failure
|
|
34
|
+
Error: Hosted API failure.
|
|
35
|
+
${D}`),k}return b.includes("Hosted API request timed out")?(v(`llynt check ${e}
|
|
33
36
|
|
|
34
|
-
Error: ${
|
|
35
|
-
|
|
37
|
+
Error: Hosted API timeout (${i})`),k):b.includes("Hosted API error (401)")?(v(`llynt check ${e}
|
|
38
|
+
|
|
39
|
+
Error: Invalid API key. Sign up at https://llynt.dev`),k):b.includes("Hosted API error (403)")?(v(`llynt check ${e}
|
|
40
|
+
|
|
41
|
+
Error: API key revoked. Sign up at https://llynt.dev for a new key.`),k):b.includes("Hosted API error (429)")?(v(`llynt check ${e}
|
|
42
|
+
|
|
43
|
+
Error: Rate limit exceeded. ${l?"Sign up at https://llynt.dev for 100 checks/day.":"Try again later or upgrade at https://llynt.dev"}`),k):b.includes("Hosted API error")?(v(`llynt check ${e}
|
|
44
|
+
|
|
45
|
+
Error: Hosted API failure at ${i}`),k):(v(`llynt check ${e}
|
|
46
|
+
|
|
47
|
+
Error: ${b.split(`
|
|
48
|
+
`)[0]}`),k)}}var at=P(require("node:fs")),O=P(require("node:path"));var Yt=`name: llynt-pr-gate
|
|
49
|
+
|
|
50
|
+
on:
|
|
51
|
+
pull_request:
|
|
52
|
+
paths:
|
|
53
|
+
- 'llynt/**'
|
|
54
|
+
- '.github/workflows/llynt-pr-gate.yml'
|
|
55
|
+
|
|
56
|
+
jobs:
|
|
57
|
+
check:
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- name: Checkout
|
|
61
|
+
uses: actions/checkout@v4
|
|
62
|
+
- name: Setup Node
|
|
63
|
+
uses: actions/setup-node@v4
|
|
64
|
+
with:
|
|
65
|
+
node-version: '22'
|
|
66
|
+
- name: Install Playwright Chromium
|
|
67
|
+
run: npx playwright install --with-deps chromium
|
|
68
|
+
- name: llynt check
|
|
69
|
+
env:
|
|
70
|
+
LLYNT_API_KEY: \${{ secrets.LLYNT_API_KEY }}
|
|
71
|
+
run: npx llynt check \${{ vars.LLYNT_BASE_URL || 'http://localhost:3000' }}
|
|
72
|
+
`,qt=`name: llynt-repo-sweep
|
|
73
|
+
|
|
74
|
+
on:
|
|
75
|
+
workflow_dispatch:
|
|
76
|
+
schedule:
|
|
77
|
+
- cron: '0 12 * * 1'
|
|
78
|
+
|
|
79
|
+
jobs:
|
|
80
|
+
sweep:
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
steps:
|
|
83
|
+
- name: Checkout
|
|
84
|
+
uses: actions/checkout@v4
|
|
85
|
+
- name: Setup Node
|
|
86
|
+
uses: actions/setup-node@v4
|
|
87
|
+
with:
|
|
88
|
+
node-version: '22'
|
|
89
|
+
- name: Install Playwright Chromium
|
|
90
|
+
run: npx playwright install --with-deps chromium
|
|
91
|
+
- name: llynt check
|
|
92
|
+
env:
|
|
93
|
+
LLYNT_API_KEY: \${{ secrets.LLYNT_API_KEY }}
|
|
94
|
+
run: npx llynt check \${{ vars.LLYNT_BASE_URL || 'http://localhost:3000' }}
|
|
95
|
+
`;function bt(t,n,e,o){if(at.existsSync(t)&&!e){o.skipped.push(t);return}at.mkdirSync(O.dirname(t),{recursive:!0}),at.writeFileSync(t,n,"utf8"),o.created.push(t)}function Jt(t){if(!t)return;let n=t.trim();if(n)try{return new URL(n).toString().replace(/\/+$/,"")}catch{return}}function je(){return["version: v0","run:"," profile: guard-minimal"," depth: quick"," focus:"," - static",""].join(`
|
|
96
|
+
`)}function ze(t){let n={_schemaVersion:"llynt-targets@0",createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),...t?{baseUrlHint:t}:{},defaults:{selector:"body",viewports:[{width:360,height:720}]},targets:[{id:"home",path:"/",tags:["bootstrap"],lifecycleState:"approved"}],collections:[{id:"all",description:"Initial bootstrap target set.",targetIds:["home"]}]};return`${JSON.stringify(n,null,2)}
|
|
97
|
+
`}function Ye(t){process.stdout.write(`llynt init
|
|
98
|
+
|
|
99
|
+
`),process.stdout.write(` Created: ${t.created.length}
|
|
100
|
+
`);for(let n of t.created)process.stdout.write(` + ${n}
|
|
101
|
+
`);process.stdout.write(` Skipped: ${t.skipped.length}
|
|
102
|
+
`);for(let n of t.skipped)process.stdout.write(` = ${n}
|
|
103
|
+
`);process.stdout.write(`
|
|
104
|
+
`),process.stdout.write(`Next steps:
|
|
105
|
+
`),process.stdout.write(` 1. Export your API key: export LLYNT_API_KEY=<your-key>
|
|
106
|
+
`),process.stdout.write(` 2. Set your base URL in GitHub variable: LLYNT_BASE_URL
|
|
107
|
+
`),process.stdout.write(` 3. Run: npx llynt check <your-url>
|
|
108
|
+
`)}async function Gt(t){let n=et(t),e=typeof n["repo-root"]=="string"?String(n["repo-root"]):process.cwd(),o=typeof n.config=="string"?String(n.config):O.join("llynt","config.yml"),r=typeof n["targets-path"]=="string"?String(n["targets-path"]):O.join("llynt","targets.json"),u=!!n.force,l=!!n["skip-workflows"],s=!!n["skip-bootstrap-targets"],i=!!n.json,g=Jt(typeof n["base-url"]=="string"?String(n["base-url"]):void 0)??Jt(process.env.LLYNT_BASE_URL),m={configPath:O.resolve(e,o),targetsPath:O.resolve(e,r),prWorkflowPath:O.resolve(e,".github","workflows","llynt-pr-gate.yml"),sweepWorkflowPath:O.resolve(e,".github","workflows","llynt-repo-sweep.yml"),created:[],skipped:[]};return bt(m.configPath,je(),u,m),s||bt(m.targetsPath,ze(g),u,m),l||(bt(m.prWorkflowPath,Yt,u,m),bt(m.sweepWorkflowPath,qt,u,m)),i?process.stdout.write(JSON.stringify({ok:!0,created:m.created,skipped:m.skipped,paths:{config:m.configPath,targets:m.targetsPath,prWorkflow:m.prWorkflowPath,sweepWorkflow:m.sweepWorkflowPath}},null,2)+`
|
|
109
|
+
`):Ye(m),0}var qe=P(require("http")),Je=P(require("https"));var Ge=0,vt=3;function J(t){process.stdout.write(t.trimEnd()+`
|
|
110
|
+
`)}function Xe(t){return t.trim().toLowerCase()}function Ze(t){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)}async function Qe(t,n,e){let o=new URL(`${t}/v1/rotate-key`),r=Buffer.from(JSON.stringify({email:n,currentKey:e}),"utf8");return new Promise(u=>{let s=(o.protocol==="https:"?Je:qe).request({method:"POST",protocol:o.protocol,hostname:o.hostname,port:o.port,path:o.pathname,headers:{"content-type":"application/json","content-length":String(r.length),"user-agent":"llynt-cli-rotate/0"}},i=>{let g=[];i.on("data",m=>g.push(m)),i.on("end",()=>{let m=Buffer.concat(g).toString("utf8");try{let w=JSON.parse(m);if(i.statusCode&&i.statusCode>=200&&i.statusCode<300&&typeof w.apiKey=="string"){u({apiKey:w.apiKey});return}u({error:w.error??`Rotation failed (HTTP ${i.statusCode??0})`})}catch{u({error:`Rotation failed (HTTP ${i.statusCode??0})`})}})});s.on("error",()=>u({error:"Rotation request failed"})),s.setTimeout(1e4,()=>{s.destroy(),u({error:"Rotation request timed out"})}),s.end(r)})}async function Xt(t){let n=et(t),e=typeof n.email=="string"?String(n.email):"",o=typeof n.key=="string"?String(n.key).trim():"",r=Xe(e);if(!r||!Ze(r)||!o)return J("Error: Usage: llynt rotate --email <you@email.com> --key <current-key>"),vt;let u=(typeof process.env.LLYNT_API_URL=="string"?process.env.LLYNT_API_URL:"https://api.llynt.dev").trim(),l;try{l=nt(u)}catch(i){return J(`Error: ${i instanceof Error?i.message:String(i)}`),vt}J(` Rotating key for ${r}...`);let s=await Qe(l,r,o);return s.apiKey?(J(" New key issued:"),J(` export LLYNT_API_KEY=${s.apiKey}`),J(" npx llynt check <your-dev-url>"),Ge):(J(` Error: ${s.error??"Rotation failed."}`),vt)}var tn=`llynt - UI integrity checks for AI coding agents
|
|
36
111
|
|
|
37
112
|
Usage:
|
|
38
113
|
llynt check <url> Check a URL
|
|
114
|
+
llynt init Scaffold PR gate/bootstrap files
|
|
115
|
+
llynt rotate Rotate hosted API key
|
|
39
116
|
llynt help Show this help
|
|
40
117
|
|
|
41
|
-
7 checks run instantly. Set LLYNT_API_KEY for
|
|
118
|
+
7 checks run instantly. Set LLYNT_API_KEY for 26 rules.
|
|
42
119
|
Sign up free at https://llynt.dev
|
|
43
120
|
|
|
44
121
|
Examples:
|
|
45
122
|
npx llynt check http://localhost:3000
|
|
46
123
|
npx llynt check https://my-app.vercel.app
|
|
47
|
-
`;async function
|
|
124
|
+
`;async function en(){let t=process.argv.slice(2),n=t[0];if((!n||n==="help"||n==="--help"||n==="-h")&&(process.stdout.write(tn),process.exit(0)),n==="check"){let e=await zt(t.slice(1));process.exit(e)}if(n==="init"){let e=await Gt(t.slice(1));process.exit(e)}if(n==="rotate"){let e=await Xt(t.slice(1));process.exit(e)}process.stderr.write(`Unknown command: ${n}
|
|
48
125
|
|
|
49
126
|
Run "llynt help" for usage.
|
|
50
|
-
`),process.exit(3)}
|
|
127
|
+
`),process.exit(3)}en().catch(t=>{process.stderr.write(`Fatal: ${t instanceof Error?t.message:String(t)}
|
|
51
128
|
`),process.exit(3)});
|