@riddledc/riddle-proof 0.7.59 → 0.7.60

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 CHANGED
@@ -355,6 +355,11 @@ game, or preview surfaces that render inside iframes:
355
355
 
356
356
  ```json
357
357
  [
358
+ {
359
+ "type": "frame_url_matches",
360
+ "selector": ".game-player-root iframe",
361
+ "pattern": "/saved/hot-path-.+/index\\.html$"
362
+ },
358
363
  {
359
364
  "type": "frame_text_visible",
360
365
  "selector": ".game-player-root iframe",
@@ -368,6 +373,11 @@ game, or preview surfaces that render inside iframes:
368
373
  ]
369
374
  ```
370
375
 
376
+ Use `frame_url_equals` when the iframe must resolve to one exact embedded
377
+ resource, or `frame_url_matches` when a preview/job/saved-game URL has a stable
378
+ shape but a generated ID. URL checks fail when the frame is missing, just like
379
+ frame text and overflow checks.
380
+
371
381
  Frame checks capture each matching iframe's URL, title, compact text sample,
372
382
  scroll width, client width, measured horizontal overflow, and top visible
373
383
  overflow offenders. This keeps embedded-player audits in profile mode instead
@@ -22,6 +22,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
22
22
  "selector_count_eq",
23
23
  "selector_text_order",
24
24
  "frame_text_visible",
25
+ "frame_url_equals",
26
+ "frame_url_matches",
25
27
  "frame_no_horizontal_overflow",
26
28
  "text_visible",
27
29
  "text_absent",
@@ -506,12 +508,19 @@ function normalizeCheck(input, index) {
506
508
  if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue(input.selector)) {
507
509
  throw new Error(`checks[${index}] ${type} requires selector.`);
508
510
  }
509
- if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
511
+ if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
510
512
  throw new Error(`checks[${index}] ${type} requires selector.`);
511
513
  }
512
514
  if (type === "frame_text_visible" && !stringValue(input.text) && !stringValue(input.pattern)) {
513
515
  throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
514
516
  }
517
+ const expectedUrl = stringFromOwn(input, "expected_url", "expectedUrl", "url", "expected_value", "expectedValue", "value");
518
+ if (type === "frame_url_equals" && expectedUrl === void 0) {
519
+ throw new Error(`checks[${index}] frame_url_equals requires expected_url.`);
520
+ }
521
+ if (type === "frame_url_matches" && !stringValue(input.pattern)) {
522
+ throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
523
+ }
515
524
  if ((type === "text_visible" || type === "text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
516
525
  throw new Error(`checks[${index}] ${type} requires text or pattern.`);
517
526
  }
@@ -544,6 +553,7 @@ function normalizeCheck(input, index) {
544
553
  expected_path: stringValue(input.expected_path),
545
554
  param: stringValue(input.param) || stringValue(input.search_param) || stringValue(input.searchParam) || stringValue(input.key),
546
555
  expected_value: expectedValue,
556
+ expected_url: expectedUrl,
547
557
  expected_routes: expectedRoutes,
548
558
  selector: stringValue(input.selector),
549
559
  expected_texts: expectedTexts,
@@ -1011,6 +1021,35 @@ function assessCheckFromEvidence(check, evidence) {
1011
1021
  message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
1012
1022
  };
1013
1023
  }
1024
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
1025
+ const key = selectorKey(check);
1026
+ const expectedUrl = check.expected_url || check.expected_value || "";
1027
+ const results = viewports.map((viewport) => {
1028
+ const frames = frameEvidenceForSelector(viewport, key);
1029
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
1030
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : matchText(url, check));
1031
+ return {
1032
+ viewport: viewport.name,
1033
+ frame_count: frames.length,
1034
+ matched_count: matches.length,
1035
+ matched: matches.length > 0,
1036
+ urls: urls.slice(0, 10)
1037
+ };
1038
+ });
1039
+ const failed = results.filter((result) => !result.matched).length;
1040
+ return {
1041
+ type: check.type,
1042
+ label: checkLabel(check),
1043
+ status: failed ? "failed" : "passed",
1044
+ evidence: {
1045
+ selector: key,
1046
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
1047
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
1048
+ viewports: results.map((result) => toJsonValue(result))
1049
+ },
1050
+ message: failed ? `Frame selector ${key} URL assertion failed in ${failed} viewport(s).` : void 0
1051
+ };
1052
+ }
1014
1053
  if (check.type === "frame_no_horizontal_overflow") {
1015
1054
  const key = selectorKey(check);
1016
1055
  const maxOverflow = check.max_overflow_px ?? 4;
@@ -1902,6 +1941,36 @@ function assessProfile(profile, evidence) {
1902
1941
  });
1903
1942
  continue;
1904
1943
  }
1944
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
1945
+ const selector = check.selector || "";
1946
+ const expectedUrl = check.expected_url || check.expected_value || "";
1947
+ const results = checkViewports.map((viewport) => {
1948
+ const frames = frameEvidenceForSelector(viewport, selector);
1949
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
1950
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : textMatches(url, check));
1951
+ return {
1952
+ viewport: viewport.name,
1953
+ frame_count: frames.length,
1954
+ matched_count: matches.length,
1955
+ matched: matches.length > 0,
1956
+ urls: urls.slice(0, 10),
1957
+ };
1958
+ });
1959
+ const failed = results.filter((result) => !result.matched).length;
1960
+ checks.push({
1961
+ type: check.type,
1962
+ label: check.label || check.type,
1963
+ status: failed ? "failed" : "passed",
1964
+ evidence: {
1965
+ selector,
1966
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
1967
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
1968
+ viewports: results,
1969
+ },
1970
+ message: failed ? "Frame selector " + selector + " URL assertion failed in " + failed + " viewport(s)." : undefined,
1971
+ });
1972
+ continue;
1973
+ }
1905
1974
  if (check.type === "frame_no_horizontal_overflow") {
1906
1975
  const selector = check.selector || "";
1907
1976
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
@@ -3373,7 +3442,7 @@ async function captureViewport(viewport) {
3373
3442
  if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
3374
3443
  text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
3375
3444
  }
3376
- if ((check.type === "frame_text_visible" || check.type === "frame_no_horizontal_overflow") && check.selector) {
3445
+ if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
3377
3446
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
3378
3447
  frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
3379
3448
  }
package/dist/cli.cjs CHANGED
@@ -6895,6 +6895,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
6895
6895
  "selector_count_eq",
6896
6896
  "selector_text_order",
6897
6897
  "frame_text_visible",
6898
+ "frame_url_equals",
6899
+ "frame_url_matches",
6898
6900
  "frame_no_horizontal_overflow",
6899
6901
  "text_visible",
6900
6902
  "text_absent",
@@ -7379,12 +7381,19 @@ function normalizeCheck(input, index) {
7379
7381
  if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue2(input.selector)) {
7380
7382
  throw new Error(`checks[${index}] ${type} requires selector.`);
7381
7383
  }
7382
- if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue2(input.selector)) {
7384
+ if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue2(input.selector)) {
7383
7385
  throw new Error(`checks[${index}] ${type} requires selector.`);
7384
7386
  }
7385
7387
  if (type === "frame_text_visible" && !stringValue2(input.text) && !stringValue2(input.pattern)) {
7386
7388
  throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
7387
7389
  }
7390
+ const expectedUrl = stringFromOwn(input, "expected_url", "expectedUrl", "url", "expected_value", "expectedValue", "value");
7391
+ if (type === "frame_url_equals" && expectedUrl === void 0) {
7392
+ throw new Error(`checks[${index}] frame_url_equals requires expected_url.`);
7393
+ }
7394
+ if (type === "frame_url_matches" && !stringValue2(input.pattern)) {
7395
+ throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
7396
+ }
7388
7397
  if ((type === "text_visible" || type === "text_absent") && !stringValue2(input.text) && !stringValue2(input.pattern)) {
7389
7398
  throw new Error(`checks[${index}] ${type} requires text or pattern.`);
7390
7399
  }
@@ -7417,6 +7426,7 @@ function normalizeCheck(input, index) {
7417
7426
  expected_path: stringValue2(input.expected_path),
7418
7427
  param: stringValue2(input.param) || stringValue2(input.search_param) || stringValue2(input.searchParam) || stringValue2(input.key),
7419
7428
  expected_value: expectedValue,
7429
+ expected_url: expectedUrl,
7420
7430
  expected_routes: expectedRoutes,
7421
7431
  selector: stringValue2(input.selector),
7422
7432
  expected_texts: expectedTexts,
@@ -7884,6 +7894,35 @@ function assessCheckFromEvidence(check, evidence) {
7884
7894
  message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
7885
7895
  };
7886
7896
  }
7897
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
7898
+ const key = selectorKey(check);
7899
+ const expectedUrl = check.expected_url || check.expected_value || "";
7900
+ const results = viewports.map((viewport) => {
7901
+ const frames = frameEvidenceForSelector(viewport, key);
7902
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
7903
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : matchText(url, check));
7904
+ return {
7905
+ viewport: viewport.name,
7906
+ frame_count: frames.length,
7907
+ matched_count: matches.length,
7908
+ matched: matches.length > 0,
7909
+ urls: urls.slice(0, 10)
7910
+ };
7911
+ });
7912
+ const failed = results.filter((result) => !result.matched).length;
7913
+ return {
7914
+ type: check.type,
7915
+ label: checkLabel(check),
7916
+ status: failed ? "failed" : "passed",
7917
+ evidence: {
7918
+ selector: key,
7919
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
7920
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
7921
+ viewports: results.map((result) => toJsonValue(result))
7922
+ },
7923
+ message: failed ? `Frame selector ${key} URL assertion failed in ${failed} viewport(s).` : void 0
7924
+ };
7925
+ }
7887
7926
  if (check.type === "frame_no_horizontal_overflow") {
7888
7927
  const key = selectorKey(check);
7889
7928
  const maxOverflow = check.max_overflow_px ?? 4;
@@ -8759,6 +8798,36 @@ function assessProfile(profile, evidence) {
8759
8798
  });
8760
8799
  continue;
8761
8800
  }
8801
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
8802
+ const selector = check.selector || "";
8803
+ const expectedUrl = check.expected_url || check.expected_value || "";
8804
+ const results = checkViewports.map((viewport) => {
8805
+ const frames = frameEvidenceForSelector(viewport, selector);
8806
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
8807
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : textMatches(url, check));
8808
+ return {
8809
+ viewport: viewport.name,
8810
+ frame_count: frames.length,
8811
+ matched_count: matches.length,
8812
+ matched: matches.length > 0,
8813
+ urls: urls.slice(0, 10),
8814
+ };
8815
+ });
8816
+ const failed = results.filter((result) => !result.matched).length;
8817
+ checks.push({
8818
+ type: check.type,
8819
+ label: check.label || check.type,
8820
+ status: failed ? "failed" : "passed",
8821
+ evidence: {
8822
+ selector,
8823
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
8824
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
8825
+ viewports: results,
8826
+ },
8827
+ message: failed ? "Frame selector " + selector + " URL assertion failed in " + failed + " viewport(s)." : undefined,
8828
+ });
8829
+ continue;
8830
+ }
8762
8831
  if (check.type === "frame_no_horizontal_overflow") {
8763
8832
  const selector = check.selector || "";
8764
8833
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
@@ -10230,7 +10299,7 @@ async function captureViewport(viewport) {
10230
10299
  if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
10231
10300
  text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
10232
10301
  }
10233
- if ((check.type === "frame_text_visible" || check.type === "frame_no_horizontal_overflow") && check.selector) {
10302
+ if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
10234
10303
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
10235
10304
  frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
10236
10305
  }
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-Q5VW6MAP.js";
13
+ } from "./chunk-FLQ4HHTT.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
package/dist/index.cjs CHANGED
@@ -8736,6 +8736,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
8736
8736
  "selector_count_eq",
8737
8737
  "selector_text_order",
8738
8738
  "frame_text_visible",
8739
+ "frame_url_equals",
8740
+ "frame_url_matches",
8739
8741
  "frame_no_horizontal_overflow",
8740
8742
  "text_visible",
8741
8743
  "text_absent",
@@ -9220,12 +9222,19 @@ function normalizeCheck(input, index) {
9220
9222
  if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue5(input.selector)) {
9221
9223
  throw new Error(`checks[${index}] ${type} requires selector.`);
9222
9224
  }
9223
- if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue5(input.selector)) {
9225
+ if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue5(input.selector)) {
9224
9226
  throw new Error(`checks[${index}] ${type} requires selector.`);
9225
9227
  }
9226
9228
  if (type === "frame_text_visible" && !stringValue5(input.text) && !stringValue5(input.pattern)) {
9227
9229
  throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
9228
9230
  }
9231
+ const expectedUrl = stringFromOwn(input, "expected_url", "expectedUrl", "url", "expected_value", "expectedValue", "value");
9232
+ if (type === "frame_url_equals" && expectedUrl === void 0) {
9233
+ throw new Error(`checks[${index}] frame_url_equals requires expected_url.`);
9234
+ }
9235
+ if (type === "frame_url_matches" && !stringValue5(input.pattern)) {
9236
+ throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
9237
+ }
9229
9238
  if ((type === "text_visible" || type === "text_absent") && !stringValue5(input.text) && !stringValue5(input.pattern)) {
9230
9239
  throw new Error(`checks[${index}] ${type} requires text or pattern.`);
9231
9240
  }
@@ -9258,6 +9267,7 @@ function normalizeCheck(input, index) {
9258
9267
  expected_path: stringValue5(input.expected_path),
9259
9268
  param: stringValue5(input.param) || stringValue5(input.search_param) || stringValue5(input.searchParam) || stringValue5(input.key),
9260
9269
  expected_value: expectedValue,
9270
+ expected_url: expectedUrl,
9261
9271
  expected_routes: expectedRoutes,
9262
9272
  selector: stringValue5(input.selector),
9263
9273
  expected_texts: expectedTexts,
@@ -9725,6 +9735,35 @@ function assessCheckFromEvidence(check, evidence) {
9725
9735
  message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
9726
9736
  };
9727
9737
  }
9738
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
9739
+ const key = selectorKey(check);
9740
+ const expectedUrl = check.expected_url || check.expected_value || "";
9741
+ const results = viewports.map((viewport) => {
9742
+ const frames = frameEvidenceForSelector(viewport, key);
9743
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
9744
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : matchText(url, check));
9745
+ return {
9746
+ viewport: viewport.name,
9747
+ frame_count: frames.length,
9748
+ matched_count: matches.length,
9749
+ matched: matches.length > 0,
9750
+ urls: urls.slice(0, 10)
9751
+ };
9752
+ });
9753
+ const failed = results.filter((result) => !result.matched).length;
9754
+ return {
9755
+ type: check.type,
9756
+ label: checkLabel(check),
9757
+ status: failed ? "failed" : "passed",
9758
+ evidence: {
9759
+ selector: key,
9760
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
9761
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
9762
+ viewports: results.map((result) => toJsonValue(result))
9763
+ },
9764
+ message: failed ? `Frame selector ${key} URL assertion failed in ${failed} viewport(s).` : void 0
9765
+ };
9766
+ }
9728
9767
  if (check.type === "frame_no_horizontal_overflow") {
9729
9768
  const key = selectorKey(check);
9730
9769
  const maxOverflow = check.max_overflow_px ?? 4;
@@ -10616,6 +10655,36 @@ function assessProfile(profile, evidence) {
10616
10655
  });
10617
10656
  continue;
10618
10657
  }
10658
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
10659
+ const selector = check.selector || "";
10660
+ const expectedUrl = check.expected_url || check.expected_value || "";
10661
+ const results = checkViewports.map((viewport) => {
10662
+ const frames = frameEvidenceForSelector(viewport, selector);
10663
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
10664
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : textMatches(url, check));
10665
+ return {
10666
+ viewport: viewport.name,
10667
+ frame_count: frames.length,
10668
+ matched_count: matches.length,
10669
+ matched: matches.length > 0,
10670
+ urls: urls.slice(0, 10),
10671
+ };
10672
+ });
10673
+ const failed = results.filter((result) => !result.matched).length;
10674
+ checks.push({
10675
+ type: check.type,
10676
+ label: check.label || check.type,
10677
+ status: failed ? "failed" : "passed",
10678
+ evidence: {
10679
+ selector,
10680
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
10681
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
10682
+ viewports: results,
10683
+ },
10684
+ message: failed ? "Frame selector " + selector + " URL assertion failed in " + failed + " viewport(s)." : undefined,
10685
+ });
10686
+ continue;
10687
+ }
10619
10688
  if (check.type === "frame_no_horizontal_overflow") {
10620
10689
  const selector = check.selector || "";
10621
10690
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
@@ -12087,7 +12156,7 @@ async function captureViewport(viewport) {
12087
12156
  if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
12088
12157
  text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
12089
12158
  }
12090
- if ((check.type === "frame_text_visible" || check.type === "frame_no_horizontal_overflow") && check.selector) {
12159
+ if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
12091
12160
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
12092
12161
  frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
12093
12162
  }
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-Q5VW6MAP.js";
61
+ } from "./chunk-FLQ4HHTT.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -65,6 +65,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
65
65
  "selector_count_eq",
66
66
  "selector_text_order",
67
67
  "frame_text_visible",
68
+ "frame_url_equals",
69
+ "frame_url_matches",
68
70
  "frame_no_horizontal_overflow",
69
71
  "text_visible",
70
72
  "text_absent",
@@ -549,12 +551,19 @@ function normalizeCheck(input, index) {
549
551
  if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue(input.selector)) {
550
552
  throw new Error(`checks[${index}] ${type} requires selector.`);
551
553
  }
552
- if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
554
+ if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
553
555
  throw new Error(`checks[${index}] ${type} requires selector.`);
554
556
  }
555
557
  if (type === "frame_text_visible" && !stringValue(input.text) && !stringValue(input.pattern)) {
556
558
  throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
557
559
  }
560
+ const expectedUrl = stringFromOwn(input, "expected_url", "expectedUrl", "url", "expected_value", "expectedValue", "value");
561
+ if (type === "frame_url_equals" && expectedUrl === void 0) {
562
+ throw new Error(`checks[${index}] frame_url_equals requires expected_url.`);
563
+ }
564
+ if (type === "frame_url_matches" && !stringValue(input.pattern)) {
565
+ throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
566
+ }
558
567
  if ((type === "text_visible" || type === "text_absent") && !stringValue(input.text) && !stringValue(input.pattern)) {
559
568
  throw new Error(`checks[${index}] ${type} requires text or pattern.`);
560
569
  }
@@ -587,6 +596,7 @@ function normalizeCheck(input, index) {
587
596
  expected_path: stringValue(input.expected_path),
588
597
  param: stringValue(input.param) || stringValue(input.search_param) || stringValue(input.searchParam) || stringValue(input.key),
589
598
  expected_value: expectedValue,
599
+ expected_url: expectedUrl,
590
600
  expected_routes: expectedRoutes,
591
601
  selector: stringValue(input.selector),
592
602
  expected_texts: expectedTexts,
@@ -1054,6 +1064,35 @@ function assessCheckFromEvidence(check, evidence) {
1054
1064
  message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
1055
1065
  };
1056
1066
  }
1067
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
1068
+ const key = selectorKey(check);
1069
+ const expectedUrl = check.expected_url || check.expected_value || "";
1070
+ const results = viewports.map((viewport) => {
1071
+ const frames = frameEvidenceForSelector(viewport, key);
1072
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
1073
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : matchText(url, check));
1074
+ return {
1075
+ viewport: viewport.name,
1076
+ frame_count: frames.length,
1077
+ matched_count: matches.length,
1078
+ matched: matches.length > 0,
1079
+ urls: urls.slice(0, 10)
1080
+ };
1081
+ });
1082
+ const failed = results.filter((result) => !result.matched).length;
1083
+ return {
1084
+ type: check.type,
1085
+ label: checkLabel(check),
1086
+ status: failed ? "failed" : "passed",
1087
+ evidence: {
1088
+ selector: key,
1089
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
1090
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
1091
+ viewports: results.map((result) => toJsonValue(result))
1092
+ },
1093
+ message: failed ? `Frame selector ${key} URL assertion failed in ${failed} viewport(s).` : void 0
1094
+ };
1095
+ }
1057
1096
  if (check.type === "frame_no_horizontal_overflow") {
1058
1097
  const key = selectorKey(check);
1059
1098
  const maxOverflow = check.max_overflow_px ?? 4;
@@ -1945,6 +1984,36 @@ function assessProfile(profile, evidence) {
1945
1984
  });
1946
1985
  continue;
1947
1986
  }
1987
+ if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
1988
+ const selector = check.selector || "";
1989
+ const expectedUrl = check.expected_url || check.expected_value || "";
1990
+ const results = checkViewports.map((viewport) => {
1991
+ const frames = frameEvidenceForSelector(viewport, selector);
1992
+ const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
1993
+ const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : textMatches(url, check));
1994
+ return {
1995
+ viewport: viewport.name,
1996
+ frame_count: frames.length,
1997
+ matched_count: matches.length,
1998
+ matched: matches.length > 0,
1999
+ urls: urls.slice(0, 10),
2000
+ };
2001
+ });
2002
+ const failed = results.filter((result) => !result.matched).length;
2003
+ checks.push({
2004
+ type: check.type,
2005
+ label: check.label || check.type,
2006
+ status: failed ? "failed" : "passed",
2007
+ evidence: {
2008
+ selector,
2009
+ expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
2010
+ pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
2011
+ viewports: results,
2012
+ },
2013
+ message: failed ? "Frame selector " + selector + " URL assertion failed in " + failed + " viewport(s)." : undefined,
2014
+ });
2015
+ continue;
2016
+ }
1948
2017
  if (check.type === "frame_no_horizontal_overflow") {
1949
2018
  const selector = check.selector || "";
1950
2019
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
@@ -3416,7 +3485,7 @@ async function captureViewport(viewport) {
3416
3485
  if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
3417
3486
  text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
3418
3487
  }
3419
- if ((check.type === "frame_text_visible" || check.type === "frame_no_horizontal_overflow") && check.selector) {
3488
+ if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
3420
3489
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
3421
3490
  frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
3422
3491
  }
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
4
4
  declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
5
5
  declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
6
6
  declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
7
- declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
7
+ declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
8
8
  declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text", "window_call"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
@@ -94,6 +94,7 @@ interface RiddleProofProfileCheck {
94
94
  expected_path?: string;
95
95
  param?: string;
96
96
  expected_value?: string;
97
+ expected_url?: string;
97
98
  expected_routes?: RiddleProofProfileRouteInventoryRoute[];
98
99
  selector?: string;
99
100
  expected_texts?: string[];
package/dist/profile.d.ts CHANGED
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
4
4
  declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
5
5
  declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
6
6
  declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
7
- declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
7
+ declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
8
8
  declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text", "window_call"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
@@ -94,6 +94,7 @@ interface RiddleProofProfileCheck {
94
94
  expected_path?: string;
95
95
  param?: string;
96
96
  expected_value?: string;
97
+ expected_url?: string;
97
98
  expected_routes?: RiddleProofProfileRouteInventoryRoute[];
98
99
  selector?: string;
99
100
  expected_texts?: string[];
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-Q5VW6MAP.js";
22
+ } from "./chunk-FLQ4HHTT.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
295
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
385
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
662
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
295
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
385
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
662
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.59",
3
+ "version": "0.7.60",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",