@riddledc/riddle-proof 0.7.21 → 0.7.23

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.
@@ -38,6 +38,79 @@ function stringValue(value) {
38
38
  function numberValue(value) {
39
39
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
40
40
  }
41
+ function horizontalBoundsOverflowPx(value) {
42
+ if (!isRecord(value)) return 0;
43
+ let max = maxPositiveNumber(
44
+ value.overflow_px,
45
+ value.overflow,
46
+ value.bounds_overflow_px,
47
+ value.horizontal_overflow_px,
48
+ value.left_overflow_px,
49
+ value.right_overflow_px
50
+ );
51
+ for (const offender of boundsOffendersForEvidence(value)) {
52
+ max = Math.max(max, horizontalOffenderOverflowPx(offender));
53
+ }
54
+ return roundPixels(max);
55
+ }
56
+ function horizontalOffenderOverflowPx(value) {
57
+ if (!isRecord(value)) return 0;
58
+ let max = maxPositiveNumber(
59
+ value.overflow,
60
+ value.overflow_px,
61
+ value.bounds_overflow_px,
62
+ value.horizontal_overflow_px,
63
+ value.left_overflow_px,
64
+ value.right_overflow_px,
65
+ value.leftOverflowPx,
66
+ value.rightOverflowPx
67
+ );
68
+ const clipped = isRecord(value.clipped || value.clip || value.clipping) ? value.clipped || value.clip || value.clipping : void 0;
69
+ if (isRecord(clipped)) {
70
+ max = Math.max(max, maxPositiveNumber(
71
+ clipped.left,
72
+ clipped.right,
73
+ clipped.left_px,
74
+ clipped.right_px,
75
+ clipped.leftPx,
76
+ clipped.rightPx
77
+ ));
78
+ }
79
+ const rect = isRecord(value.rect || value.bounds || value.bounding_rect || value.boundingRect) ? value.rect || value.bounds || value.bounding_rect || value.boundingRect : void 0;
80
+ const viewportWidth = numberValue(value.viewport_width ?? value.viewportWidth);
81
+ if (isRecord(rect) && viewportWidth !== void 0) {
82
+ const left = numberValue(rect.left);
83
+ const right = numberValue(rect.right);
84
+ if (left !== void 0 && left < 0) max = Math.max(max, Math.abs(left));
85
+ if (right !== void 0 && right > viewportWidth) max = Math.max(max, right - viewportWidth);
86
+ }
87
+ return roundPixels(max);
88
+ }
89
+ function boundsOffendersForEvidence(value) {
90
+ if (!isRecord(value)) return [];
91
+ const offenders = [
92
+ ...Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [],
93
+ ...Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [],
94
+ ...Array.isArray(value.bounds_offenders) ? value.bounds_offenders : [],
95
+ ...Array.isArray(value.boundsOffenders) ? value.boundsOffenders : [],
96
+ ...Array.isArray(value.clipped_elements) ? value.clipped_elements : [],
97
+ ...Array.isArray(value.clippedElements) ? value.clippedElements : [],
98
+ ...Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [],
99
+ ...Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []
100
+ ];
101
+ return offenders.filter((item) => isRecord(item)).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
102
+ }
103
+ function maxPositiveNumber(...values) {
104
+ let max = 0;
105
+ for (const value of values) {
106
+ const number = numberValue(value);
107
+ if (number !== void 0 && number > max) max = number;
108
+ }
109
+ return max;
110
+ }
111
+ function roundPixels(value) {
112
+ return Math.round(value * 100) / 100;
113
+ }
41
114
  function timeoutSecValue(value) {
42
115
  const number = numberValue(value);
43
116
  return number && number > 0 ? Math.ceil(number) : void 0;
@@ -380,7 +453,7 @@ function assessCheckFromEvidence(check, evidence) {
380
453
  message: "No applicable viewport evidence was captured for overflow check."
381
454
  };
382
455
  }
383
- const failed = applicable.filter((viewport) => (viewport.overflow_px ?? 0) > maxOverflow);
456
+ const failed = applicable.filter((viewport) => horizontalBoundsOverflowPx(viewport) > maxOverflow);
384
457
  return {
385
458
  type: check.type,
386
459
  label: checkLabel(check),
@@ -388,9 +461,11 @@ function assessCheckFromEvidence(check, evidence) {
388
461
  evidence: {
389
462
  max_overflow_px: maxOverflow,
390
463
  overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
464
+ bounds_overflow_px: applicable.map((viewport) => horizontalBoundsOverflowPx(viewport)),
465
+ overflow_offender_counts: applicable.map((viewport) => boundsOffendersForEvidence(viewport).length),
391
466
  viewports: applicable.map((viewport) => viewport.name)
392
467
  },
393
- message: failed.length ? `Horizontal overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
468
+ message: failed.length ? `Horizontal bounds overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
394
469
  };
395
470
  }
396
471
  if (check.type === "no_fatal_console_errors") {
@@ -612,6 +687,77 @@ function textMatches(sample, check) {
612
687
  }
613
688
  return String(sample || "").includes(check.text || "");
614
689
  }
690
+ function numberValue(value) {
691
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
692
+ }
693
+ function maxPositiveNumber() {
694
+ let max = 0;
695
+ for (const value of arguments) {
696
+ const number = numberValue(value);
697
+ if (number !== undefined && number > max) max = number;
698
+ }
699
+ return max;
700
+ }
701
+ function roundPixels(value) {
702
+ return Math.round(value * 100) / 100;
703
+ }
704
+ function isRecord(value) {
705
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
706
+ }
707
+ function boundsOffendersForEvidence(value) {
708
+ if (!isRecord(value)) return [];
709
+ const offenders = []
710
+ .concat(Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [])
711
+ .concat(Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [])
712
+ .concat(Array.isArray(value.bounds_offenders) ? value.bounds_offenders : [])
713
+ .concat(Array.isArray(value.boundsOffenders) ? value.boundsOffenders : [])
714
+ .concat(Array.isArray(value.clipped_elements) ? value.clipped_elements : [])
715
+ .concat(Array.isArray(value.clippedElements) ? value.clippedElements : [])
716
+ .concat(Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [])
717
+ .concat(Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []);
718
+ return offenders.filter(isRecord).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
719
+ }
720
+ function horizontalOffenderOverflowPx(value) {
721
+ if (!isRecord(value)) return 0;
722
+ let max = maxPositiveNumber(
723
+ value.overflow,
724
+ value.overflow_px,
725
+ value.bounds_overflow_px,
726
+ value.horizontal_overflow_px,
727
+ value.left_overflow_px,
728
+ value.right_overflow_px,
729
+ value.leftOverflowPx,
730
+ value.rightOverflowPx
731
+ );
732
+ const clipped = value.clipped || value.clip || value.clipping;
733
+ if (isRecord(clipped)) {
734
+ max = Math.max(max, maxPositiveNumber(clipped.left, clipped.right, clipped.left_px, clipped.right_px, clipped.leftPx, clipped.rightPx));
735
+ }
736
+ const rect = value.rect || value.bounds || value.bounding_rect || value.boundingRect;
737
+ const viewportWidth = numberValue(value.viewport_width != null ? value.viewport_width : value.viewportWidth);
738
+ if (isRecord(rect) && viewportWidth !== undefined) {
739
+ const left = numberValue(rect.left);
740
+ const right = numberValue(rect.right);
741
+ if (left !== undefined && left < 0) max = Math.max(max, Math.abs(left));
742
+ if (right !== undefined && right > viewportWidth) max = Math.max(max, right - viewportWidth);
743
+ }
744
+ return roundPixels(max);
745
+ }
746
+ function horizontalBoundsOverflowPx(value) {
747
+ if (!isRecord(value)) return 0;
748
+ let max = maxPositiveNumber(
749
+ value.overflow_px,
750
+ value.overflow,
751
+ value.bounds_overflow_px,
752
+ value.horizontal_overflow_px,
753
+ value.left_overflow_px,
754
+ value.right_overflow_px
755
+ );
756
+ for (const offender of boundsOffendersForEvidence(value)) {
757
+ max = Math.max(max, horizontalOffenderOverflowPx(offender));
758
+ }
759
+ return roundPixels(max);
760
+ }
615
761
  function assessProfile(profile, evidence) {
616
762
  const checks = [];
617
763
  const viewports = evidence.viewports || [];
@@ -729,13 +875,19 @@ function assessProfile(profile, evidence) {
729
875
  });
730
876
  continue;
731
877
  }
732
- const failed = applicable.filter((viewport) => (viewport.overflow_px || 0) > maxOverflow);
878
+ const failed = applicable.filter((viewport) => horizontalBoundsOverflowPx(viewport) > maxOverflow);
733
879
  checks.push({
734
880
  type: check.type,
735
881
  label: check.label || check.type,
736
882
  status: failed.length ? "failed" : "passed",
737
- evidence: { max_overflow_px: maxOverflow, overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null), viewports: applicable.map((viewport) => viewport.name) },
738
- message: failed.length ? "Horizontal overflow exceeded " + maxOverflow + "px in " + failed.length + " viewport(s)." : undefined,
883
+ evidence: {
884
+ max_overflow_px: maxOverflow,
885
+ overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
886
+ bounds_overflow_px: applicable.map((viewport) => horizontalBoundsOverflowPx(viewport)),
887
+ overflow_offender_counts: applicable.map((viewport) => boundsOffendersForEvidence(viewport).length),
888
+ viewports: applicable.map((viewport) => viewport.name)
889
+ },
890
+ message: failed.length ? "Horizontal bounds overflow exceeded " + maxOverflow + "px in " + failed.length + " viewport(s)." : undefined,
739
891
  });
740
892
  continue;
741
893
  }
@@ -965,14 +1117,55 @@ async function captureViewport(viewport) {
965
1117
  const body = document.body;
966
1118
  const documentElement = document.documentElement;
967
1119
  const text = (body ? body.innerText : "").replace(/\s+/g, " ").trim();
1120
+ const clientWidth = documentElement ? documentElement.clientWidth : window.innerWidth;
1121
+ const scrollWidth = documentElement ? documentElement.scrollWidth : 0;
1122
+ const viewportWidth = clientWidth || window.innerWidth;
1123
+ const overflowOffenders = [];
1124
+ for (const element of Array.from(body ? body.querySelectorAll("*") : [])) {
1125
+ const rect = element.getBoundingClientRect();
1126
+ if (!rect || rect.width < 1 || rect.height < 1) continue;
1127
+ const style = window.getComputedStyle(element);
1128
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") continue;
1129
+ const leftOverflow = Math.max(0, -rect.left);
1130
+ const rightOverflow = Math.max(0, rect.right - viewportWidth);
1131
+ const overflow = Math.max(leftOverflow, rightOverflow);
1132
+ if (overflow <= 0.5) continue;
1133
+ const tag = element.tagName ? element.tagName.toLowerCase() : "element";
1134
+ const id = element.id ? "#" + element.id : "";
1135
+ const className = typeof element.className === "string"
1136
+ ? element.className.trim().split(/\s+/).filter(Boolean).slice(0, 2).join(".")
1137
+ : "";
1138
+ overflowOffenders.push({
1139
+ selector: tag + id + (className ? "." + className : ""),
1140
+ tag,
1141
+ text: (element.textContent || "").replace(/\s+/g, " ").trim().slice(0, 120),
1142
+ overflow,
1143
+ left_overflow_px: leftOverflow,
1144
+ right_overflow_px: rightOverflow,
1145
+ viewport_width: viewportWidth,
1146
+ rect: {
1147
+ left: rect.left,
1148
+ right: rect.right,
1149
+ width: rect.width,
1150
+ },
1151
+ });
1152
+ }
1153
+ overflowOffenders.sort((a, b) => b.overflow - a.overflow);
1154
+ const boundsOverflowPx = Math.max(
1155
+ 0,
1156
+ scrollWidth - (clientWidth || viewportWidth),
1157
+ ...overflowOffenders.map((offender) => offender.overflow),
1158
+ );
968
1159
  return {
969
1160
  url: location.href,
970
1161
  pathname: location.pathname,
971
1162
  title: document.title,
972
1163
  body_text_length: text.length,
973
1164
  body_text_sample: text.slice(0, 8000),
974
- scroll_width: documentElement ? documentElement.scrollWidth : 0,
975
- client_width: documentElement ? documentElement.clientWidth : window.innerWidth,
1165
+ scroll_width: scrollWidth,
1166
+ client_width: clientWidth,
1167
+ bounds_overflow_px: Math.round(boundsOverflowPx * 100) / 100,
1168
+ overflow_offenders: overflowOffenders.slice(0, 10),
976
1169
  };
977
1170
  }).catch((error) => ({
978
1171
  url: page.url(),
@@ -982,6 +1175,8 @@ async function captureViewport(viewport) {
982
1175
  body_text_sample: "",
983
1176
  scroll_width: 0,
984
1177
  client_width: viewport.width,
1178
+ bounds_overflow_px: 0,
1179
+ overflow_offenders: [],
985
1180
  evaluation_error: String(error && error.message ? error.message : error).slice(0, 1000),
986
1181
  }));
987
1182
  const selectors = {};
@@ -1020,6 +1215,8 @@ async function captureViewport(viewport) {
1020
1215
  scroll_width: dom.scroll_width,
1021
1216
  client_width: dom.client_width,
1022
1217
  overflow_px: Math.max(0, (dom.scroll_width || 0) - (dom.client_width || viewport.width)),
1218
+ bounds_overflow_px: dom.bounds_overflow_px,
1219
+ overflow_offenders: dom.overflow_offenders || [],
1023
1220
  selectors,
1024
1221
  text_matches,
1025
1222
  setup_action_results: setupActionResults,
@@ -1051,6 +1248,8 @@ function buildProfileEvidence(currentViewports) {
1051
1248
  routes: currentViewports.map((viewport) => viewport.route),
1052
1249
  titles: currentViewports.map((viewport) => viewport.title),
1053
1250
  overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
1251
+ bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
1252
+ overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
1054
1253
  },
1055
1254
  };
1056
1255
  }
@@ -1074,24 +1273,36 @@ return result;
1074
1273
  }
1075
1274
  function collectRiddleProfileArtifactRefs(input) {
1076
1275
  const refs = [];
1077
- const seen = /* @__PURE__ */ new Set();
1276
+ const indexes = /* @__PURE__ */ new Map();
1277
+ const priorities = /* @__PURE__ */ new Map();
1078
1278
  function add(item, source) {
1079
1279
  if (!isRecord(item)) return;
1080
- const name = stringValue(item.name) || stringValue(item.filename) || "";
1081
1280
  const url = stringValue(item.url);
1082
1281
  const path = stringValue(item.path);
1282
+ const rawName = stringValue(item.name) || stringValue(item.filename) || artifactNameFromPath(url || path) || "";
1283
+ const name = normalizeRiddleProfileArtifactName(rawName);
1083
1284
  if (!name && !url && !path) return;
1084
- const key = `${name}:${url || path || ""}`;
1085
- if (seen.has(key)) return;
1086
- seen.add(key);
1087
- refs.push({
1285
+ const key = profileArtifactDedupeKey(name, url || path || "");
1286
+ const priority = profileArtifactPriority(rawName, name);
1287
+ const ref = {
1088
1288
  name,
1089
1289
  url,
1090
1290
  path,
1091
1291
  kind: stringValue(item.kind) || stringValue(item.type),
1092
1292
  content_type: stringValue(item.content_type) || stringValue(item.contentType),
1093
1293
  source
1094
- });
1294
+ };
1295
+ const existingIndex = indexes.get(key);
1296
+ if (existingIndex !== void 0) {
1297
+ if (priority > (priorities.get(key) || 0)) {
1298
+ refs[existingIndex] = ref;
1299
+ priorities.set(key, priority);
1300
+ }
1301
+ return;
1302
+ }
1303
+ indexes.set(key, refs.length);
1304
+ priorities.set(key, priority);
1305
+ refs.push(ref);
1095
1306
  }
1096
1307
  function visit(value, source) {
1097
1308
  if (Array.isArray(value)) {
@@ -1106,6 +1317,31 @@ function collectRiddleProfileArtifactRefs(input) {
1106
1317
  visit(input, "artifacts");
1107
1318
  return refs;
1108
1319
  }
1320
+ function normalizeRiddleProfileArtifactName(name) {
1321
+ return name.replace(/\.json\.json$/i, ".json");
1322
+ }
1323
+ function profileArtifactDedupeKey(name, location) {
1324
+ if (PROFILE_SINGLETON_ARTIFACT_NAMES.has(name.toLowerCase())) return name.toLowerCase();
1325
+ return `${name}:${location}`;
1326
+ }
1327
+ function profileArtifactPriority(rawName, normalizedName) {
1328
+ const lower = normalizedName.toLowerCase();
1329
+ if (!PROFILE_SINGLETON_ARTIFACT_NAMES.has(lower)) return 1;
1330
+ return /\.json\.json$/i.test(rawName) ? 2 : 1;
1331
+ }
1332
+ function artifactNameFromPath(value) {
1333
+ if (!value) return "";
1334
+ try {
1335
+ return new URL(value).pathname.split("/").filter(Boolean).pop() || "";
1336
+ } catch {
1337
+ return value.split(/[\\/]/).filter(Boolean).pop() || "";
1338
+ }
1339
+ }
1340
+ var PROFILE_SINGLETON_ARTIFACT_NAMES = /* @__PURE__ */ new Set([
1341
+ "proof.json",
1342
+ "console.json",
1343
+ "dom-summary.json"
1344
+ ]);
1109
1345
  function extractRiddleProofProfileResult(input) {
1110
1346
  if (!isRecord(input)) return void 0;
1111
1347
  if (input.version === RIDDLE_PROOF_PROFILE_RESULT_VERSION) return input;