@riddledc/riddle-proof 0.7.58 → 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/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",
@@ -7109,6 +7111,11 @@ function normalizeSetupAction(input, index) {
7109
7111
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
7110
7112
  const type = normalizeSetupActionType(stringValue2(input.type), index);
7111
7113
  const selector = stringValue2(input.selector);
7114
+ const frameSelector = stringFromOwn(input, "frame_selector", "frameSelector", "iframe_selector", "iframeSelector");
7115
+ const frameIndex = numberValue(valueFromOwn(input, "frame_index", "frameIndex", "iframe_index", "iframeIndex"));
7116
+ if (frameIndex !== void 0 && (!Number.isInteger(frameIndex) || frameIndex < 0)) {
7117
+ throw new Error(`target.setup_actions[${index}].frame_index must be a non-negative integer.`);
7118
+ }
7112
7119
  if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text" || type === "assert_text_visible" || type === "assert_text_absent" || type === "assert_selector_count") && !selector) {
7113
7120
  throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
7114
7121
  }
@@ -7161,6 +7168,8 @@ function normalizeSetupAction(input, index) {
7161
7168
  return {
7162
7169
  type,
7163
7170
  selector,
7171
+ frame_selector: frameSelector,
7172
+ frame_index: frameIndex,
7164
7173
  force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
7165
7174
  key,
7166
7175
  value,
@@ -7372,12 +7381,19 @@ function normalizeCheck(input, index) {
7372
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)) {
7373
7382
  throw new Error(`checks[${index}] ${type} requires selector.`);
7374
7383
  }
7375
- 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)) {
7376
7385
  throw new Error(`checks[${index}] ${type} requires selector.`);
7377
7386
  }
7378
7387
  if (type === "frame_text_visible" && !stringValue2(input.text) && !stringValue2(input.pattern)) {
7379
7388
  throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
7380
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
+ }
7381
7397
  if ((type === "text_visible" || type === "text_absent") && !stringValue2(input.text) && !stringValue2(input.pattern)) {
7382
7398
  throw new Error(`checks[${index}] ${type} requires text or pattern.`);
7383
7399
  }
@@ -7410,6 +7426,7 @@ function normalizeCheck(input, index) {
7410
7426
  expected_path: stringValue2(input.expected_path),
7411
7427
  param: stringValue2(input.param) || stringValue2(input.search_param) || stringValue2(input.searchParam) || stringValue2(input.key),
7412
7428
  expected_value: expectedValue,
7429
+ expected_url: expectedUrl,
7413
7430
  expected_routes: expectedRoutes,
7414
7431
  selector: stringValue2(input.selector),
7415
7432
  expected_texts: expectedTexts,
@@ -7877,6 +7894,35 @@ function assessCheckFromEvidence(check, evidence) {
7877
7894
  message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
7878
7895
  };
7879
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
+ }
7880
7926
  if (check.type === "frame_no_horizontal_overflow") {
7881
7927
  const key = selectorKey(check);
7882
7928
  const maxOverflow = check.max_overflow_px ?? 4;
@@ -8047,6 +8093,8 @@ function assessSetupActionsFromEvidence(profile, evidence) {
8047
8093
  viewport: viewport.name,
8048
8094
  action: result.action ?? result.type ?? null,
8049
8095
  selector: result.selector ?? null,
8096
+ frame_selector: result.frame_selector ?? null,
8097
+ frame_index: result.frame_index ?? null,
8050
8098
  reason: result.reason ?? result.error ?? null
8051
8099
  });
8052
8100
  }
@@ -8543,10 +8591,12 @@ function assessProfile(profile, evidence) {
8543
8591
  if (result && result.ok === false) {
8544
8592
  failed.push({
8545
8593
  viewport: viewport.name,
8546
- action: result.action || result.type || null,
8547
- selector: result.selector || null,
8548
- reason: result.reason || result.error || null,
8549
- });
8594
+ action: result.action || result.type || null,
8595
+ selector: result.selector || null,
8596
+ frame_selector: result.frame_selector || null,
8597
+ frame_index: result.frame_index ?? null,
8598
+ reason: result.reason || result.error || null,
8599
+ });
8550
8600
  }
8551
8601
  }
8552
8602
  if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
@@ -8748,6 +8798,36 @@ function assessProfile(profile, evidence) {
8748
8798
  });
8749
8799
  continue;
8750
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
+ }
8751
8831
  if (check.type === "frame_no_horizontal_overflow") {
8752
8832
  const selector = check.selector || "";
8753
8833
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
@@ -9009,8 +9089,8 @@ function setupJsonValue(value) {
9009
9089
  function setupValuesEqual(left, right) {
9010
9090
  return JSON.stringify(setupJsonValue(left)) === JSON.stringify(setupJsonValue(right));
9011
9091
  }
9012
- async function setupReadWindowValue(path) {
9013
- return await page.evaluate(({ path }) => {
9092
+ async function setupReadWindowValue(context, path) {
9093
+ return await context.evaluate(({ path }) => {
9014
9094
  const toJsonValue = (value) => {
9015
9095
  if (value === null || value === undefined) return null;
9016
9096
  if (typeof value === "string" || typeof value === "boolean") return value;
@@ -9032,6 +9112,80 @@ async function setupReadWindowValue(path) {
9032
9112
  return { ok: true, value: toJsonValue(current) };
9033
9113
  }, { path });
9034
9114
  }
9115
+ function setupFrameSelector(action) {
9116
+ return String(action?.frame_selector || action?.frameSelector || action?.iframe_selector || action?.iframeSelector || "").trim();
9117
+ }
9118
+ function setupFrameIndex(action) {
9119
+ const raw = action?.frame_index ?? action?.frameIndex ?? action?.iframe_index ?? action?.iframeIndex ?? 0;
9120
+ const number = Number(raw);
9121
+ return Number.isInteger(number) && number >= 0 ? number : 0;
9122
+ }
9123
+ function setupScopeEvidence(scope) {
9124
+ if (!scope || !scope.frame_selector) return {};
9125
+ return {
9126
+ frame_selector: scope.frame_selector,
9127
+ frame_index: scope.frame_index,
9128
+ frame_count: scope.frame_count,
9129
+ };
9130
+ }
9131
+ function setupScopeFailure(base, scope) {
9132
+ return {
9133
+ ...base,
9134
+ ...setupScopeEvidence(scope),
9135
+ reason: scope?.reason || "frame_scope_unavailable",
9136
+ error: scope?.error || undefined,
9137
+ };
9138
+ }
9139
+ async function setupActionScope(action, timeout) {
9140
+ const frameSelector = setupFrameSelector(action);
9141
+ if (!frameSelector) return { ok: true, context: page };
9142
+ const frameIndex = setupFrameIndex(action);
9143
+ let frameCount = 0;
9144
+ let locator = null;
9145
+ try {
9146
+ await page.waitForSelector(frameSelector, { state: "attached", timeout });
9147
+ locator = page.locator(frameSelector);
9148
+ frameCount = await locator.count();
9149
+ } catch (error) {
9150
+ return {
9151
+ ok: false,
9152
+ reason: "frame_selector_not_found",
9153
+ frame_selector: frameSelector,
9154
+ frame_index: frameIndex,
9155
+ frame_count: frameCount,
9156
+ error: String(error && error.message ? error.message : error).slice(0, 1000),
9157
+ };
9158
+ }
9159
+ if (!frameCount) {
9160
+ return { ok: false, reason: "frame_selector_not_found", frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
9161
+ }
9162
+ if (frameIndex >= frameCount) {
9163
+ return { ok: false, reason: "frame_index_out_of_range", frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
9164
+ }
9165
+ const handle = await locator.nth(frameIndex).elementHandle({ timeout }).catch((error) => ({ __riddle_error: error }));
9166
+ if (!handle || handle.__riddle_error) {
9167
+ return {
9168
+ ok: false,
9169
+ reason: "frame_element_unavailable",
9170
+ frame_selector: frameSelector,
9171
+ frame_index: frameIndex,
9172
+ frame_count: frameCount,
9173
+ error: handle?.__riddle_error ? String(handle.__riddle_error && handle.__riddle_error.message ? handle.__riddle_error.message : handle.__riddle_error).slice(0, 1000) : undefined,
9174
+ };
9175
+ }
9176
+ const frame = typeof handle.contentFrame === "function" ? await handle.contentFrame().catch((error) => ({ __riddle_error: error })) : null;
9177
+ if (!frame || frame.__riddle_error) {
9178
+ return {
9179
+ ok: false,
9180
+ reason: "content_frame_unavailable",
9181
+ frame_selector: frameSelector,
9182
+ frame_index: frameIndex,
9183
+ frame_count: frameCount,
9184
+ error: frame?.__riddle_error ? String(frame.__riddle_error && frame.__riddle_error.message ? frame.__riddle_error.message : frame.__riddle_error).slice(0, 1000) : undefined,
9185
+ };
9186
+ }
9187
+ return { ok: true, context: frame, frame_selector: frameSelector, frame_index: frameIndex, frame_count: frameCount };
9188
+ }
9035
9189
  async function setupLocatorText(locator, index) {
9036
9190
  return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
9037
9191
  }
@@ -9193,7 +9347,8 @@ async function registerNetworkMocks(mocks) {
9193
9347
  }
9194
9348
  async function executeSetupAction(action, ordinal) {
9195
9349
  const type = setupActionType(action);
9196
- const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null };
9350
+ const frameSelector = setupFrameSelector(action);
9351
+ const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null };
9197
9352
  const timeout = setupNumber(action.timeout_ms, 5000);
9198
9353
  try {
9199
9354
  if (type === "wait") {
@@ -9202,51 +9357,65 @@ async function executeSetupAction(action, ordinal) {
9202
9357
  return { ...base, ok: true, ms };
9203
9358
  }
9204
9359
  if (type === "wait_for_selector") {
9205
- await page.waitForSelector(action.selector, { state: "visible", timeout });
9206
- return { ...base, ok: true, timeout_ms: timeout };
9360
+ const scope = await setupActionScope(action, timeout);
9361
+ if (!scope.ok) return setupScopeFailure(base, scope);
9362
+ await scope.context.waitForSelector(action.selector, { state: "visible", timeout });
9363
+ return { ...base, ...setupScopeEvidence(scope), ok: true, timeout_ms: timeout };
9207
9364
  }
9208
9365
  if (type === "press") {
9209
9366
  const key = String(action.key || "").trim();
9210
9367
  if (!key) return { ...base, reason: "missing_key" };
9368
+ const scope = await setupActionScope(action, timeout);
9369
+ if (!scope.ok) return setupScopeFailure(base, scope);
9211
9370
  if (!action.selector) {
9212
- await page.keyboard.press(key);
9213
- return { ...base, ok: true, key };
9371
+ if (scope.frame_selector) {
9372
+ await scope.context.locator("body").press(key, { timeout });
9373
+ } else {
9374
+ await page.keyboard.press(key);
9375
+ }
9376
+ return { ...base, ...setupScopeEvidence(scope), ok: true, key };
9214
9377
  }
9215
- const locator = page.locator(action.selector);
9378
+ const locator = scope.context.locator(action.selector);
9216
9379
  const count = await locator.count();
9217
9380
  if (!count) return { ...base, reason: "selector_not_found", count, key };
9218
9381
  const targetIndex = Number.isInteger(action.index) ? action.index : 0;
9219
9382
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
9220
9383
  await locator.nth(targetIndex).press(key, { timeout });
9221
- return { ...base, ok: true, count, target_index: targetIndex, key };
9384
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key };
9222
9385
  }
9223
9386
  if (type === "local_storage" || type === "session_storage") {
9224
9387
  const value = setupActionValue(action);
9225
- await page.evaluate(({ type, key, value }) => {
9388
+ const scope = await setupActionScope(action, timeout);
9389
+ if (!scope.ok) return setupScopeFailure(base, scope);
9390
+ await scope.context.evaluate(({ type, key, value }) => {
9226
9391
  const storage = type === "session_storage" ? window.sessionStorage : window.localStorage;
9227
9392
  storage.setItem(key, value);
9228
9393
  }, { type, key: action.key, value });
9229
9394
  if (action.reload === true) {
9230
9395
  await page.reload({ waitUntil: "domcontentloaded", timeout: 45000 });
9231
9396
  }
9232
- return { ...base, ok: true, key: action.key, value_length: value.length, reload: action.reload === true };
9397
+ return { ...base, ...setupScopeEvidence(scope), ok: true, key: action.key, value_length: value.length, reload: action.reload === true };
9233
9398
  }
9234
9399
  if (type === "clear_storage") {
9235
9400
  const storage = action.storage || "both";
9236
- await page.evaluate(({ storage }) => {
9401
+ const scope = await setupActionScope(action, timeout);
9402
+ if (!scope.ok) return setupScopeFailure(base, scope);
9403
+ await scope.context.evaluate(({ storage }) => {
9237
9404
  if (storage === "local" || storage === "both") window.localStorage.clear();
9238
9405
  if (storage === "session" || storage === "both") window.sessionStorage.clear();
9239
9406
  }, { storage });
9240
9407
  if (action.reload === true) {
9241
9408
  await page.reload({ waitUntil: "domcontentloaded", timeout: 45000 });
9242
9409
  }
9243
- return { ...base, ok: true, storage, reload: action.reload === true };
9410
+ return { ...base, ...setupScopeEvidence(scope), ok: true, storage, reload: action.reload === true };
9244
9411
  }
9245
9412
  if (type === "window_call") {
9246
9413
  const path = String(action.path || action.function_path || action.functionPath || "");
9247
9414
  const args = Array.isArray(action.args) ? action.args : [];
9248
9415
  if (!path) return { ...base, path, reason: "missing_path" };
9249
- const result = await page.evaluate(async ({ path, args }) => {
9416
+ const scope = await setupActionScope(action, timeout);
9417
+ if (!scope.ok) return setupScopeFailure(base, scope);
9418
+ const result = await scope.context.evaluate(async ({ path, args }) => {
9250
9419
  const toJsonValue = (value) => {
9251
9420
  if (value === null || value === undefined) return null;
9252
9421
  if (typeof value === "string" || typeof value === "boolean") return value;
@@ -9286,6 +9455,7 @@ async function executeSetupAction(action, ordinal) {
9286
9455
  const expectationMet = !hasExpectation || setupValuesEqual(result.returned, expected);
9287
9456
  return {
9288
9457
  ...base,
9458
+ ...setupScopeEvidence(scope),
9289
9459
  ok: Boolean(result.ok && expectationMet),
9290
9460
  path,
9291
9461
  arg_count: args.length,
@@ -9316,13 +9486,16 @@ async function executeSetupAction(action, ordinal) {
9316
9486
  : action.expect;
9317
9487
  if (!path) return { ...base, path, reason: "missing_path" };
9318
9488
  if (!hasExpected) return { ...base, path, reason: "missing_expected_value" };
9489
+ const scope = await setupActionScope(action, timeout);
9490
+ if (!scope.ok) return setupScopeFailure(base, scope);
9319
9491
  const startedAt = Date.now();
9320
9492
  let result = null;
9321
9493
  while (Date.now() - startedAt <= timeout) {
9322
- result = await setupReadWindowValue(path);
9494
+ result = await setupReadWindowValue(scope.context, path);
9323
9495
  if (result.ok && setupValuesEqual(result.value, expected)) {
9324
9496
  return {
9325
9497
  ...base,
9498
+ ...setupScopeEvidence(scope),
9326
9499
  ok: true,
9327
9500
  path,
9328
9501
  value: setupJsonValue(result.value),
@@ -9334,6 +9507,7 @@ async function executeSetupAction(action, ordinal) {
9334
9507
  }
9335
9508
  return {
9336
9509
  ...base,
9510
+ ...setupScopeEvidence(scope),
9337
9511
  path,
9338
9512
  reason: result?.ok ? "unexpected_value" : result?.reason || "path_not_found",
9339
9513
  missing_part: result?.missing_part || undefined,
@@ -9350,11 +9524,13 @@ async function executeSetupAction(action, ordinal) {
9350
9524
  const hasExpected = expected !== undefined;
9351
9525
  if (!path) return { ...base, path, reason: "missing_path" };
9352
9526
  if (!hasExpected && minValue === undefined && maxValue === undefined) return { ...base, path, reason: "missing_number_expectation" };
9527
+ const scope = await setupActionScope(action, timeout);
9528
+ if (!scope.ok) return setupScopeFailure(base, scope);
9353
9529
  const startedAt = Date.now();
9354
9530
  let result = null;
9355
9531
  let lastReason = "path_not_found";
9356
9532
  while (Date.now() - startedAt <= timeout) {
9357
- result = await setupReadWindowValue(path);
9533
+ result = await setupReadWindowValue(scope.context, path);
9358
9534
  if (result.ok) {
9359
9535
  const actual = setupFiniteNumber(result.value);
9360
9536
  if (actual === undefined) {
@@ -9368,6 +9544,7 @@ async function executeSetupAction(action, ordinal) {
9368
9544
  } else {
9369
9545
  return {
9370
9546
  ...base,
9547
+ ...setupScopeEvidence(scope),
9371
9548
  ok: true,
9372
9549
  path,
9373
9550
  value: actual,
@@ -9384,6 +9561,7 @@ async function executeSetupAction(action, ordinal) {
9384
9561
  }
9385
9562
  return {
9386
9563
  ...base,
9564
+ ...setupScopeEvidence(scope),
9387
9565
  path,
9388
9566
  reason: lastReason,
9389
9567
  missing_part: result?.missing_part || undefined,
@@ -9395,7 +9573,9 @@ async function executeSetupAction(action, ordinal) {
9395
9573
  };
9396
9574
  }
9397
9575
  if (type === "click") {
9398
- const locator = page.locator(action.selector);
9576
+ const scope = await setupActionScope(action, timeout);
9577
+ if (!scope.ok) return setupScopeFailure(base, scope);
9578
+ const locator = scope.context.locator(action.selector);
9399
9579
  const count = await locator.count();
9400
9580
  if (!count) return { ...base, reason: "selector_not_found", count };
9401
9581
  let targetIndex = Number.isInteger(action.index) ? action.index : 0;
@@ -9427,33 +9607,39 @@ async function executeSetupAction(action, ordinal) {
9427
9607
  ? { timeout, noWaitAfter: true, force: true }
9428
9608
  : { timeout, noWaitAfter: true };
9429
9609
  await locator.nth(targetIndex).click(clickOptions);
9430
- return { ...base, ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
9610
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
9431
9611
  }
9432
9612
  if (type === "fill" || type === "set_input_value") {
9433
- const locator = page.locator(action.selector);
9613
+ const scope = await setupActionScope(action, timeout);
9614
+ if (!scope.ok) return setupScopeFailure(base, scope);
9615
+ const locator = scope.context.locator(action.selector);
9434
9616
  const count = await locator.count();
9435
9617
  if (!count) return { ...base, reason: "selector_not_found", count };
9436
9618
  const targetIndex = Number.isInteger(action.index) ? action.index : 0;
9437
9619
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
9438
9620
  const value = setupActionValue(action);
9439
9621
  await locator.nth(targetIndex).fill(value, { timeout });
9440
- return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
9622
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, value_length: value.length };
9441
9623
  }
9442
9624
  if (type === "assert_selector_count") {
9443
- const locator = page.locator(action.selector);
9625
+ const scope = await setupActionScope(action, timeout);
9626
+ if (!scope.ok) return setupScopeFailure(base, scope);
9627
+ const locator = scope.context.locator(action.selector);
9444
9628
  const expectedCount = setupNumber(action.expected_count, -1);
9445
9629
  if (!Number.isInteger(expectedCount) || expectedCount < 0) return { ...base, reason: "invalid_expected_count", expected_count: action.expected_count };
9446
9630
  const startedAt = Date.now();
9447
9631
  let count = 0;
9448
9632
  while (Date.now() - startedAt <= timeout) {
9449
9633
  count = await locator.count().catch(() => 0);
9450
- if (count === expectedCount) return { ...base, ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
9634
+ if (count === expectedCount) return { ...base, ...setupScopeEvidence(scope), ok: true, count, expected_count: expectedCount, timeout_ms: timeout };
9451
9635
  await page.waitForTimeout(100);
9452
9636
  }
9453
- return { ...base, reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
9637
+ return { ...base, ...setupScopeEvidence(scope), reason: "selector_count_mismatch", count, expected_count: expectedCount, timeout_ms: timeout };
9454
9638
  }
9455
9639
  if (type === "wait_for_text") {
9456
- const locator = page.locator(action.selector);
9640
+ const scope = await setupActionScope(action, timeout);
9641
+ if (!scope.ok) return setupScopeFailure(base, scope);
9642
+ const locator = scope.context.locator(action.selector);
9457
9643
  const startedAt = Date.now();
9458
9644
  let lastText = "";
9459
9645
  while (Date.now() - startedAt <= timeout) {
@@ -9462,15 +9648,17 @@ async function executeSetupAction(action, ordinal) {
9462
9648
  const text = await setupLocatorText(locator, index);
9463
9649
  lastText = text || lastText;
9464
9650
  if (setupTextMatches(text, action)) {
9465
- return { ...base, ok: true, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
9651
+ return { ...base, ...setupScopeEvidence(scope), ok: true, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
9466
9652
  }
9467
9653
  }
9468
9654
  await page.waitForTimeout(100);
9469
9655
  }
9470
- return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
9656
+ return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
9471
9657
  }
9472
9658
  if (type === "assert_text_visible" || type === "assert_text_absent") {
9473
- const locator = page.locator(action.selector);
9659
+ const scope = await setupActionScope(action, timeout);
9660
+ if (!scope.ok) return setupScopeFailure(base, scope);
9661
+ const locator = scope.context.locator(action.selector);
9474
9662
  const startedAt = Date.now();
9475
9663
  let lastText = "";
9476
9664
  let matchedText = "";
@@ -9489,7 +9677,7 @@ async function executeSetupAction(action, ordinal) {
9489
9677
  if (type === "assert_text_visible") {
9490
9678
  const visible = await setupLocatorVisible(locator, index);
9491
9679
  if (visible) {
9492
- return { ...base, ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
9680
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, text: compactSetupResultText(text), target_index: index, timeout_ms: timeout };
9493
9681
  }
9494
9682
  hiddenMatch = true;
9495
9683
  break;
@@ -9498,17 +9686,17 @@ async function executeSetupAction(action, ordinal) {
9498
9686
  }
9499
9687
  }
9500
9688
  if (type === "assert_text_absent" && !matched) {
9501
- return { ...base, ok: true, count, timeout_ms: timeout };
9689
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, timeout_ms: timeout };
9502
9690
  }
9503
9691
  await page.waitForTimeout(100);
9504
9692
  }
9505
9693
  if (type === "assert_text_visible") {
9506
9694
  if (hiddenMatch) {
9507
- return { ...base, reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
9695
+ return { ...base, ...setupScopeEvidence(scope), reason: "matching_element_not_visible", text: compactSetupResultText(matchedText), timeout_ms: timeout };
9508
9696
  }
9509
- return { ...base, reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
9697
+ return { ...base, ...setupScopeEvidence(scope), reason: "text_not_found", text: compactSetupResultText(lastText), timeout_ms: timeout };
9510
9698
  }
9511
- return { ...base, reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
9699
+ return { ...base, ...setupScopeEvidence(scope), reason: "text_still_present", text: compactSetupResultText(matchedText || lastText), timeout_ms: timeout };
9512
9700
  }
9513
9701
  return { ...base, reason: "unsupported_action" };
9514
9702
  } catch (error) {
@@ -10111,7 +10299,7 @@ async function captureViewport(viewport) {
10111
10299
  if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
10112
10300
  text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
10113
10301
  }
10114
- 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) {
10115
10303
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
10116
10304
  frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
10117
10305
  }
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-7ZBK2GQX.js";
13
+ } from "./chunk-FLQ4HHTT.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport