@riddledc/riddle-proof 0.7.40 → 0.7.42

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
@@ -145,6 +145,8 @@ or as a stronger proof base before a change loop.
145
145
  "checks": [
146
146
  { "type": "route_loaded", "expected_path": "/pricing" },
147
147
  { "type": "selector_visible", "selector": "[data-testid='pricing-cards']" },
148
+ { "type": "selector_absent", "selector": "[data-testid='loading-spinner']" },
149
+ { "type": "selector_count_equals", "selector": "[data-testid='pricing-card']", "expected_count": 3 },
148
150
  { "type": "text_visible", "text": "Start building" },
149
151
  { "type": "no_mobile_horizontal_overflow" },
150
152
  { "type": "no_fatal_console_errors" }
@@ -210,7 +212,10 @@ When `responses` is present, `network_mocks_succeeded` requires each configured
210
212
  response to be hit at least once by default and records `hit_index`,
211
213
  `response_index`, and `response_label` for each request. Set
212
214
  `required_hit_count` / `min_hits` or `required: false` when a different
213
- contract is intentional.
215
+ contract is intentional. Set `repeat_responses: true` when the sequence should
216
+ cycle instead of reusing the final response, for example to repeat a fail-then-
217
+ success pair across multiple viewports. Repeated sequences also record
218
+ `sequence_cycle: true` after the first cycle.
214
219
 
215
220
  `target.setup_actions` is optional. Use it when the meaningful proof surface
216
221
  appears only after a picker, tab, login stub, storage seed, form fill,
@@ -249,6 +254,21 @@ only unallowed `error` / `assert` console events and page errors fail the check.
249
254
  Use `allowed_page_error_patterns`, `allowed_console_texts`, or
250
255
  `allowed_page_error_texts` for narrower matching when needed.
251
256
 
257
+ Use `selector_absent` when a forbidden element must not render, and
258
+ `selector_count_equals` / `selector_count_equal` / `selector_count_eq` when a
259
+ profile needs an exact DOM count rather than a lower bound:
260
+
261
+ ```json
262
+ [
263
+ { "type": "selector_absent", "selector": ".game-player-root iframe" },
264
+ { "type": "selector_count_equals", "selector": ".pricing-card", "expected_count": 3 }
265
+ ]
266
+ ```
267
+
268
+ These checks are useful for audit/no-diff profiles where the product should
269
+ show a fallback state and avoid rendering stale loaders, duplicate rows, or
270
+ missing-resource iframes.
271
+
252
272
  Use `selector_text_order` when a table, list, or card group must show visible
253
273
  items in a specific order after setup actions such as sorting or filtering:
254
274
 
@@ -13,7 +13,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
13
13
  var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
14
14
  "route_loaded",
15
15
  "selector_visible",
16
+ "selector_absent",
16
17
  "selector_count_at_least",
18
+ "selector_count_equals",
19
+ "selector_count_equal",
20
+ "selector_count_eq",
17
21
  "selector_text_order",
18
22
  "frame_text_visible",
19
23
  "frame_no_horizontal_overflow",
@@ -261,6 +265,7 @@ function normalizeNetworkMock(input, index) {
261
265
  url,
262
266
  method: stringValue(input.method)?.toUpperCase(),
263
267
  responses,
268
+ repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
264
269
  required_hit_count: requiredHitCount,
265
270
  required: input.required === false ? false : true
266
271
  };
@@ -352,7 +357,7 @@ function normalizeCheck(input, index) {
352
357
  if (!isSupportedCheckType(type)) {
353
358
  throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
354
359
  }
355
- if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue(input.selector)) {
360
+ 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)) {
356
361
  throw new Error(`checks[${index}] ${type} requires selector.`);
357
362
  }
358
363
  if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
@@ -367,6 +372,10 @@ function normalizeCheck(input, index) {
367
372
  if (type === "selector_count_at_least" && numberValue(input.min_count) === void 0) {
368
373
  throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
369
374
  }
375
+ const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
376
+ if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
377
+ throw new Error(`checks[${index}] ${type} requires expected_count.`);
378
+ }
370
379
  const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
371
380
  if (type === "selector_text_order") {
372
381
  if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
@@ -398,6 +407,7 @@ function normalizeCheck(input, index) {
398
407
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
399
408
  allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
400
409
  min_count: numberValue(input.min_count),
410
+ expected_count: expectedCount,
401
411
  max_overflow_px: numberValue(input.max_overflow_px),
402
412
  timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
403
413
  run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
@@ -665,6 +675,20 @@ function assessCheckFromEvidence(check, evidence) {
665
675
  message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
666
676
  };
667
677
  }
678
+ if (check.type === "selector_absent") {
679
+ const key = selectorKey(check);
680
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
681
+ return {
682
+ type: check.type,
683
+ label: checkLabel(check),
684
+ status: failed.length ? "failed" : "passed",
685
+ evidence: {
686
+ selector: key,
687
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
688
+ },
689
+ message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
690
+ };
691
+ }
668
692
  if (check.type === "selector_count_at_least") {
669
693
  const key = selectorKey(check);
670
694
  const minCount = check.min_count ?? 1;
@@ -681,6 +705,22 @@ function assessCheckFromEvidence(check, evidence) {
681
705
  message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
682
706
  };
683
707
  }
708
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
709
+ const key = selectorKey(check);
710
+ const expectedCount = check.expected_count ?? 0;
711
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
712
+ return {
713
+ type: check.type,
714
+ label: checkLabel(check),
715
+ status: failed.length ? "failed" : "passed",
716
+ evidence: {
717
+ selector: key,
718
+ expected_count: expectedCount,
719
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
720
+ },
721
+ message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
722
+ };
723
+ }
684
724
  if (check.type === "selector_text_order") {
685
725
  const key = selectorKey(check);
686
726
  const expectedTexts = check.expected_texts || [];
@@ -1417,6 +1457,18 @@ function assessProfile(profile, evidence) {
1417
1457
  });
1418
1458
  continue;
1419
1459
  }
1460
+ if (check.type === "selector_absent") {
1461
+ const selector = check.selector || "";
1462
+ const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
1463
+ checks.push({
1464
+ type: check.type,
1465
+ label: check.label || check.type,
1466
+ status: failed.length ? "failed" : "passed",
1467
+ evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1468
+ message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
1469
+ });
1470
+ continue;
1471
+ }
1420
1472
  if (check.type === "selector_count_at_least") {
1421
1473
  const selector = check.selector || "";
1422
1474
  const minCount = check.min_count == null ? 1 : check.min_count;
@@ -1430,6 +1482,19 @@ function assessProfile(profile, evidence) {
1430
1482
  });
1431
1483
  continue;
1432
1484
  }
1485
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
1486
+ const selector = check.selector || "";
1487
+ const expectedCount = check.expected_count == null ? 0 : check.expected_count;
1488
+ const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
1489
+ checks.push({
1490
+ type: check.type,
1491
+ label: check.label || check.type,
1492
+ status: failed.length ? "failed" : "passed",
1493
+ evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1494
+ message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
1495
+ });
1496
+ continue;
1497
+ }
1433
1498
  if (check.type === "selector_text_order") {
1434
1499
  const selector = check.selector || "";
1435
1500
  const expectedTexts = check.expected_texts || [];
@@ -1748,7 +1813,9 @@ async function registerNetworkMocks(mocks) {
1748
1813
  const responses = Array.isArray(mock.responses) ? mock.responses : [];
1749
1814
  const hitIndex = hitCount;
1750
1815
  hitCount += 1;
1751
- const responseIndex = responses.length ? Math.min(hitIndex, responses.length - 1) : null;
1816
+ const responseIndex = responses.length
1817
+ ? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
1818
+ : null;
1752
1819
  const response = responseIndex === null ? mock : responses[responseIndex];
1753
1820
  const headers = { ...(response.headers || mock.headers || {}) };
1754
1821
  let body = response.body || "";
@@ -1763,7 +1830,8 @@ async function registerNetworkMocks(mocks) {
1763
1830
  response_label: response.label || null,
1764
1831
  hit_index: hitIndex,
1765
1832
  response_index: responseIndex,
1766
- sequence_reused: responseIndex !== null && hitIndex >= responses.length,
1833
+ sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
1834
+ sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
1767
1835
  url: request.url(),
1768
1836
  method,
1769
1837
  status: response.status || mock.status || 200,
@@ -2443,7 +2511,16 @@ async function captureViewport(viewport) {
2443
2511
  const text_sequences = {};
2444
2512
  const text_matches = {};
2445
2513
  for (const check of profile.checks || []) {
2446
- if ((check.type === "selector_visible" || check.type === "selector_count_at_least") && check.selector) {
2514
+ if (
2515
+ (
2516
+ check.type === "selector_visible"
2517
+ || check.type === "selector_absent"
2518
+ || check.type === "selector_count_at_least"
2519
+ || check.type === "selector_count_equals"
2520
+ || check.type === "selector_count_equal"
2521
+ || check.type === "selector_count_eq"
2522
+ ) && check.selector
2523
+ ) {
2447
2524
  selectors[check.selector] = await selectorStats(check.selector);
2448
2525
  }
2449
2526
  if (check.type === "selector_text_order" && check.selector) {
package/dist/cli.cjs CHANGED
@@ -6886,7 +6886,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
6886
6886
  var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
6887
6887
  "route_loaded",
6888
6888
  "selector_visible",
6889
+ "selector_absent",
6889
6890
  "selector_count_at_least",
6891
+ "selector_count_equals",
6892
+ "selector_count_equal",
6893
+ "selector_count_eq",
6890
6894
  "selector_text_order",
6891
6895
  "frame_text_visible",
6892
6896
  "frame_no_horizontal_overflow",
@@ -7134,6 +7138,7 @@ function normalizeNetworkMock(input, index) {
7134
7138
  url,
7135
7139
  method: stringValue2(input.method)?.toUpperCase(),
7136
7140
  responses,
7141
+ repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
7137
7142
  required_hit_count: requiredHitCount,
7138
7143
  required: input.required === false ? false : true
7139
7144
  };
@@ -7225,7 +7230,7 @@ function normalizeCheck(input, index) {
7225
7230
  if (!isSupportedCheckType(type)) {
7226
7231
  throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
7227
7232
  }
7228
- if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue2(input.selector)) {
7233
+ 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)) {
7229
7234
  throw new Error(`checks[${index}] ${type} requires selector.`);
7230
7235
  }
7231
7236
  if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue2(input.selector)) {
@@ -7240,6 +7245,10 @@ function normalizeCheck(input, index) {
7240
7245
  if (type === "selector_count_at_least" && numberValue(input.min_count) === void 0) {
7241
7246
  throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
7242
7247
  }
7248
+ const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
7249
+ if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
7250
+ throw new Error(`checks[${index}] ${type} requires expected_count.`);
7251
+ }
7243
7252
  const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
7244
7253
  if (type === "selector_text_order") {
7245
7254
  if (!stringValue2(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
@@ -7271,6 +7280,7 @@ function normalizeCheck(input, index) {
7271
7280
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
7272
7281
  allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
7273
7282
  min_count: numberValue(input.min_count),
7283
+ expected_count: expectedCount,
7274
7284
  max_overflow_px: numberValue(input.max_overflow_px),
7275
7285
  timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
7276
7286
  run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
@@ -7538,6 +7548,20 @@ function assessCheckFromEvidence(check, evidence) {
7538
7548
  message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
7539
7549
  };
7540
7550
  }
7551
+ if (check.type === "selector_absent") {
7552
+ const key = selectorKey(check);
7553
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
7554
+ return {
7555
+ type: check.type,
7556
+ label: checkLabel(check),
7557
+ status: failed.length ? "failed" : "passed",
7558
+ evidence: {
7559
+ selector: key,
7560
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
7561
+ },
7562
+ message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
7563
+ };
7564
+ }
7541
7565
  if (check.type === "selector_count_at_least") {
7542
7566
  const key = selectorKey(check);
7543
7567
  const minCount = check.min_count ?? 1;
@@ -7554,6 +7578,22 @@ function assessCheckFromEvidence(check, evidence) {
7554
7578
  message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
7555
7579
  };
7556
7580
  }
7581
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
7582
+ const key = selectorKey(check);
7583
+ const expectedCount = check.expected_count ?? 0;
7584
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
7585
+ return {
7586
+ type: check.type,
7587
+ label: checkLabel(check),
7588
+ status: failed.length ? "failed" : "passed",
7589
+ evidence: {
7590
+ selector: key,
7591
+ expected_count: expectedCount,
7592
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
7593
+ },
7594
+ message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
7595
+ };
7596
+ }
7557
7597
  if (check.type === "selector_text_order") {
7558
7598
  const key = selectorKey(check);
7559
7599
  const expectedTexts = check.expected_texts || [];
@@ -8274,6 +8314,18 @@ function assessProfile(profile, evidence) {
8274
8314
  });
8275
8315
  continue;
8276
8316
  }
8317
+ if (check.type === "selector_absent") {
8318
+ const selector = check.selector || "";
8319
+ const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
8320
+ checks.push({
8321
+ type: check.type,
8322
+ label: check.label || check.type,
8323
+ status: failed.length ? "failed" : "passed",
8324
+ evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8325
+ message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
8326
+ });
8327
+ continue;
8328
+ }
8277
8329
  if (check.type === "selector_count_at_least") {
8278
8330
  const selector = check.selector || "";
8279
8331
  const minCount = check.min_count == null ? 1 : check.min_count;
@@ -8287,6 +8339,19 @@ function assessProfile(profile, evidence) {
8287
8339
  });
8288
8340
  continue;
8289
8341
  }
8342
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
8343
+ const selector = check.selector || "";
8344
+ const expectedCount = check.expected_count == null ? 0 : check.expected_count;
8345
+ const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
8346
+ checks.push({
8347
+ type: check.type,
8348
+ label: check.label || check.type,
8349
+ status: failed.length ? "failed" : "passed",
8350
+ evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8351
+ message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
8352
+ });
8353
+ continue;
8354
+ }
8290
8355
  if (check.type === "selector_text_order") {
8291
8356
  const selector = check.selector || "";
8292
8357
  const expectedTexts = check.expected_texts || [];
@@ -8605,7 +8670,9 @@ async function registerNetworkMocks(mocks) {
8605
8670
  const responses = Array.isArray(mock.responses) ? mock.responses : [];
8606
8671
  const hitIndex = hitCount;
8607
8672
  hitCount += 1;
8608
- const responseIndex = responses.length ? Math.min(hitIndex, responses.length - 1) : null;
8673
+ const responseIndex = responses.length
8674
+ ? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
8675
+ : null;
8609
8676
  const response = responseIndex === null ? mock : responses[responseIndex];
8610
8677
  const headers = { ...(response.headers || mock.headers || {}) };
8611
8678
  let body = response.body || "";
@@ -8620,7 +8687,8 @@ async function registerNetworkMocks(mocks) {
8620
8687
  response_label: response.label || null,
8621
8688
  hit_index: hitIndex,
8622
8689
  response_index: responseIndex,
8623
- sequence_reused: responseIndex !== null && hitIndex >= responses.length,
8690
+ sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
8691
+ sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
8624
8692
  url: request.url(),
8625
8693
  method,
8626
8694
  status: response.status || mock.status || 200,
@@ -9300,7 +9368,16 @@ async function captureViewport(viewport) {
9300
9368
  const text_sequences = {};
9301
9369
  const text_matches = {};
9302
9370
  for (const check of profile.checks || []) {
9303
- if ((check.type === "selector_visible" || check.type === "selector_count_at_least") && check.selector) {
9371
+ if (
9372
+ (
9373
+ check.type === "selector_visible"
9374
+ || check.type === "selector_absent"
9375
+ || check.type === "selector_count_at_least"
9376
+ || check.type === "selector_count_equals"
9377
+ || check.type === "selector_count_equal"
9378
+ || check.type === "selector_count_eq"
9379
+ ) && check.selector
9380
+ ) {
9304
9381
  selectors[check.selector] = await selectorStats(check.selector);
9305
9382
  }
9306
9383
  if (check.type === "selector_text_order" && check.selector) {
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-RAVOICNN.js";
13
+ } from "./chunk-MT2OZCCP.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
package/dist/index.cjs CHANGED
@@ -8727,7 +8727,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
8727
8727
  var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
8728
8728
  "route_loaded",
8729
8729
  "selector_visible",
8730
+ "selector_absent",
8730
8731
  "selector_count_at_least",
8732
+ "selector_count_equals",
8733
+ "selector_count_equal",
8734
+ "selector_count_eq",
8731
8735
  "selector_text_order",
8732
8736
  "frame_text_visible",
8733
8737
  "frame_no_horizontal_overflow",
@@ -8975,6 +8979,7 @@ function normalizeNetworkMock(input, index) {
8975
8979
  url,
8976
8980
  method: stringValue5(input.method)?.toUpperCase(),
8977
8981
  responses,
8982
+ repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
8978
8983
  required_hit_count: requiredHitCount,
8979
8984
  required: input.required === false ? false : true
8980
8985
  };
@@ -9066,7 +9071,7 @@ function normalizeCheck(input, index) {
9066
9071
  if (!isSupportedCheckType(type)) {
9067
9072
  throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
9068
9073
  }
9069
- if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue5(input.selector)) {
9074
+ 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)) {
9070
9075
  throw new Error(`checks[${index}] ${type} requires selector.`);
9071
9076
  }
9072
9077
  if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue5(input.selector)) {
@@ -9081,6 +9086,10 @@ function normalizeCheck(input, index) {
9081
9086
  if (type === "selector_count_at_least" && numberValue3(input.min_count) === void 0) {
9082
9087
  throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
9083
9088
  }
9089
+ const expectedCount = numberValue3(input.expected_count) ?? numberValue3(input.expectedCount) ?? numberValue3(input.count);
9090
+ if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
9091
+ throw new Error(`checks[${index}] ${type} requires expected_count.`);
9092
+ }
9084
9093
  const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
9085
9094
  if (type === "selector_text_order") {
9086
9095
  if (!stringValue5(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
@@ -9112,6 +9121,7 @@ function normalizeCheck(input, index) {
9112
9121
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
9113
9122
  allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
9114
9123
  min_count: numberValue3(input.min_count),
9124
+ expected_count: expectedCount,
9115
9125
  max_overflow_px: numberValue3(input.max_overflow_px),
9116
9126
  timeout_ms: numberValue3(input.timeout_ms) ?? numberValue3(input.timeoutMs),
9117
9127
  run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
@@ -9379,6 +9389,20 @@ function assessCheckFromEvidence(check, evidence) {
9379
9389
  message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
9380
9390
  };
9381
9391
  }
9392
+ if (check.type === "selector_absent") {
9393
+ const key = selectorKey(check);
9394
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
9395
+ return {
9396
+ type: check.type,
9397
+ label: checkLabel(check),
9398
+ status: failed.length ? "failed" : "passed",
9399
+ evidence: {
9400
+ selector: key,
9401
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
9402
+ },
9403
+ message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
9404
+ };
9405
+ }
9382
9406
  if (check.type === "selector_count_at_least") {
9383
9407
  const key = selectorKey(check);
9384
9408
  const minCount = check.min_count ?? 1;
@@ -9395,6 +9419,22 @@ function assessCheckFromEvidence(check, evidence) {
9395
9419
  message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
9396
9420
  };
9397
9421
  }
9422
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
9423
+ const key = selectorKey(check);
9424
+ const expectedCount = check.expected_count ?? 0;
9425
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
9426
+ return {
9427
+ type: check.type,
9428
+ label: checkLabel(check),
9429
+ status: failed.length ? "failed" : "passed",
9430
+ evidence: {
9431
+ selector: key,
9432
+ expected_count: expectedCount,
9433
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
9434
+ },
9435
+ message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
9436
+ };
9437
+ }
9398
9438
  if (check.type === "selector_text_order") {
9399
9439
  const key = selectorKey(check);
9400
9440
  const expectedTexts = check.expected_texts || [];
@@ -10131,6 +10171,18 @@ function assessProfile(profile, evidence) {
10131
10171
  });
10132
10172
  continue;
10133
10173
  }
10174
+ if (check.type === "selector_absent") {
10175
+ const selector = check.selector || "";
10176
+ const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
10177
+ checks.push({
10178
+ type: check.type,
10179
+ label: check.label || check.type,
10180
+ status: failed.length ? "failed" : "passed",
10181
+ evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10182
+ message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
10183
+ });
10184
+ continue;
10185
+ }
10134
10186
  if (check.type === "selector_count_at_least") {
10135
10187
  const selector = check.selector || "";
10136
10188
  const minCount = check.min_count == null ? 1 : check.min_count;
@@ -10144,6 +10196,19 @@ function assessProfile(profile, evidence) {
10144
10196
  });
10145
10197
  continue;
10146
10198
  }
10199
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
10200
+ const selector = check.selector || "";
10201
+ const expectedCount = check.expected_count == null ? 0 : check.expected_count;
10202
+ const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
10203
+ checks.push({
10204
+ type: check.type,
10205
+ label: check.label || check.type,
10206
+ status: failed.length ? "failed" : "passed",
10207
+ evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10208
+ message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
10209
+ });
10210
+ continue;
10211
+ }
10147
10212
  if (check.type === "selector_text_order") {
10148
10213
  const selector = check.selector || "";
10149
10214
  const expectedTexts = check.expected_texts || [];
@@ -10462,7 +10527,9 @@ async function registerNetworkMocks(mocks) {
10462
10527
  const responses = Array.isArray(mock.responses) ? mock.responses : [];
10463
10528
  const hitIndex = hitCount;
10464
10529
  hitCount += 1;
10465
- const responseIndex = responses.length ? Math.min(hitIndex, responses.length - 1) : null;
10530
+ const responseIndex = responses.length
10531
+ ? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
10532
+ : null;
10466
10533
  const response = responseIndex === null ? mock : responses[responseIndex];
10467
10534
  const headers = { ...(response.headers || mock.headers || {}) };
10468
10535
  let body = response.body || "";
@@ -10477,7 +10544,8 @@ async function registerNetworkMocks(mocks) {
10477
10544
  response_label: response.label || null,
10478
10545
  hit_index: hitIndex,
10479
10546
  response_index: responseIndex,
10480
- sequence_reused: responseIndex !== null && hitIndex >= responses.length,
10547
+ sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
10548
+ sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
10481
10549
  url: request.url(),
10482
10550
  method,
10483
10551
  status: response.status || mock.status || 200,
@@ -11157,7 +11225,16 @@ async function captureViewport(viewport) {
11157
11225
  const text_sequences = {};
11158
11226
  const text_matches = {};
11159
11227
  for (const check of profile.checks || []) {
11160
- if ((check.type === "selector_visible" || check.type === "selector_count_at_least") && check.selector) {
11228
+ if (
11229
+ (
11230
+ check.type === "selector_visible"
11231
+ || check.type === "selector_absent"
11232
+ || check.type === "selector_count_at_least"
11233
+ || check.type === "selector_count_equals"
11234
+ || check.type === "selector_count_equal"
11235
+ || check.type === "selector_count_eq"
11236
+ ) && check.selector
11237
+ ) {
11161
11238
  selectors[check.selector] = await selectorStats(check.selector);
11162
11239
  }
11163
11240
  if (check.type === "selector_text_order" && check.selector) {
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-RAVOICNN.js";
61
+ } from "./chunk-MT2OZCCP.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -56,7 +56,11 @@ var RIDDLE_PROOF_PROFILE_STATUSES = [
56
56
  var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
57
57
  "route_loaded",
58
58
  "selector_visible",
59
+ "selector_absent",
59
60
  "selector_count_at_least",
61
+ "selector_count_equals",
62
+ "selector_count_equal",
63
+ "selector_count_eq",
60
64
  "selector_text_order",
61
65
  "frame_text_visible",
62
66
  "frame_no_horizontal_overflow",
@@ -304,6 +308,7 @@ function normalizeNetworkMock(input, index) {
304
308
  url,
305
309
  method: stringValue(input.method)?.toUpperCase(),
306
310
  responses,
311
+ repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
307
312
  required_hit_count: requiredHitCount,
308
313
  required: input.required === false ? false : true
309
314
  };
@@ -395,7 +400,7 @@ function normalizeCheck(input, index) {
395
400
  if (!isSupportedCheckType(type)) {
396
401
  throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
397
402
  }
398
- if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue(input.selector)) {
403
+ 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)) {
399
404
  throw new Error(`checks[${index}] ${type} requires selector.`);
400
405
  }
401
406
  if ((type === "frame_text_visible" || type === "frame_no_horizontal_overflow") && !stringValue(input.selector)) {
@@ -410,6 +415,10 @@ function normalizeCheck(input, index) {
410
415
  if (type === "selector_count_at_least" && numberValue(input.min_count) === void 0) {
411
416
  throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
412
417
  }
418
+ const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
419
+ if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
420
+ throw new Error(`checks[${index}] ${type} requires expected_count.`);
421
+ }
413
422
  const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
414
423
  if (type === "selector_text_order") {
415
424
  if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
@@ -441,6 +450,7 @@ function normalizeCheck(input, index) {
441
450
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
442
451
  allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
443
452
  min_count: numberValue(input.min_count),
453
+ expected_count: expectedCount,
444
454
  max_overflow_px: numberValue(input.max_overflow_px),
445
455
  timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
446
456
  run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
@@ -708,6 +718,20 @@ function assessCheckFromEvidence(check, evidence) {
708
718
  message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
709
719
  };
710
720
  }
721
+ if (check.type === "selector_absent") {
722
+ const key = selectorKey(check);
723
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
724
+ return {
725
+ type: check.type,
726
+ label: checkLabel(check),
727
+ status: failed.length ? "failed" : "passed",
728
+ evidence: {
729
+ selector: key,
730
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
731
+ },
732
+ message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
733
+ };
734
+ }
711
735
  if (check.type === "selector_count_at_least") {
712
736
  const key = selectorKey(check);
713
737
  const minCount = check.min_count ?? 1;
@@ -724,6 +748,22 @@ function assessCheckFromEvidence(check, evidence) {
724
748
  message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
725
749
  };
726
750
  }
751
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
752
+ const key = selectorKey(check);
753
+ const expectedCount = check.expected_count ?? 0;
754
+ const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
755
+ return {
756
+ type: check.type,
757
+ label: checkLabel(check),
758
+ status: failed.length ? "failed" : "passed",
759
+ evidence: {
760
+ selector: key,
761
+ expected_count: expectedCount,
762
+ counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
763
+ },
764
+ message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
765
+ };
766
+ }
727
767
  if (check.type === "selector_text_order") {
728
768
  const key = selectorKey(check);
729
769
  const expectedTexts = check.expected_texts || [];
@@ -1460,6 +1500,18 @@ function assessProfile(profile, evidence) {
1460
1500
  });
1461
1501
  continue;
1462
1502
  }
1503
+ if (check.type === "selector_absent") {
1504
+ const selector = check.selector || "";
1505
+ const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
1506
+ checks.push({
1507
+ type: check.type,
1508
+ label: check.label || check.type,
1509
+ status: failed.length ? "failed" : "passed",
1510
+ evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1511
+ message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
1512
+ });
1513
+ continue;
1514
+ }
1463
1515
  if (check.type === "selector_count_at_least") {
1464
1516
  const selector = check.selector || "";
1465
1517
  const minCount = check.min_count == null ? 1 : check.min_count;
@@ -1473,6 +1525,19 @@ function assessProfile(profile, evidence) {
1473
1525
  });
1474
1526
  continue;
1475
1527
  }
1528
+ if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
1529
+ const selector = check.selector || "";
1530
+ const expectedCount = check.expected_count == null ? 0 : check.expected_count;
1531
+ const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
1532
+ checks.push({
1533
+ type: check.type,
1534
+ label: check.label || check.type,
1535
+ status: failed.length ? "failed" : "passed",
1536
+ evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1537
+ message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
1538
+ });
1539
+ continue;
1540
+ }
1476
1541
  if (check.type === "selector_text_order") {
1477
1542
  const selector = check.selector || "";
1478
1543
  const expectedTexts = check.expected_texts || [];
@@ -1791,7 +1856,9 @@ async function registerNetworkMocks(mocks) {
1791
1856
  const responses = Array.isArray(mock.responses) ? mock.responses : [];
1792
1857
  const hitIndex = hitCount;
1793
1858
  hitCount += 1;
1794
- const responseIndex = responses.length ? Math.min(hitIndex, responses.length - 1) : null;
1859
+ const responseIndex = responses.length
1860
+ ? (mock.repeat_responses ? hitIndex % responses.length : Math.min(hitIndex, responses.length - 1))
1861
+ : null;
1795
1862
  const response = responseIndex === null ? mock : responses[responseIndex];
1796
1863
  const headers = { ...(response.headers || mock.headers || {}) };
1797
1864
  let body = response.body || "";
@@ -1806,7 +1873,8 @@ async function registerNetworkMocks(mocks) {
1806
1873
  response_label: response.label || null,
1807
1874
  hit_index: hitIndex,
1808
1875
  response_index: responseIndex,
1809
- sequence_reused: responseIndex !== null && hitIndex >= responses.length,
1876
+ sequence_reused: responseIndex !== null && !mock.repeat_responses && hitIndex >= responses.length,
1877
+ sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
1810
1878
  url: request.url(),
1811
1879
  method,
1812
1880
  status: response.status || mock.status || 200,
@@ -2486,7 +2554,16 @@ async function captureViewport(viewport) {
2486
2554
  const text_sequences = {};
2487
2555
  const text_matches = {};
2488
2556
  for (const check of profile.checks || []) {
2489
- if ((check.type === "selector_visible" || check.type === "selector_count_at_least") && check.selector) {
2557
+ if (
2558
+ (
2559
+ check.type === "selector_visible"
2560
+ || check.type === "selector_absent"
2561
+ || check.type === "selector_count_at_least"
2562
+ || check.type === "selector_count_equals"
2563
+ || check.type === "selector_count_equal"
2564
+ || check.type === "selector_count_eq"
2565
+ ) && check.selector
2566
+ ) {
2490
2567
  selectors[check.selector] = await selectorStats(check.selector);
2491
2568
  }
2492
2569
  if (check.type === "selector_text_order" && check.selector) {
@@ -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", "selector_visible", "selector_count_at_least", "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", "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"];
8
8
  declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
@@ -47,6 +47,7 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
47
47
  url: string;
48
48
  method?: string;
49
49
  responses?: RiddleProofProfileNetworkMockResponse[];
50
+ repeat_responses?: boolean;
50
51
  required_hit_count?: number;
51
52
  required?: boolean;
52
53
  }
@@ -87,6 +88,7 @@ interface RiddleProofProfileCheck {
87
88
  allowed_page_error_texts?: string[];
88
89
  allowed_page_error_patterns?: string[];
89
90
  min_count?: number;
91
+ expected_count?: number;
90
92
  max_overflow_px?: number;
91
93
  timeout_ms?: number;
92
94
  run_direct_routes?: boolean;
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", "selector_visible", "selector_count_at_least", "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", "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"];
8
8
  declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
@@ -47,6 +47,7 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
47
47
  url: string;
48
48
  method?: string;
49
49
  responses?: RiddleProofProfileNetworkMockResponse[];
50
+ repeat_responses?: boolean;
50
51
  required_hit_count?: number;
51
52
  required?: boolean;
52
53
  }
@@ -87,6 +88,7 @@ interface RiddleProofProfileCheck {
87
88
  allowed_page_error_texts?: string[];
88
89
  allowed_page_error_patterns?: string[];
89
90
  min_count?: number;
91
+ expected_count?: number;
90
92
  max_overflow_px?: number;
91
93
  timeout_ms?: number;
92
94
  run_direct_routes?: boolean;
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-RAVOICNN.js";
22
+ } from "./chunk-MT2OZCCP.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.40",
3
+ "version": "0.7.42",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",