cbrowser 19.4.0 → 19.4.2

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.
Files changed (33) hide show
  1. package/dist/analysis/accessibility-empathy.d.ts.map +1 -1
  2. package/dist/analysis/accessibility-empathy.js +43 -6
  3. package/dist/analysis/accessibility-empathy.js.map +1 -1
  4. package/dist/analysis/page-understanding.d.ts.map +1 -1
  5. package/dist/analysis/page-understanding.js +27 -1
  6. package/dist/analysis/page-understanding.js.map +1 -1
  7. package/dist/browser.d.ts.map +1 -1
  8. package/dist/browser.js +6 -0
  9. package/dist/browser.js.map +1 -1
  10. package/dist/mcp-server.d.ts.map +1 -1
  11. package/dist/mcp-server.js +3 -2
  12. package/dist/mcp-server.js.map +1 -1
  13. package/dist/mcp-tools/base/performance-tools.d.ts.map +1 -1
  14. package/dist/mcp-tools/base/performance-tools.js +3 -2
  15. package/dist/mcp-tools/base/performance-tools.js.map +1 -1
  16. package/dist/mcp-tools/base/persona-comparison-tools.d.ts.map +1 -1
  17. package/dist/mcp-tools/base/persona-comparison-tools.js +5 -2
  18. package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
  19. package/dist/performance/metrics.d.ts +8 -1
  20. package/dist/performance/metrics.d.ts.map +1 -1
  21. package/dist/performance/metrics.js +24 -14
  22. package/dist/performance/metrics.js.map +1 -1
  23. package/dist/types.d.ts +7 -0
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/types.js.map +1 -1
  26. package/dist/visual/regression.d.ts +17 -0
  27. package/dist/visual/regression.d.ts.map +1 -1
  28. package/dist/visual/regression.js +19 -0
  29. package/dist/visual/regression.js.map +1 -1
  30. package/dist/visual/responsive.d.ts.map +1 -1
  31. package/dist/visual/responsive.js +59 -12
  32. package/dist/visual/responsive.js.map +1 -1
  33. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"accessibility-empathy.d.ts","sourceRoot":"","sources":["../../src/analysis/accessibility-empathy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoCH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EAEnB,oBAAoB,EAEpB,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AA8OrB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,4BAA4B,CAAA;CAAE,EAChH,QAAQ,EAAE,CAAC,EAAE,EACb,UAAU,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC7B;IAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAAC,UAAU,EAAE,KAAK,CAAC,CAAC,GAAG;QAAE,eAAe,EAAE,IAAI,CAAC;QAAC,kBAAkB,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,CAAA;KAAE,CAAC,CAAA;CAAE,CAmB3G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACnH,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAWhC;AAqgFD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAuG3E;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CA2UjF;AAsBD,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAGhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAE9E;AAmKD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAmB,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,KAAK,CAAC;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAC7E,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,EAChC,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,EACnC,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC5B,MAAM,EAAE,CAkBV;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAmY7B"}
1
+ {"version":3,"file":"accessibility-empathy.d.ts","sourceRoot":"","sources":["../../src/analysis/accessibility-empathy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoCH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EAEnB,oBAAoB,EAEpB,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AA8OrB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,4BAA4B,CAAA;CAAE,EAChH,QAAQ,EAAE,CAAC,EAAE,EACb,UAAU,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC7B;IAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAAC,UAAU,EAAE,KAAK,CAAC,CAAC,GAAG;QAAE,eAAe,EAAE,IAAI,CAAC;QAAC,kBAAkB,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,CAAA;KAAE,CAAC,CAAA;CAAE,CAmB3G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACnH,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAWhC;AA0iFD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAuG3E;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CA2UjF;AAsBD,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAGhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAE9E;AAmKD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAmB,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,KAAK,CAAC;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAC7E,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,EAChC,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,EACnC,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC5B,MAAM,EAAE,CAkBV;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAmY7B"}
@@ -289,7 +289,7 @@ async function detectSmallTouchTargets(ctx) {
289
289
  const _motorControl = persona.accessibilityTraits.motorControl ?? 0.5;
290
290
  const smallTargets = await page.$$eval('button, a, input[type="checkbox"], input[type="radio"], [role="button"], [onclick]', (elements, vpOnly) => {
291
291
  const vh = window.innerHeight;
292
- return elements.map(el => {
292
+ const mapped = elements.map(el => {
293
293
  const rect = el.getBoundingClientRect();
294
294
  const text = el.textContent?.trim().slice(0, 50) || '';
295
295
  const href = el.href || el.getAttribute('href') || '';
@@ -348,8 +348,32 @@ async function detectSmallTouchTargets(ctx) {
348
348
  area: rect.width * rect.height,
349
349
  inViewport: rect.bottom > 0 && rect.top < vh,
350
350
  exempt: isSkipLink || isVisuallyHidden || isInlineTextLink,
351
+ notTarget: isSkipLink || isVisuallyHidden,
352
+ inlineLink: isInlineTextLink,
353
+ spaced: false,
354
+ el,
351
355
  };
352
- }).filter(el => {
356
+ });
357
+ // WCAG 2.5.8's spacing exception: an undersized target passes when a 24px
358
+ // circle centred on it intersects no other target and no other undersized
359
+ // target's circle. Without it a lone 152x20 button was a critical AA failure
360
+ // (v10 Q2). Measured against every live target, on or off screen; a target
361
+ // nested in another (a button in a link) is the same target, not a neighbour.
362
+ // An inline text link is exempt from the SIZE rule only: it is still a target,
363
+ // so it stays a neighbour (with no circle of its own). 19.4.1 dropped it, and
364
+ // cbrowser.ai's carousel step dots overlapping an inline link passed.
365
+ const live = mapped.filter((m) => m.area > 0 && !m.notTarget);
366
+ const small = (m) => !m.inlineLink && (Math.round(m.width) < 24 || Math.round(m.height) < 24);
367
+ const centre = (m) => ({ x: m.x + m.width / 2, y: m.y + m.height / 2 });
368
+ const distToRect = (p, m) => Math.hypot(Math.max(m.x - p.x, 0, p.x - (m.x + m.width)), Math.max(m.y - p.y, 0, p.y - (m.y + m.height)));
369
+ for (const u of live) {
370
+ if (!small(u))
371
+ continue;
372
+ const c = centre(u);
373
+ u.spaced = !live.some((t) => t !== u && !t.el.contains(u.el) && !u.el.contains(t.el) &&
374
+ (distToRect(c, t) < 12 || (small(t) && Math.hypot(c.x - centre(t).x, c.y - centre(t).y) < 24)));
375
+ }
376
+ return mapped.map(({ el: _el, ...rest }) => rest).filter(el => {
353
377
  if (el.area <= 0 || el.exempt)
354
378
  return false;
355
379
  if (vpOnly && !el.inViewport)
@@ -373,7 +397,8 @@ async function detectSmallTouchTargets(ctx) {
373
397
  .map((t) => {
374
398
  const w = Math.round(t.width);
375
399
  const h = Math.round(t.height);
376
- return { ...t, w, h, failsAA: w < aaMinimum || h < aaMinimum, failsAAA: w < aaaTarget || h < aaaTarget };
400
+ const undersized = w < aaMinimum || h < aaMinimum;
401
+ return { ...t, w, h, undersized, failsAA: undersized && !t.spaced, failsAAA: w < aaaTarget || h < aaaTarget };
377
402
  })
378
403
  .filter((t) => t.failsAAA);
379
404
  // AA failures first, THEN the cap, so AAA-only candidates (now more numerous
@@ -393,6 +418,14 @@ async function detectSmallTouchTargets(ctx) {
393
418
  rawSeverity = w < 16 || h < 16 ? "critical" : "major";
394
419
  ctx.wcagViolations.add("2.5.8");
395
420
  }
421
+ else if (target.undersized) {
422
+ // Under 24px but passes 2.5.8 AA by its spacing exception; still under the
423
+ // 44px of 2.5.5 AAA. (v10 Q2)
424
+ wcagCriteria.push("2.5.5");
425
+ description = `Touch target under 24px (${w}x${h}px) — passes WCAG 2.5.8 AA by its spacing exception (nothing within its 24px circle), but under 44px for 2.5.5 AAA`;
426
+ rawSeverity = "minor";
427
+ ctx.wcagViolations.add("2.5.5");
428
+ }
396
429
  else {
397
430
  // Passes AA but fails AAA: at least 24 on both axes, under 44 on one.
398
431
  wcagCriteria.push("2.5.5");
@@ -890,7 +923,10 @@ async function detectMotionWithoutPause(ctx) {
890
923
  function pushClassOnlyMotionBarrier(ctx, selectors) {
891
924
  const n = selectors.length;
892
925
  ctx.barriers.push({
893
- type: "cognitive_load",
926
+ // "timing", the type 2.2.2 is weighted as. "cognitive_load" rated the fix
927
+ // "hard" and fired the ADHD "complex form layout" friction on pages with no
928
+ // form. (v10 B44/B45)
929
+ type: "timing",
894
930
  element: `${n} class-named motion element${n === 1 ? "" : "s"}`,
895
931
  // A page-wide aggregate; each element is in members.
896
932
  pageLevel: true,
@@ -907,7 +943,8 @@ function pushClassOnlyMotionBarrier(ctx, selectors) {
907
943
  /** One 2.2.2 barrier for an uncredited motion group. */
908
944
  function pushMotionBarrier(ctx, g) {
909
945
  ctx.barriers.push({
910
- type: "cognitive_load",
946
+ // See pushClassOnlyMotionBarrier: 2.2.2 is a timing barrier. (v10 B44/B45)
947
+ type: "timing",
911
948
  element: g.selector,
912
949
  description: `Moving content (${g.memberCount} animated element${g.memberCount === 1 ? "" : "s"}, found by ${g.detectedBy}) has no associated pause, stop or hide control - may distract users with attention difficulties`,
913
950
  affectedPersonas: ["cognitive-adhd", "dyslexic-user"],
@@ -3261,7 +3298,7 @@ export async function runEmpathyAudit(url, options) {
3261
3298
  if (!persona) {
3262
3299
  // Try to find as a regular cognitive persona and wrap it
3263
3300
  const { getAnyPersona, createCognitivePersona } = await import("../personas.js");
3264
- let cognitivePersona = getAnyPersona(personaName);
3301
+ const cognitivePersona = getAnyPersona(personaName);
3265
3302
  // No CMS fallback here any more: the account's CMS personas were loaded
3266
3303
  // into the scoped registry above, so getAnyPersona already sees them, and
3267
3304
  // a name nothing resolves was refused before the loop. (2026-10-09)