@riddledc/riddle-proof 0.7.132 → 0.7.134

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/index.cjs CHANGED
@@ -8794,7 +8794,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
8794
8794
  "wait",
8795
8795
  "wait_for_selector",
8796
8796
  "wait_for_text",
8797
- "window_call"
8797
+ "window_call",
8798
+ "window_call_until"
8798
8799
  ];
8799
8800
  var RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES = [
8800
8801
  "aborted",
@@ -9184,6 +9185,19 @@ function profileSetupActionCounts(results) {
9184
9185
  function profileSetupScreenshotLabels(results) {
9185
9186
  return results.filter((result) => profileSetupResultAction(result) === "screenshot" && result.ok !== false && typeof result.screenshot_label === "string").map((result) => result.screenshot_label).filter(Boolean);
9186
9187
  }
9188
+ function profileSetupWindowCallUntilReceipts(results) {
9189
+ return results.filter((result) => profileSetupResultAction(result) === "window_call_until").map((result) => ({
9190
+ ordinal: result.ordinal ?? null,
9191
+ ok: result.ok !== false,
9192
+ path: result.path ?? null,
9193
+ until_path: result.until_path ?? null,
9194
+ until_value: result.until_value ?? null,
9195
+ until_expected_value: result.until_expected_value ?? null,
9196
+ call_count: result.call_count ?? null,
9197
+ max_calls: result.max_calls ?? null,
9198
+ reason: result.reason ?? result.error ?? null
9199
+ }));
9200
+ }
9187
9201
  function sampleProfileSetupSummaryItems(items, limit) {
9188
9202
  if (items.length <= limit) return items;
9189
9203
  const firstCount = Math.floor(limit / 2);
@@ -9209,6 +9223,9 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
9209
9223
  const optionalFailed = results.filter((result) => result.ok === false && result.optional === true);
9210
9224
  const successfulClicks = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false);
9211
9225
  const clickCountValues = successfulClicks.map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0).filter((value) => value !== void 0);
9226
+ const windowCallUntilReceipts = profileSetupWindowCallUntilReceipts(results);
9227
+ const windowCallUntilCallCounts = windowCallUntilReceipts.map((result) => typeof result.call_count === "number" && Number.isFinite(result.call_count) ? result.call_count : void 0).filter((value) => value !== void 0);
9228
+ const sampledWindowCallUntilReceipts = sampleProfileSetupSummaryItems(windowCallUntilReceipts, 8);
9212
9229
  const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
9213
9230
  const clickCount = typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0;
9214
9231
  return {
@@ -9241,6 +9258,10 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
9241
9258
  clicked_truncated: clickedItems.length > clicked.length,
9242
9259
  click_count_action_total: clickCountValues.length,
9243
9260
  click_count_value_total: clickCountValues.reduce((sum, value) => sum + value, 0),
9261
+ window_call_until_total: windowCallUntilReceipts.length,
9262
+ window_call_until_call_total: windowCallUntilCallCounts.reduce((sum, value) => sum + value, 0),
9263
+ window_call_until_truncated: windowCallUntilReceipts.length > sampledWindowCallUntilReceipts.length,
9264
+ window_call_until: sampledWindowCallUntilReceipts,
9244
9265
  clicked,
9245
9266
  text_samples,
9246
9267
  failed: failed.map((result) => ({
@@ -9293,7 +9314,7 @@ function isSupportedCheckType(value) {
9293
9314
  }
9294
9315
  function normalizeSetupActionType(value, index) {
9295
9316
  const normalizedInput = String(value || "").trim().replace(/-/g, "_");
9296
- const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput;
9317
+ const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput;
9297
9318
  if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
9298
9319
  return normalized;
9299
9320
  }
@@ -9424,10 +9445,10 @@ function normalizeSetupAction(input, index) {
9424
9445
  throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
9425
9446
  }
9426
9447
  const path6 = stringFromOwn(input, "path", "function_path", "functionPath", "window_path", "windowPath", "state_path", "statePath");
9427
- if ((type === "window_call" || type === "assert_window_value" || type === "assert_window_number") && !path6) {
9448
+ if ((type === "window_call" || type === "window_call_until" || type === "assert_window_value" || type === "assert_window_number") && !path6) {
9428
9449
  throw new Error(`target.setup_actions[${index}] ${type} requires path.`);
9429
9450
  }
9430
- const args = type === "window_call" ? normalizeSetupActionArgs(input, index) : void 0;
9451
+ const args = type === "window_call" || type === "window_call_until" ? normalizeSetupActionArgs(input, index) : void 0;
9431
9452
  const hasExpectedValue = hasOwn(input, "expected_value") || hasOwn(input, "expectedValue") || hasOwn(input, "expected") || hasOwn(input, "expect_value") || hasOwn(input, "expectValue") || hasOwn(input, "expect");
9432
9453
  if (type === "assert_window_value" && !hasExpectedValue) {
9433
9454
  throw new Error(`target.setup_actions[${index}] ${type} requires expected_value.`);
@@ -9444,6 +9465,24 @@ function normalizeSetupAction(input, index) {
9444
9465
  }
9445
9466
  }
9446
9467
  const hasExpectedReturn = hasOwn(input, "expect_return") || hasOwn(input, "expectReturn") || hasOwn(input, "expected_return") || hasOwn(input, "expectedReturn");
9468
+ const untilPath = stringFromOwn(input, "until_path", "untilPath", "until_state_path", "untilStatePath", "until_window_path", "untilWindowPath", "until");
9469
+ const hasUntilExpectedValue = hasOwn(input, "until_expected_value") || hasOwn(input, "untilExpectedValue") || hasOwn(input, "until_expected") || hasOwn(input, "untilExpected") || hasOwn(input, "until_value") || hasOwn(input, "untilValue") || hasOwn(input, "expected_value") || hasOwn(input, "expectedValue") || hasOwn(input, "expected");
9470
+ if (type === "window_call_until") {
9471
+ if (!untilPath) {
9472
+ throw new Error(`target.setup_actions[${index}] ${type} requires until_path.`);
9473
+ }
9474
+ if (!hasUntilExpectedValue) {
9475
+ throw new Error(`target.setup_actions[${index}] ${type} requires until_expected_value.`);
9476
+ }
9477
+ }
9478
+ const maxCalls = numberValue3(valueFromOwn(input, "max_calls", "maxCalls", "max_attempts", "maxAttempts", "attempts"));
9479
+ if (type === "window_call_until" && (maxCalls === void 0 || !Number.isInteger(maxCalls) || maxCalls < 1 || maxCalls > 100)) {
9480
+ throw new Error(`target.setup_actions[${index}].max_calls must be an integer from 1 to 100.`);
9481
+ }
9482
+ const intervalMs = numberValue3(valueFromOwn(input, "interval_ms", "intervalMs", "poll_ms", "pollMs", "call_interval_ms", "callIntervalMs"));
9483
+ if (type === "window_call_until" && intervalMs !== void 0 && (!Number.isInteger(intervalMs) || intervalMs < 0 || intervalMs > 5e3)) {
9484
+ throw new Error(`target.setup_actions[${index}].interval_ms must be an integer from 0 to 5000.`);
9485
+ }
9447
9486
  const steps = numberValue3(input.steps);
9448
9487
  if (type === "drag" && steps !== void 0 && (!Number.isInteger(steps) || steps < 1 || steps > 100)) {
9449
9488
  throw new Error(`target.setup_actions[${index}].steps must be an integer from 1 to 100.`);
@@ -9470,6 +9509,10 @@ function normalizeSetupAction(input, index) {
9470
9509
  path: path6,
9471
9510
  args,
9472
9511
  expect_return: hasExpectedReturn ? toJsonValue(valueFromOwn(input, "expect_return", "expectReturn", "expected_return", "expectedReturn")) : void 0,
9512
+ until_path: untilPath,
9513
+ until_expected_value: hasUntilExpectedValue ? toJsonValue(valueFromOwn(input, "until_expected_value", "untilExpectedValue", "until_expected", "untilExpected", "until_value", "untilValue", "expected_value", "expectedValue", "expected")) : void 0,
9514
+ max_calls: maxCalls,
9515
+ interval_ms: intervalMs,
9473
9516
  expected_value: hasExpectedValue ? toJsonValue(rawExpectedValue) : void 0,
9474
9517
  min_value: minValue,
9475
9518
  max_value: maxValue,
@@ -13346,6 +13389,34 @@ async function setupReadWindowValue(context, path) {
13346
13389
  return { ok: true, value: toJsonValue(current) };
13347
13390
  }, { path });
13348
13391
  }
13392
+ async function setupCallWindowFunction(context, path, args) {
13393
+ return await context.evaluate(async ({ path, args }) => {
13394
+ const toJsonValue = (value) => {
13395
+ if (value === null || value === undefined) return null;
13396
+ if (typeof value === "string" || typeof value === "boolean") return value;
13397
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
13398
+ if (Array.isArray(value)) return value.map(toJsonValue);
13399
+ if (typeof value === "object") {
13400
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
13401
+ }
13402
+ return String(value);
13403
+ };
13404
+ const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
13405
+ let parent = window;
13406
+ let current = window;
13407
+ for (const part of pathParts) {
13408
+ parent = current;
13409
+ current = current?.[part];
13410
+ }
13411
+ if (typeof current !== "function") return { ok: false, reason: "missing_function" };
13412
+ try {
13413
+ const returned = await current.apply(parent, Array.isArray(args) ? args : []);
13414
+ return { ok: true, returned: toJsonValue(returned) };
13415
+ } catch (error) {
13416
+ return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
13417
+ }
13418
+ }, { path, args });
13419
+ }
13349
13420
  function setupFrameSelector(action) {
13350
13421
  return String(action?.frame_selector || action?.frameSelector || action?.iframe_selector || action?.iframeSelector || "").trim();
13351
13422
  }
@@ -13863,32 +13934,7 @@ async function executeSetupAction(action, ordinal, viewport) {
13863
13934
  if (!path) return { ...base, path, reason: "missing_path" };
13864
13935
  const scope = await setupActionScope(action, timeout);
13865
13936
  if (!scope.ok) return setupScopeFailure(base, scope);
13866
- const result = await scope.context.evaluate(async ({ path, args }) => {
13867
- const toJsonValue = (value) => {
13868
- if (value === null || value === undefined) return null;
13869
- if (typeof value === "string" || typeof value === "boolean") return value;
13870
- if (typeof value === "number") return Number.isFinite(value) ? value : null;
13871
- if (Array.isArray(value)) return value.map(toJsonValue);
13872
- if (typeof value === "object") {
13873
- return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
13874
- }
13875
- return String(value);
13876
- };
13877
- const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
13878
- let parent = window;
13879
- let current = window;
13880
- for (const part of pathParts) {
13881
- parent = current;
13882
- current = current?.[part];
13883
- }
13884
- if (typeof current !== "function") return { ok: false, reason: "missing_function" };
13885
- try {
13886
- const returned = await current.apply(parent, Array.isArray(args) ? args : []);
13887
- return { ok: true, returned: toJsonValue(returned) };
13888
- } catch (error) {
13889
- return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
13890
- }
13891
- }, { path, args });
13937
+ const result = await setupCallWindowFunction(scope.context, path, args);
13892
13938
  const hasExpectation = setupHasOwn(action, "expect_return")
13893
13939
  || setupHasOwn(action, "expectReturn")
13894
13940
  || setupHasOwn(action, "expected_return")
@@ -13913,6 +13959,126 @@ async function executeSetupAction(action, ordinal, viewport) {
13913
13959
  error: result.error || undefined,
13914
13960
  };
13915
13961
  }
13962
+ if (type === "window_call_until") {
13963
+ const path = String(action.path || action.function_path || action.functionPath || "");
13964
+ const untilPath = String(action.until_path || action.untilPath || action.until_state_path || action.untilStatePath || action.until_window_path || action.untilWindowPath || action.until || "");
13965
+ const args = Array.isArray(action.args) ? action.args : [];
13966
+ if (!path) return { ...base, path, reason: "missing_path" };
13967
+ if (!untilPath) return { ...base, path, reason: "missing_until_path" };
13968
+ const hasUntilExpected = setupHasOwn(action, "until_expected_value")
13969
+ || setupHasOwn(action, "untilExpectedValue")
13970
+ || setupHasOwn(action, "until_expected")
13971
+ || setupHasOwn(action, "untilExpected")
13972
+ || setupHasOwn(action, "until_value")
13973
+ || setupHasOwn(action, "untilValue")
13974
+ || setupHasOwn(action, "expected_value")
13975
+ || setupHasOwn(action, "expectedValue")
13976
+ || setupHasOwn(action, "expected");
13977
+ const untilExpected = setupHasOwn(action, "until_expected_value")
13978
+ ? action.until_expected_value
13979
+ : setupHasOwn(action, "untilExpectedValue")
13980
+ ? action.untilExpectedValue
13981
+ : setupHasOwn(action, "until_expected")
13982
+ ? action.until_expected
13983
+ : setupHasOwn(action, "untilExpected")
13984
+ ? action.untilExpected
13985
+ : setupHasOwn(action, "until_value")
13986
+ ? action.until_value
13987
+ : setupHasOwn(action, "untilValue")
13988
+ ? action.untilValue
13989
+ : setupHasOwn(action, "expected_value")
13990
+ ? action.expected_value
13991
+ : setupHasOwn(action, "expectedValue")
13992
+ ? action.expectedValue
13993
+ : action.expected;
13994
+ if (!hasUntilExpected) return { ...base, path, until_path: untilPath, reason: "missing_until_expected_value" };
13995
+ const maxCalls = Math.min(100, Math.max(1, Math.floor(setupNumber(action.max_calls ?? action.maxCalls ?? action.max_attempts ?? action.maxAttempts ?? action.attempts, 1) || 1)));
13996
+ const intervalMs = Math.min(5000, Math.max(0, Math.floor(setupNumber(action.interval_ms ?? action.intervalMs ?? action.poll_ms ?? action.pollMs ?? action.call_interval_ms ?? action.callIntervalMs, 100) || 0)));
13997
+ const hasReturnExpectation = setupHasOwn(action, "expect_return")
13998
+ || setupHasOwn(action, "expectReturn")
13999
+ || setupHasOwn(action, "expected_return")
14000
+ || setupHasOwn(action, "expectedReturn");
14001
+ const expectedReturn = setupHasOwn(action, "expect_return")
14002
+ ? action.expect_return
14003
+ : setupHasOwn(action, "expectReturn")
14004
+ ? action.expectReturn
14005
+ : setupHasOwn(action, "expected_return")
14006
+ ? action.expected_return
14007
+ : action.expectedReturn;
14008
+ const scope = await setupActionScope(action, timeout);
14009
+ if (!scope.ok) return setupScopeFailure(base, scope);
14010
+ const startedAt = Date.now();
14011
+ let callCount = 0;
14012
+ let lastCallResult = null;
14013
+ let lastPredicateResult = await setupReadWindowValue(scope.context, untilPath);
14014
+ if (lastPredicateResult.ok && setupValuesEqual(lastPredicateResult.value, untilExpected)) {
14015
+ return {
14016
+ ...base,
14017
+ ...setupScopeEvidence(scope),
14018
+ ok: true,
14019
+ path,
14020
+ arg_count: args.length,
14021
+ until_path: untilPath,
14022
+ until_value: setupJsonValue(lastPredicateResult.value),
14023
+ until_expected_value: setupJsonValue(untilExpected),
14024
+ call_count: callCount,
14025
+ max_calls: maxCalls,
14026
+ interval_ms: intervalMs,
14027
+ timeout_ms: timeout,
14028
+ };
14029
+ }
14030
+ while (callCount < maxCalls && Date.now() - startedAt <= timeout) {
14031
+ lastCallResult = await setupCallWindowFunction(scope.context, path, args);
14032
+ callCount += 1;
14033
+ if (!lastCallResult.ok) break;
14034
+ if (hasReturnExpectation && !setupValuesEqual(lastCallResult.returned, expectedReturn)) break;
14035
+ lastPredicateResult = await setupReadWindowValue(scope.context, untilPath);
14036
+ if (lastPredicateResult.ok && setupValuesEqual(lastPredicateResult.value, untilExpected)) {
14037
+ return {
14038
+ ...base,
14039
+ ...setupScopeEvidence(scope),
14040
+ ok: true,
14041
+ path,
14042
+ arg_count: args.length,
14043
+ returned: setupJsonValue(lastCallResult.returned),
14044
+ expected_return: hasReturnExpectation ? setupJsonValue(expectedReturn) : undefined,
14045
+ until_path: untilPath,
14046
+ until_value: setupJsonValue(lastPredicateResult.value),
14047
+ until_expected_value: setupJsonValue(untilExpected),
14048
+ call_count: callCount,
14049
+ max_calls: maxCalls,
14050
+ interval_ms: intervalMs,
14051
+ timeout_ms: timeout,
14052
+ };
14053
+ }
14054
+ if (callCount < maxCalls && intervalMs) await page.waitForTimeout(intervalMs);
14055
+ }
14056
+ const returnExpectationMet = !hasReturnExpectation || setupValuesEqual(lastCallResult?.returned, expectedReturn);
14057
+ return {
14058
+ ...base,
14059
+ ...setupScopeEvidence(scope),
14060
+ path,
14061
+ arg_count: args.length,
14062
+ returned: setupJsonValue(lastCallResult?.returned),
14063
+ expected_return: hasReturnExpectation ? setupJsonValue(expectedReturn) : undefined,
14064
+ until_path: untilPath,
14065
+ until_value: setupJsonValue(lastPredicateResult?.value),
14066
+ until_expected_value: setupJsonValue(untilExpected),
14067
+ call_count: callCount,
14068
+ max_calls: maxCalls,
14069
+ interval_ms: intervalMs,
14070
+ timeout_ms: timeout,
14071
+ reason: lastCallResult && !lastCallResult.ok
14072
+ ? lastCallResult.reason
14073
+ : hasReturnExpectation && !returnExpectationMet
14074
+ ? "unexpected_return_value"
14075
+ : Date.now() - startedAt > timeout
14076
+ ? "timeout"
14077
+ : "until_condition_not_met",
14078
+ error: lastCallResult?.error || undefined,
14079
+ missing_part: lastPredicateResult?.missing_part || undefined,
14080
+ };
14081
+ }
13916
14082
  if (type === "assert_window_value") {
13917
14083
  const path = String(action.path || action.window_path || action.windowPath || "");
13918
14084
  const hasExpected = setupHasOwn(action, "expected_value")
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  resolveRiddleProofProfileTimeoutSec,
63
63
  slugifyRiddleProofProfileName,
64
64
  summarizeRiddleProofProfileResult
65
- } from "./chunk-PKE223TX.js";
65
+ } from "./chunk-TALHK3NV.js";
66
66
  import {
67
67
  DEFAULT_RIDDLE_API_BASE_URL,
68
68
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -108,7 +108,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
108
108
  "wait",
109
109
  "wait_for_selector",
110
110
  "wait_for_text",
111
- "window_call"
111
+ "window_call",
112
+ "window_call_until"
112
113
  ];
113
114
  var RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES = [
114
115
  "aborted",
@@ -498,6 +499,19 @@ function profileSetupActionCounts(results) {
498
499
  function profileSetupScreenshotLabels(results) {
499
500
  return results.filter((result) => profileSetupResultAction(result) === "screenshot" && result.ok !== false && typeof result.screenshot_label === "string").map((result) => result.screenshot_label).filter(Boolean);
500
501
  }
502
+ function profileSetupWindowCallUntilReceipts(results) {
503
+ return results.filter((result) => profileSetupResultAction(result) === "window_call_until").map((result) => ({
504
+ ordinal: result.ordinal ?? null,
505
+ ok: result.ok !== false,
506
+ path: result.path ?? null,
507
+ until_path: result.until_path ?? null,
508
+ until_value: result.until_value ?? null,
509
+ until_expected_value: result.until_expected_value ?? null,
510
+ call_count: result.call_count ?? null,
511
+ max_calls: result.max_calls ?? null,
512
+ reason: result.reason ?? result.error ?? null
513
+ }));
514
+ }
501
515
  function sampleProfileSetupSummaryItems(items, limit) {
502
516
  if (items.length <= limit) return items;
503
517
  const firstCount = Math.floor(limit / 2);
@@ -523,6 +537,9 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
523
537
  const optionalFailed = results.filter((result) => result.ok === false && result.optional === true);
524
538
  const successfulClicks = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false);
525
539
  const clickCountValues = successfulClicks.map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0).filter((value) => value !== void 0);
540
+ const windowCallUntilReceipts = profileSetupWindowCallUntilReceipts(results);
541
+ const windowCallUntilCallCounts = windowCallUntilReceipts.map((result) => typeof result.call_count === "number" && Number.isFinite(result.call_count) ? result.call_count : void 0).filter((value) => value !== void 0);
542
+ const sampledWindowCallUntilReceipts = sampleProfileSetupSummaryItems(windowCallUntilReceipts, 8);
526
543
  const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
527
544
  const clickCount = typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0;
528
545
  return {
@@ -555,6 +572,10 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
555
572
  clicked_truncated: clickedItems.length > clicked.length,
556
573
  click_count_action_total: clickCountValues.length,
557
574
  click_count_value_total: clickCountValues.reduce((sum, value) => sum + value, 0),
575
+ window_call_until_total: windowCallUntilReceipts.length,
576
+ window_call_until_call_total: windowCallUntilCallCounts.reduce((sum, value) => sum + value, 0),
577
+ window_call_until_truncated: windowCallUntilReceipts.length > sampledWindowCallUntilReceipts.length,
578
+ window_call_until: sampledWindowCallUntilReceipts,
558
579
  clicked,
559
580
  text_samples,
560
581
  failed: failed.map((result) => ({
@@ -607,7 +628,7 @@ function isSupportedCheckType(value) {
607
628
  }
608
629
  function normalizeSetupActionType(value, index) {
609
630
  const normalizedInput = String(value || "").trim().replace(/-/g, "_");
610
- const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput;
631
+ const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput;
611
632
  if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
612
633
  return normalized;
613
634
  }
@@ -738,10 +759,10 @@ function normalizeSetupAction(input, index) {
738
759
  throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
739
760
  }
740
761
  const path = stringFromOwn(input, "path", "function_path", "functionPath", "window_path", "windowPath", "state_path", "statePath");
741
- if ((type === "window_call" || type === "assert_window_value" || type === "assert_window_number") && !path) {
762
+ if ((type === "window_call" || type === "window_call_until" || type === "assert_window_value" || type === "assert_window_number") && !path) {
742
763
  throw new Error(`target.setup_actions[${index}] ${type} requires path.`);
743
764
  }
744
- const args = type === "window_call" ? normalizeSetupActionArgs(input, index) : void 0;
765
+ const args = type === "window_call" || type === "window_call_until" ? normalizeSetupActionArgs(input, index) : void 0;
745
766
  const hasExpectedValue = hasOwn(input, "expected_value") || hasOwn(input, "expectedValue") || hasOwn(input, "expected") || hasOwn(input, "expect_value") || hasOwn(input, "expectValue") || hasOwn(input, "expect");
746
767
  if (type === "assert_window_value" && !hasExpectedValue) {
747
768
  throw new Error(`target.setup_actions[${index}] ${type} requires expected_value.`);
@@ -758,6 +779,24 @@ function normalizeSetupAction(input, index) {
758
779
  }
759
780
  }
760
781
  const hasExpectedReturn = hasOwn(input, "expect_return") || hasOwn(input, "expectReturn") || hasOwn(input, "expected_return") || hasOwn(input, "expectedReturn");
782
+ const untilPath = stringFromOwn(input, "until_path", "untilPath", "until_state_path", "untilStatePath", "until_window_path", "untilWindowPath", "until");
783
+ const hasUntilExpectedValue = hasOwn(input, "until_expected_value") || hasOwn(input, "untilExpectedValue") || hasOwn(input, "until_expected") || hasOwn(input, "untilExpected") || hasOwn(input, "until_value") || hasOwn(input, "untilValue") || hasOwn(input, "expected_value") || hasOwn(input, "expectedValue") || hasOwn(input, "expected");
784
+ if (type === "window_call_until") {
785
+ if (!untilPath) {
786
+ throw new Error(`target.setup_actions[${index}] ${type} requires until_path.`);
787
+ }
788
+ if (!hasUntilExpectedValue) {
789
+ throw new Error(`target.setup_actions[${index}] ${type} requires until_expected_value.`);
790
+ }
791
+ }
792
+ const maxCalls = numberValue(valueFromOwn(input, "max_calls", "maxCalls", "max_attempts", "maxAttempts", "attempts"));
793
+ if (type === "window_call_until" && (maxCalls === void 0 || !Number.isInteger(maxCalls) || maxCalls < 1 || maxCalls > 100)) {
794
+ throw new Error(`target.setup_actions[${index}].max_calls must be an integer from 1 to 100.`);
795
+ }
796
+ const intervalMs = numberValue(valueFromOwn(input, "interval_ms", "intervalMs", "poll_ms", "pollMs", "call_interval_ms", "callIntervalMs"));
797
+ if (type === "window_call_until" && intervalMs !== void 0 && (!Number.isInteger(intervalMs) || intervalMs < 0 || intervalMs > 5e3)) {
798
+ throw new Error(`target.setup_actions[${index}].interval_ms must be an integer from 0 to 5000.`);
799
+ }
761
800
  const steps = numberValue(input.steps);
762
801
  if (type === "drag" && steps !== void 0 && (!Number.isInteger(steps) || steps < 1 || steps > 100)) {
763
802
  throw new Error(`target.setup_actions[${index}].steps must be an integer from 1 to 100.`);
@@ -784,6 +823,10 @@ function normalizeSetupAction(input, index) {
784
823
  path,
785
824
  args,
786
825
  expect_return: hasExpectedReturn ? toJsonValue(valueFromOwn(input, "expect_return", "expectReturn", "expected_return", "expectedReturn")) : void 0,
826
+ until_path: untilPath,
827
+ until_expected_value: hasUntilExpectedValue ? toJsonValue(valueFromOwn(input, "until_expected_value", "untilExpectedValue", "until_expected", "untilExpected", "until_value", "untilValue", "expected_value", "expectedValue", "expected")) : void 0,
828
+ max_calls: maxCalls,
829
+ interval_ms: intervalMs,
787
830
  expected_value: hasExpectedValue ? toJsonValue(rawExpectedValue) : void 0,
788
831
  min_value: minValue,
789
832
  max_value: maxValue,
@@ -4660,6 +4703,34 @@ async function setupReadWindowValue(context, path) {
4660
4703
  return { ok: true, value: toJsonValue(current) };
4661
4704
  }, { path });
4662
4705
  }
4706
+ async function setupCallWindowFunction(context, path, args) {
4707
+ return await context.evaluate(async ({ path, args }) => {
4708
+ const toJsonValue = (value) => {
4709
+ if (value === null || value === undefined) return null;
4710
+ if (typeof value === "string" || typeof value === "boolean") return value;
4711
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
4712
+ if (Array.isArray(value)) return value.map(toJsonValue);
4713
+ if (typeof value === "object") {
4714
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
4715
+ }
4716
+ return String(value);
4717
+ };
4718
+ const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
4719
+ let parent = window;
4720
+ let current = window;
4721
+ for (const part of pathParts) {
4722
+ parent = current;
4723
+ current = current?.[part];
4724
+ }
4725
+ if (typeof current !== "function") return { ok: false, reason: "missing_function" };
4726
+ try {
4727
+ const returned = await current.apply(parent, Array.isArray(args) ? args : []);
4728
+ return { ok: true, returned: toJsonValue(returned) };
4729
+ } catch (error) {
4730
+ return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
4731
+ }
4732
+ }, { path, args });
4733
+ }
4663
4734
  function setupFrameSelector(action) {
4664
4735
  return String(action?.frame_selector || action?.frameSelector || action?.iframe_selector || action?.iframeSelector || "").trim();
4665
4736
  }
@@ -5177,32 +5248,7 @@ async function executeSetupAction(action, ordinal, viewport) {
5177
5248
  if (!path) return { ...base, path, reason: "missing_path" };
5178
5249
  const scope = await setupActionScope(action, timeout);
5179
5250
  if (!scope.ok) return setupScopeFailure(base, scope);
5180
- const result = await scope.context.evaluate(async ({ path, args }) => {
5181
- const toJsonValue = (value) => {
5182
- if (value === null || value === undefined) return null;
5183
- if (typeof value === "string" || typeof value === "boolean") return value;
5184
- if (typeof value === "number") return Number.isFinite(value) ? value : null;
5185
- if (Array.isArray(value)) return value.map(toJsonValue);
5186
- if (typeof value === "object") {
5187
- return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
5188
- }
5189
- return String(value);
5190
- };
5191
- const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
5192
- let parent = window;
5193
- let current = window;
5194
- for (const part of pathParts) {
5195
- parent = current;
5196
- current = current?.[part];
5197
- }
5198
- if (typeof current !== "function") return { ok: false, reason: "missing_function" };
5199
- try {
5200
- const returned = await current.apply(parent, Array.isArray(args) ? args : []);
5201
- return { ok: true, returned: toJsonValue(returned) };
5202
- } catch (error) {
5203
- return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
5204
- }
5205
- }, { path, args });
5251
+ const result = await setupCallWindowFunction(scope.context, path, args);
5206
5252
  const hasExpectation = setupHasOwn(action, "expect_return")
5207
5253
  || setupHasOwn(action, "expectReturn")
5208
5254
  || setupHasOwn(action, "expected_return")
@@ -5227,6 +5273,126 @@ async function executeSetupAction(action, ordinal, viewport) {
5227
5273
  error: result.error || undefined,
5228
5274
  };
5229
5275
  }
5276
+ if (type === "window_call_until") {
5277
+ const path = String(action.path || action.function_path || action.functionPath || "");
5278
+ const untilPath = String(action.until_path || action.untilPath || action.until_state_path || action.untilStatePath || action.until_window_path || action.untilWindowPath || action.until || "");
5279
+ const args = Array.isArray(action.args) ? action.args : [];
5280
+ if (!path) return { ...base, path, reason: "missing_path" };
5281
+ if (!untilPath) return { ...base, path, reason: "missing_until_path" };
5282
+ const hasUntilExpected = setupHasOwn(action, "until_expected_value")
5283
+ || setupHasOwn(action, "untilExpectedValue")
5284
+ || setupHasOwn(action, "until_expected")
5285
+ || setupHasOwn(action, "untilExpected")
5286
+ || setupHasOwn(action, "until_value")
5287
+ || setupHasOwn(action, "untilValue")
5288
+ || setupHasOwn(action, "expected_value")
5289
+ || setupHasOwn(action, "expectedValue")
5290
+ || setupHasOwn(action, "expected");
5291
+ const untilExpected = setupHasOwn(action, "until_expected_value")
5292
+ ? action.until_expected_value
5293
+ : setupHasOwn(action, "untilExpectedValue")
5294
+ ? action.untilExpectedValue
5295
+ : setupHasOwn(action, "until_expected")
5296
+ ? action.until_expected
5297
+ : setupHasOwn(action, "untilExpected")
5298
+ ? action.untilExpected
5299
+ : setupHasOwn(action, "until_value")
5300
+ ? action.until_value
5301
+ : setupHasOwn(action, "untilValue")
5302
+ ? action.untilValue
5303
+ : setupHasOwn(action, "expected_value")
5304
+ ? action.expected_value
5305
+ : setupHasOwn(action, "expectedValue")
5306
+ ? action.expectedValue
5307
+ : action.expected;
5308
+ if (!hasUntilExpected) return { ...base, path, until_path: untilPath, reason: "missing_until_expected_value" };
5309
+ const maxCalls = Math.min(100, Math.max(1, Math.floor(setupNumber(action.max_calls ?? action.maxCalls ?? action.max_attempts ?? action.maxAttempts ?? action.attempts, 1) || 1)));
5310
+ const intervalMs = Math.min(5000, Math.max(0, Math.floor(setupNumber(action.interval_ms ?? action.intervalMs ?? action.poll_ms ?? action.pollMs ?? action.call_interval_ms ?? action.callIntervalMs, 100) || 0)));
5311
+ const hasReturnExpectation = setupHasOwn(action, "expect_return")
5312
+ || setupHasOwn(action, "expectReturn")
5313
+ || setupHasOwn(action, "expected_return")
5314
+ || setupHasOwn(action, "expectedReturn");
5315
+ const expectedReturn = setupHasOwn(action, "expect_return")
5316
+ ? action.expect_return
5317
+ : setupHasOwn(action, "expectReturn")
5318
+ ? action.expectReturn
5319
+ : setupHasOwn(action, "expected_return")
5320
+ ? action.expected_return
5321
+ : action.expectedReturn;
5322
+ const scope = await setupActionScope(action, timeout);
5323
+ if (!scope.ok) return setupScopeFailure(base, scope);
5324
+ const startedAt = Date.now();
5325
+ let callCount = 0;
5326
+ let lastCallResult = null;
5327
+ let lastPredicateResult = await setupReadWindowValue(scope.context, untilPath);
5328
+ if (lastPredicateResult.ok && setupValuesEqual(lastPredicateResult.value, untilExpected)) {
5329
+ return {
5330
+ ...base,
5331
+ ...setupScopeEvidence(scope),
5332
+ ok: true,
5333
+ path,
5334
+ arg_count: args.length,
5335
+ until_path: untilPath,
5336
+ until_value: setupJsonValue(lastPredicateResult.value),
5337
+ until_expected_value: setupJsonValue(untilExpected),
5338
+ call_count: callCount,
5339
+ max_calls: maxCalls,
5340
+ interval_ms: intervalMs,
5341
+ timeout_ms: timeout,
5342
+ };
5343
+ }
5344
+ while (callCount < maxCalls && Date.now() - startedAt <= timeout) {
5345
+ lastCallResult = await setupCallWindowFunction(scope.context, path, args);
5346
+ callCount += 1;
5347
+ if (!lastCallResult.ok) break;
5348
+ if (hasReturnExpectation && !setupValuesEqual(lastCallResult.returned, expectedReturn)) break;
5349
+ lastPredicateResult = await setupReadWindowValue(scope.context, untilPath);
5350
+ if (lastPredicateResult.ok && setupValuesEqual(lastPredicateResult.value, untilExpected)) {
5351
+ return {
5352
+ ...base,
5353
+ ...setupScopeEvidence(scope),
5354
+ ok: true,
5355
+ path,
5356
+ arg_count: args.length,
5357
+ returned: setupJsonValue(lastCallResult.returned),
5358
+ expected_return: hasReturnExpectation ? setupJsonValue(expectedReturn) : undefined,
5359
+ until_path: untilPath,
5360
+ until_value: setupJsonValue(lastPredicateResult.value),
5361
+ until_expected_value: setupJsonValue(untilExpected),
5362
+ call_count: callCount,
5363
+ max_calls: maxCalls,
5364
+ interval_ms: intervalMs,
5365
+ timeout_ms: timeout,
5366
+ };
5367
+ }
5368
+ if (callCount < maxCalls && intervalMs) await page.waitForTimeout(intervalMs);
5369
+ }
5370
+ const returnExpectationMet = !hasReturnExpectation || setupValuesEqual(lastCallResult?.returned, expectedReturn);
5371
+ return {
5372
+ ...base,
5373
+ ...setupScopeEvidence(scope),
5374
+ path,
5375
+ arg_count: args.length,
5376
+ returned: setupJsonValue(lastCallResult?.returned),
5377
+ expected_return: hasReturnExpectation ? setupJsonValue(expectedReturn) : undefined,
5378
+ until_path: untilPath,
5379
+ until_value: setupJsonValue(lastPredicateResult?.value),
5380
+ until_expected_value: setupJsonValue(untilExpected),
5381
+ call_count: callCount,
5382
+ max_calls: maxCalls,
5383
+ interval_ms: intervalMs,
5384
+ timeout_ms: timeout,
5385
+ reason: lastCallResult && !lastCallResult.ok
5386
+ ? lastCallResult.reason
5387
+ : hasReturnExpectation && !returnExpectationMet
5388
+ ? "unexpected_return_value"
5389
+ : Date.now() - startedAt > timeout
5390
+ ? "timeout"
5391
+ : "until_condition_not_met",
5392
+ error: lastCallResult?.error || undefined,
5393
+ missing_part: lastPredicateResult?.missing_part || undefined,
5394
+ };
5395
+ }
5230
5396
  if (type === "assert_window_value") {
5231
5397
  const path = String(action.path || action.window_path || action.windowPath || "");
5232
5398
  const hasExpected = setupHasOwn(action, "expected_value")
@@ -5,7 +5,7 @@ declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evide
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
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", "dialog_count_equals", "dialog_accept_count_equals", "dialog_dismiss_count_equals", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors", "no_console_warnings"];
8
- declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "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", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call"];
8
+ declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "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", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call", "window_call_until"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
11
11
  type RiddleProofProfileSetupActionType = typeof RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES[number];
@@ -129,6 +129,10 @@ interface RiddleProofProfileSetupAction {
129
129
  path?: string;
130
130
  args?: JsonValue[];
131
131
  expect_return?: JsonValue;
132
+ until_path?: string;
133
+ until_expected_value?: JsonValue;
134
+ max_calls?: number;
135
+ interval_ms?: number;
132
136
  expected_value?: JsonValue;
133
137
  min_value?: number;
134
138
  max_value?: number;