@riddledc/riddle-proof 0.7.49 → 0.7.51

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
@@ -239,15 +239,18 @@ appears only after a picker, tab, login stub, storage seed, form fill,
239
239
  transport control, or other bounded interaction. Supported setup actions are
240
240
  `click`, `fill`, `set_input_value`, `assert_text_visible`,
241
241
  `assert_text_absent`, `assert_selector_count`, `local_storage`,
242
- `session_storage`, `clear_storage`, `wait`, `wait_for_selector`, and
243
- `wait_for_text`; a failed setup action is recorded as a failed
242
+ `session_storage`, `clear_storage`, `wait`, `wait_for_selector`,
243
+ `wait_for_text`, and `window_call`; a failed setup action is recorded as a failed
244
244
  `setup_actions_succeeded` check so the profile cannot pass without reaching
245
245
  the intended state. Text-matched `click` actions prefer visible matching
246
246
  elements, which keeps responsive layouts from selecting hidden desktop or
247
- mobile-only links. Use setup assertions when the pre-click or pre-navigation
248
- state is part of the contract, for example a fresh row must be present, stale
249
- copy must be absent, or exactly one source link must exist before clicking into
250
- the final route. `assert_selector_count` accepts `expected_count`.
247
+ mobile-only links. Add `force: true` to a click action only when the matched
248
+ visible element is intentionally animated or otherwise never becomes stable
249
+ enough for Playwright's default click actionability checks. Use setup
250
+ assertions when the pre-click or pre-navigation state is part of the contract,
251
+ for example a fresh row must be present, stale copy must be absent, or exactly
252
+ one source link must exist before clicking into the final route.
253
+ `assert_selector_count` accepts `expected_count`.
251
254
  `local_storage` and `session_storage` accept a `key` plus string `value` or
252
255
  JSON `json` / `value_json`, and can reload the page with `reload: true`.
253
256
  `clear_storage` clears `local`, `session`, or `both` browser storage scopes,
@@ -40,7 +40,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
40
40
  "clear_storage",
41
41
  "wait",
42
42
  "wait_for_selector",
43
- "wait_for_text"
43
+ "wait_for_text",
44
+ "window_call"
44
45
  ];
45
46
  var DEFAULT_VIEWPORTS = [
46
47
  { name: "desktop", width: 1280, height: 800 }
@@ -63,6 +64,12 @@ function stringFromOwn(input, ...keys) {
63
64
  }
64
65
  return void 0;
65
66
  }
67
+ function valueFromOwn(input, ...keys) {
68
+ for (const key of keys) {
69
+ if (hasOwn(input, key)) return input[key];
70
+ }
71
+ return void 0;
72
+ }
66
73
  function numberValue(value) {
67
74
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
68
75
  }
@@ -204,6 +211,14 @@ function normalizeSetupActionStorage(value, index) {
204
211
  if (normalized === "both" || normalized === "all") return "both";
205
212
  throw new Error(`target.setup_actions[${index}].storage ${String(value)} is not supported. Supported storage values: local, session, both.`);
206
213
  }
214
+ function normalizeSetupActionArgs(input, index) {
215
+ const argsInput = valueFromOwn(input, "args", "arguments", "args_json", "argsJson");
216
+ if (argsInput === void 0) return void 0;
217
+ if (!Array.isArray(argsInput)) {
218
+ throw new Error(`target.setup_actions[${index}] window_call args must be an array.`);
219
+ }
220
+ return argsInput.map(toJsonValue);
221
+ }
207
222
  function normalizeSetupAction(input, index) {
208
223
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
209
224
  const type = normalizeSetupActionType(stringValue(input.type), index);
@@ -233,12 +248,22 @@ function normalizeSetupAction(input, index) {
233
248
  if ((type === "local_storage" || type === "session_storage") && value === void 0 && !hasJsonValue) {
234
249
  throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
235
250
  }
251
+ const path = stringFromOwn(input, "path", "function_path", "functionPath", "window_path", "windowPath");
252
+ if (type === "window_call" && !path) {
253
+ throw new Error(`target.setup_actions[${index}] ${type} requires path.`);
254
+ }
255
+ const args = type === "window_call" ? normalizeSetupActionArgs(input, index) : void 0;
256
+ const hasExpectedReturn = hasOwn(input, "expect_return") || hasOwn(input, "expectReturn") || hasOwn(input, "expected_return") || hasOwn(input, "expectedReturn");
236
257
  return {
237
258
  type,
238
259
  selector,
260
+ force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
239
261
  key,
240
262
  value,
241
263
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
264
+ path,
265
+ args,
266
+ expect_return: hasExpectedReturn ? toJsonValue(valueFromOwn(input, "expect_return", "expectReturn", "expected_return", "expectedReturn")) : void 0,
242
267
  text: stringValue(input.text),
243
268
  pattern: stringValue(input.pattern),
244
269
  flags: stringValue(input.flags),
@@ -1939,6 +1964,19 @@ function setupActionValue(action) {
1939
1964
  if (setupHasOwn(action, "value")) return String(action.value ?? "");
1940
1965
  return "";
1941
1966
  }
1967
+ function setupJsonValue(value) {
1968
+ if (value === null || value === undefined) return null;
1969
+ if (typeof value === "string" || typeof value === "boolean") return value;
1970
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
1971
+ if (Array.isArray(value)) return value.map(setupJsonValue);
1972
+ if (typeof value === "object") {
1973
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, setupJsonValue(child)]));
1974
+ }
1975
+ return String(value);
1976
+ }
1977
+ function setupValuesEqual(left, right) {
1978
+ return JSON.stringify(setupJsonValue(left)) === JSON.stringify(setupJsonValue(right));
1979
+ }
1942
1980
  async function setupLocatorText(locator, index) {
1943
1981
  return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
1944
1982
  }
@@ -2134,6 +2172,59 @@ async function executeSetupAction(action, ordinal) {
2134
2172
  }
2135
2173
  return { ...base, ok: true, storage, reload: action.reload === true };
2136
2174
  }
2175
+ if (type === "window_call") {
2176
+ const path = String(action.path || action.function_path || action.functionPath || "");
2177
+ const args = Array.isArray(action.args) ? action.args : [];
2178
+ if (!path) return { ...base, path, reason: "missing_path" };
2179
+ const result = await page.evaluate(async ({ path, args }) => {
2180
+ const toJsonValue = (value) => {
2181
+ if (value === null || value === undefined) return null;
2182
+ if (typeof value === "string" || typeof value === "boolean") return value;
2183
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
2184
+ if (Array.isArray(value)) return value.map(toJsonValue);
2185
+ if (typeof value === "object") {
2186
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
2187
+ }
2188
+ return String(value);
2189
+ };
2190
+ const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
2191
+ let parent = window;
2192
+ let current = window;
2193
+ for (const part of pathParts) {
2194
+ parent = current;
2195
+ current = current?.[part];
2196
+ }
2197
+ if (typeof current !== "function") return { ok: false, reason: "missing_function" };
2198
+ try {
2199
+ const returned = await current.apply(parent, Array.isArray(args) ? args : []);
2200
+ return { ok: true, returned: toJsonValue(returned) };
2201
+ } catch (error) {
2202
+ return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
2203
+ }
2204
+ }, { path, args });
2205
+ const hasExpectation = setupHasOwn(action, "expect_return")
2206
+ || setupHasOwn(action, "expectReturn")
2207
+ || setupHasOwn(action, "expected_return")
2208
+ || setupHasOwn(action, "expectedReturn");
2209
+ const expected = setupHasOwn(action, "expect_return")
2210
+ ? action.expect_return
2211
+ : setupHasOwn(action, "expectReturn")
2212
+ ? action.expectReturn
2213
+ : setupHasOwn(action, "expected_return")
2214
+ ? action.expected_return
2215
+ : action.expectedReturn;
2216
+ const expectationMet = !hasExpectation || setupValuesEqual(result.returned, expected);
2217
+ return {
2218
+ ...base,
2219
+ ok: Boolean(result.ok && expectationMet),
2220
+ path,
2221
+ arg_count: args.length,
2222
+ returned: setupJsonValue(result.returned),
2223
+ expected_return: hasExpectation ? setupJsonValue(expected) : undefined,
2224
+ reason: result.ok ? (expectationMet ? undefined : "unexpected_return_value") : result.reason,
2225
+ error: result.error || undefined,
2226
+ };
2227
+ }
2137
2228
  if (type === "click") {
2138
2229
  const locator = page.locator(action.selector);
2139
2230
  const count = await locator.count();
@@ -2163,8 +2254,11 @@ async function executeSetupAction(action, ordinal) {
2163
2254
  if (targetIndex < 0) return { ...base, reason: "text_not_found", count };
2164
2255
  }
2165
2256
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
2166
- await locator.nth(targetIndex).click({ timeout, noWaitAfter: true });
2167
- return { ...base, ok: true, count, target_index: targetIndex, text: matchedText };
2257
+ const clickOptions = action.force === true
2258
+ ? { timeout, noWaitAfter: true, force: true }
2259
+ : { timeout, noWaitAfter: true };
2260
+ await locator.nth(targetIndex).click(clickOptions);
2261
+ return { ...base, ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
2168
2262
  }
2169
2263
  if (type === "fill" || type === "set_input_value") {
2170
2264
  const locator = page.locator(action.selector);
package/dist/cli.cjs CHANGED
@@ -6913,7 +6913,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
6913
6913
  "clear_storage",
6914
6914
  "wait",
6915
6915
  "wait_for_selector",
6916
- "wait_for_text"
6916
+ "wait_for_text",
6917
+ "window_call"
6917
6918
  ];
6918
6919
  var DEFAULT_VIEWPORTS = [
6919
6920
  { name: "desktop", width: 1280, height: 800 }
@@ -6936,6 +6937,12 @@ function stringFromOwn(input, ...keys) {
6936
6937
  }
6937
6938
  return void 0;
6938
6939
  }
6940
+ function valueFromOwn(input, ...keys) {
6941
+ for (const key of keys) {
6942
+ if (hasOwn(input, key)) return input[key];
6943
+ }
6944
+ return void 0;
6945
+ }
6939
6946
  function numberValue(value) {
6940
6947
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
6941
6948
  }
@@ -7077,6 +7084,14 @@ function normalizeSetupActionStorage(value, index) {
7077
7084
  if (normalized === "both" || normalized === "all") return "both";
7078
7085
  throw new Error(`target.setup_actions[${index}].storage ${String(value)} is not supported. Supported storage values: local, session, both.`);
7079
7086
  }
7087
+ function normalizeSetupActionArgs(input, index) {
7088
+ const argsInput = valueFromOwn(input, "args", "arguments", "args_json", "argsJson");
7089
+ if (argsInput === void 0) return void 0;
7090
+ if (!Array.isArray(argsInput)) {
7091
+ throw new Error(`target.setup_actions[${index}] window_call args must be an array.`);
7092
+ }
7093
+ return argsInput.map(toJsonValue);
7094
+ }
7080
7095
  function normalizeSetupAction(input, index) {
7081
7096
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
7082
7097
  const type = normalizeSetupActionType(stringValue2(input.type), index);
@@ -7106,12 +7121,22 @@ function normalizeSetupAction(input, index) {
7106
7121
  if ((type === "local_storage" || type === "session_storage") && value === void 0 && !hasJsonValue) {
7107
7122
  throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
7108
7123
  }
7124
+ const path7 = stringFromOwn(input, "path", "function_path", "functionPath", "window_path", "windowPath");
7125
+ if (type === "window_call" && !path7) {
7126
+ throw new Error(`target.setup_actions[${index}] ${type} requires path.`);
7127
+ }
7128
+ const args = type === "window_call" ? normalizeSetupActionArgs(input, index) : void 0;
7129
+ const hasExpectedReturn = hasOwn(input, "expect_return") || hasOwn(input, "expectReturn") || hasOwn(input, "expected_return") || hasOwn(input, "expectedReturn");
7109
7130
  return {
7110
7131
  type,
7111
7132
  selector,
7133
+ force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
7112
7134
  key,
7113
7135
  value,
7114
7136
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
7137
+ path: path7,
7138
+ args,
7139
+ expect_return: hasExpectedReturn ? toJsonValue(valueFromOwn(input, "expect_return", "expectReturn", "expected_return", "expectedReturn")) : void 0,
7115
7140
  text: stringValue2(input.text),
7116
7141
  pattern: stringValue2(input.pattern),
7117
7142
  flags: stringValue2(input.flags),
@@ -8796,6 +8821,19 @@ function setupActionValue(action) {
8796
8821
  if (setupHasOwn(action, "value")) return String(action.value ?? "");
8797
8822
  return "";
8798
8823
  }
8824
+ function setupJsonValue(value) {
8825
+ if (value === null || value === undefined) return null;
8826
+ if (typeof value === "string" || typeof value === "boolean") return value;
8827
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
8828
+ if (Array.isArray(value)) return value.map(setupJsonValue);
8829
+ if (typeof value === "object") {
8830
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, setupJsonValue(child)]));
8831
+ }
8832
+ return String(value);
8833
+ }
8834
+ function setupValuesEqual(left, right) {
8835
+ return JSON.stringify(setupJsonValue(left)) === JSON.stringify(setupJsonValue(right));
8836
+ }
8799
8837
  async function setupLocatorText(locator, index) {
8800
8838
  return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
8801
8839
  }
@@ -8991,6 +9029,59 @@ async function executeSetupAction(action, ordinal) {
8991
9029
  }
8992
9030
  return { ...base, ok: true, storage, reload: action.reload === true };
8993
9031
  }
9032
+ if (type === "window_call") {
9033
+ const path = String(action.path || action.function_path || action.functionPath || "");
9034
+ const args = Array.isArray(action.args) ? action.args : [];
9035
+ if (!path) return { ...base, path, reason: "missing_path" };
9036
+ const result = await page.evaluate(async ({ path, args }) => {
9037
+ const toJsonValue = (value) => {
9038
+ if (value === null || value === undefined) return null;
9039
+ if (typeof value === "string" || typeof value === "boolean") return value;
9040
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
9041
+ if (Array.isArray(value)) return value.map(toJsonValue);
9042
+ if (typeof value === "object") {
9043
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
9044
+ }
9045
+ return String(value);
9046
+ };
9047
+ const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
9048
+ let parent = window;
9049
+ let current = window;
9050
+ for (const part of pathParts) {
9051
+ parent = current;
9052
+ current = current?.[part];
9053
+ }
9054
+ if (typeof current !== "function") return { ok: false, reason: "missing_function" };
9055
+ try {
9056
+ const returned = await current.apply(parent, Array.isArray(args) ? args : []);
9057
+ return { ok: true, returned: toJsonValue(returned) };
9058
+ } catch (error) {
9059
+ return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
9060
+ }
9061
+ }, { path, args });
9062
+ const hasExpectation = setupHasOwn(action, "expect_return")
9063
+ || setupHasOwn(action, "expectReturn")
9064
+ || setupHasOwn(action, "expected_return")
9065
+ || setupHasOwn(action, "expectedReturn");
9066
+ const expected = setupHasOwn(action, "expect_return")
9067
+ ? action.expect_return
9068
+ : setupHasOwn(action, "expectReturn")
9069
+ ? action.expectReturn
9070
+ : setupHasOwn(action, "expected_return")
9071
+ ? action.expected_return
9072
+ : action.expectedReturn;
9073
+ const expectationMet = !hasExpectation || setupValuesEqual(result.returned, expected);
9074
+ return {
9075
+ ...base,
9076
+ ok: Boolean(result.ok && expectationMet),
9077
+ path,
9078
+ arg_count: args.length,
9079
+ returned: setupJsonValue(result.returned),
9080
+ expected_return: hasExpectation ? setupJsonValue(expected) : undefined,
9081
+ reason: result.ok ? (expectationMet ? undefined : "unexpected_return_value") : result.reason,
9082
+ error: result.error || undefined,
9083
+ };
9084
+ }
8994
9085
  if (type === "click") {
8995
9086
  const locator = page.locator(action.selector);
8996
9087
  const count = await locator.count();
@@ -9020,8 +9111,11 @@ async function executeSetupAction(action, ordinal) {
9020
9111
  if (targetIndex < 0) return { ...base, reason: "text_not_found", count };
9021
9112
  }
9022
9113
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
9023
- await locator.nth(targetIndex).click({ timeout, noWaitAfter: true });
9024
- return { ...base, ok: true, count, target_index: targetIndex, text: matchedText };
9114
+ const clickOptions = action.force === true
9115
+ ? { timeout, noWaitAfter: true, force: true }
9116
+ : { timeout, noWaitAfter: true };
9117
+ await locator.nth(targetIndex).click(clickOptions);
9118
+ return { ...base, ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
9025
9119
  }
9026
9120
  if (type === "fill" || type === "set_input_value") {
9027
9121
  const locator = page.locator(action.selector);
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-Q3X6UTPP.js";
13
+ } from "./chunk-ZC4C52ZC.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
package/dist/index.cjs CHANGED
@@ -8754,7 +8754,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
8754
8754
  "clear_storage",
8755
8755
  "wait",
8756
8756
  "wait_for_selector",
8757
- "wait_for_text"
8757
+ "wait_for_text",
8758
+ "window_call"
8758
8759
  ];
8759
8760
  var DEFAULT_VIEWPORTS = [
8760
8761
  { name: "desktop", width: 1280, height: 800 }
@@ -8777,6 +8778,12 @@ function stringFromOwn(input, ...keys) {
8777
8778
  }
8778
8779
  return void 0;
8779
8780
  }
8781
+ function valueFromOwn(input, ...keys) {
8782
+ for (const key of keys) {
8783
+ if (hasOwn(input, key)) return input[key];
8784
+ }
8785
+ return void 0;
8786
+ }
8780
8787
  function numberValue3(value) {
8781
8788
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8782
8789
  }
@@ -8918,6 +8925,14 @@ function normalizeSetupActionStorage(value, index) {
8918
8925
  if (normalized === "both" || normalized === "all") return "both";
8919
8926
  throw new Error(`target.setup_actions[${index}].storage ${String(value)} is not supported. Supported storage values: local, session, both.`);
8920
8927
  }
8928
+ function normalizeSetupActionArgs(input, index) {
8929
+ const argsInput = valueFromOwn(input, "args", "arguments", "args_json", "argsJson");
8930
+ if (argsInput === void 0) return void 0;
8931
+ if (!Array.isArray(argsInput)) {
8932
+ throw new Error(`target.setup_actions[${index}] window_call args must be an array.`);
8933
+ }
8934
+ return argsInput.map(toJsonValue);
8935
+ }
8921
8936
  function normalizeSetupAction(input, index) {
8922
8937
  if (!isRecord2(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
8923
8938
  const type = normalizeSetupActionType(stringValue5(input.type), index);
@@ -8947,12 +8962,22 @@ function normalizeSetupAction(input, index) {
8947
8962
  if ((type === "local_storage" || type === "session_storage") && value === void 0 && !hasJsonValue) {
8948
8963
  throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
8949
8964
  }
8965
+ const path6 = stringFromOwn(input, "path", "function_path", "functionPath", "window_path", "windowPath");
8966
+ if (type === "window_call" && !path6) {
8967
+ throw new Error(`target.setup_actions[${index}] ${type} requires path.`);
8968
+ }
8969
+ const args = type === "window_call" ? normalizeSetupActionArgs(input, index) : void 0;
8970
+ const hasExpectedReturn = hasOwn(input, "expect_return") || hasOwn(input, "expectReturn") || hasOwn(input, "expected_return") || hasOwn(input, "expectedReturn");
8950
8971
  return {
8951
8972
  type,
8952
8973
  selector,
8974
+ force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
8953
8975
  key,
8954
8976
  value,
8955
8977
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
8978
+ path: path6,
8979
+ args,
8980
+ expect_return: hasExpectedReturn ? toJsonValue(valueFromOwn(input, "expect_return", "expectReturn", "expected_return", "expectedReturn")) : void 0,
8956
8981
  text: stringValue5(input.text),
8957
8982
  pattern: stringValue5(input.pattern),
8958
8983
  flags: stringValue5(input.flags),
@@ -10653,6 +10678,19 @@ function setupActionValue(action) {
10653
10678
  if (setupHasOwn(action, "value")) return String(action.value ?? "");
10654
10679
  return "";
10655
10680
  }
10681
+ function setupJsonValue(value) {
10682
+ if (value === null || value === undefined) return null;
10683
+ if (typeof value === "string" || typeof value === "boolean") return value;
10684
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
10685
+ if (Array.isArray(value)) return value.map(setupJsonValue);
10686
+ if (typeof value === "object") {
10687
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, setupJsonValue(child)]));
10688
+ }
10689
+ return String(value);
10690
+ }
10691
+ function setupValuesEqual(left, right) {
10692
+ return JSON.stringify(setupJsonValue(left)) === JSON.stringify(setupJsonValue(right));
10693
+ }
10656
10694
  async function setupLocatorText(locator, index) {
10657
10695
  return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
10658
10696
  }
@@ -10848,6 +10886,59 @@ async function executeSetupAction(action, ordinal) {
10848
10886
  }
10849
10887
  return { ...base, ok: true, storage, reload: action.reload === true };
10850
10888
  }
10889
+ if (type === "window_call") {
10890
+ const path = String(action.path || action.function_path || action.functionPath || "");
10891
+ const args = Array.isArray(action.args) ? action.args : [];
10892
+ if (!path) return { ...base, path, reason: "missing_path" };
10893
+ const result = await page.evaluate(async ({ path, args }) => {
10894
+ const toJsonValue = (value) => {
10895
+ if (value === null || value === undefined) return null;
10896
+ if (typeof value === "string" || typeof value === "boolean") return value;
10897
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
10898
+ if (Array.isArray(value)) return value.map(toJsonValue);
10899
+ if (typeof value === "object") {
10900
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
10901
+ }
10902
+ return String(value);
10903
+ };
10904
+ const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
10905
+ let parent = window;
10906
+ let current = window;
10907
+ for (const part of pathParts) {
10908
+ parent = current;
10909
+ current = current?.[part];
10910
+ }
10911
+ if (typeof current !== "function") return { ok: false, reason: "missing_function" };
10912
+ try {
10913
+ const returned = await current.apply(parent, Array.isArray(args) ? args : []);
10914
+ return { ok: true, returned: toJsonValue(returned) };
10915
+ } catch (error) {
10916
+ return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
10917
+ }
10918
+ }, { path, args });
10919
+ const hasExpectation = setupHasOwn(action, "expect_return")
10920
+ || setupHasOwn(action, "expectReturn")
10921
+ || setupHasOwn(action, "expected_return")
10922
+ || setupHasOwn(action, "expectedReturn");
10923
+ const expected = setupHasOwn(action, "expect_return")
10924
+ ? action.expect_return
10925
+ : setupHasOwn(action, "expectReturn")
10926
+ ? action.expectReturn
10927
+ : setupHasOwn(action, "expected_return")
10928
+ ? action.expected_return
10929
+ : action.expectedReturn;
10930
+ const expectationMet = !hasExpectation || setupValuesEqual(result.returned, expected);
10931
+ return {
10932
+ ...base,
10933
+ ok: Boolean(result.ok && expectationMet),
10934
+ path,
10935
+ arg_count: args.length,
10936
+ returned: setupJsonValue(result.returned),
10937
+ expected_return: hasExpectation ? setupJsonValue(expected) : undefined,
10938
+ reason: result.ok ? (expectationMet ? undefined : "unexpected_return_value") : result.reason,
10939
+ error: result.error || undefined,
10940
+ };
10941
+ }
10851
10942
  if (type === "click") {
10852
10943
  const locator = page.locator(action.selector);
10853
10944
  const count = await locator.count();
@@ -10877,8 +10968,11 @@ async function executeSetupAction(action, ordinal) {
10877
10968
  if (targetIndex < 0) return { ...base, reason: "text_not_found", count };
10878
10969
  }
10879
10970
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
10880
- await locator.nth(targetIndex).click({ timeout, noWaitAfter: true });
10881
- return { ...base, ok: true, count, target_index: targetIndex, text: matchedText };
10971
+ const clickOptions = action.force === true
10972
+ ? { timeout, noWaitAfter: true, force: true }
10973
+ : { timeout, noWaitAfter: true };
10974
+ await locator.nth(targetIndex).click(clickOptions);
10975
+ return { ...base, ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
10882
10976
  }
10883
10977
  if (type === "fill" || type === "set_input_value") {
10884
10978
  const locator = page.locator(action.selector);
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-Q3X6UTPP.js";
61
+ } from "./chunk-ZC4C52ZC.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -83,7 +83,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
83
83
  "clear_storage",
84
84
  "wait",
85
85
  "wait_for_selector",
86
- "wait_for_text"
86
+ "wait_for_text",
87
+ "window_call"
87
88
  ];
88
89
  var DEFAULT_VIEWPORTS = [
89
90
  { name: "desktop", width: 1280, height: 800 }
@@ -106,6 +107,12 @@ function stringFromOwn(input, ...keys) {
106
107
  }
107
108
  return void 0;
108
109
  }
110
+ function valueFromOwn(input, ...keys) {
111
+ for (const key of keys) {
112
+ if (hasOwn(input, key)) return input[key];
113
+ }
114
+ return void 0;
115
+ }
109
116
  function numberValue(value) {
110
117
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
111
118
  }
@@ -247,6 +254,14 @@ function normalizeSetupActionStorage(value, index) {
247
254
  if (normalized === "both" || normalized === "all") return "both";
248
255
  throw new Error(`target.setup_actions[${index}].storage ${String(value)} is not supported. Supported storage values: local, session, both.`);
249
256
  }
257
+ function normalizeSetupActionArgs(input, index) {
258
+ const argsInput = valueFromOwn(input, "args", "arguments", "args_json", "argsJson");
259
+ if (argsInput === void 0) return void 0;
260
+ if (!Array.isArray(argsInput)) {
261
+ throw new Error(`target.setup_actions[${index}] window_call args must be an array.`);
262
+ }
263
+ return argsInput.map(toJsonValue);
264
+ }
250
265
  function normalizeSetupAction(input, index) {
251
266
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
252
267
  const type = normalizeSetupActionType(stringValue(input.type), index);
@@ -276,12 +291,22 @@ function normalizeSetupAction(input, index) {
276
291
  if ((type === "local_storage" || type === "session_storage") && value === void 0 && !hasJsonValue) {
277
292
  throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
278
293
  }
294
+ const path = stringFromOwn(input, "path", "function_path", "functionPath", "window_path", "windowPath");
295
+ if (type === "window_call" && !path) {
296
+ throw new Error(`target.setup_actions[${index}] ${type} requires path.`);
297
+ }
298
+ const args = type === "window_call" ? normalizeSetupActionArgs(input, index) : void 0;
299
+ const hasExpectedReturn = hasOwn(input, "expect_return") || hasOwn(input, "expectReturn") || hasOwn(input, "expected_return") || hasOwn(input, "expectedReturn");
279
300
  return {
280
301
  type,
281
302
  selector,
303
+ force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
282
304
  key,
283
305
  value,
284
306
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
307
+ path,
308
+ args,
309
+ expect_return: hasExpectedReturn ? toJsonValue(valueFromOwn(input, "expect_return", "expectReturn", "expected_return", "expectedReturn")) : void 0,
285
310
  text: stringValue(input.text),
286
311
  pattern: stringValue(input.pattern),
287
312
  flags: stringValue(input.flags),
@@ -1982,6 +2007,19 @@ function setupActionValue(action) {
1982
2007
  if (setupHasOwn(action, "value")) return String(action.value ?? "");
1983
2008
  return "";
1984
2009
  }
2010
+ function setupJsonValue(value) {
2011
+ if (value === null || value === undefined) return null;
2012
+ if (typeof value === "string" || typeof value === "boolean") return value;
2013
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
2014
+ if (Array.isArray(value)) return value.map(setupJsonValue);
2015
+ if (typeof value === "object") {
2016
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, setupJsonValue(child)]));
2017
+ }
2018
+ return String(value);
2019
+ }
2020
+ function setupValuesEqual(left, right) {
2021
+ return JSON.stringify(setupJsonValue(left)) === JSON.stringify(setupJsonValue(right));
2022
+ }
1985
2023
  async function setupLocatorText(locator, index) {
1986
2024
  return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
1987
2025
  }
@@ -2177,6 +2215,59 @@ async function executeSetupAction(action, ordinal) {
2177
2215
  }
2178
2216
  return { ...base, ok: true, storage, reload: action.reload === true };
2179
2217
  }
2218
+ if (type === "window_call") {
2219
+ const path = String(action.path || action.function_path || action.functionPath || "");
2220
+ const args = Array.isArray(action.args) ? action.args : [];
2221
+ if (!path) return { ...base, path, reason: "missing_path" };
2222
+ const result = await page.evaluate(async ({ path, args }) => {
2223
+ const toJsonValue = (value) => {
2224
+ if (value === null || value === undefined) return null;
2225
+ if (typeof value === "string" || typeof value === "boolean") return value;
2226
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
2227
+ if (Array.isArray(value)) return value.map(toJsonValue);
2228
+ if (typeof value === "object") {
2229
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
2230
+ }
2231
+ return String(value);
2232
+ };
2233
+ const pathParts = String(path || "").split(".").map((part) => part.trim()).filter(Boolean);
2234
+ let parent = window;
2235
+ let current = window;
2236
+ for (const part of pathParts) {
2237
+ parent = current;
2238
+ current = current?.[part];
2239
+ }
2240
+ if (typeof current !== "function") return { ok: false, reason: "missing_function" };
2241
+ try {
2242
+ const returned = await current.apply(parent, Array.isArray(args) ? args : []);
2243
+ return { ok: true, returned: toJsonValue(returned) };
2244
+ } catch (error) {
2245
+ return { ok: false, reason: "function_threw", error: String(error && error.message ? error.message : error).slice(0, 1000) };
2246
+ }
2247
+ }, { path, args });
2248
+ const hasExpectation = setupHasOwn(action, "expect_return")
2249
+ || setupHasOwn(action, "expectReturn")
2250
+ || setupHasOwn(action, "expected_return")
2251
+ || setupHasOwn(action, "expectedReturn");
2252
+ const expected = setupHasOwn(action, "expect_return")
2253
+ ? action.expect_return
2254
+ : setupHasOwn(action, "expectReturn")
2255
+ ? action.expectReturn
2256
+ : setupHasOwn(action, "expected_return")
2257
+ ? action.expected_return
2258
+ : action.expectedReturn;
2259
+ const expectationMet = !hasExpectation || setupValuesEqual(result.returned, expected);
2260
+ return {
2261
+ ...base,
2262
+ ok: Boolean(result.ok && expectationMet),
2263
+ path,
2264
+ arg_count: args.length,
2265
+ returned: setupJsonValue(result.returned),
2266
+ expected_return: hasExpectation ? setupJsonValue(expected) : undefined,
2267
+ reason: result.ok ? (expectationMet ? undefined : "unexpected_return_value") : result.reason,
2268
+ error: result.error || undefined,
2269
+ };
2270
+ }
2180
2271
  if (type === "click") {
2181
2272
  const locator = page.locator(action.selector);
2182
2273
  const count = await locator.count();
@@ -2206,8 +2297,11 @@ async function executeSetupAction(action, ordinal) {
2206
2297
  if (targetIndex < 0) return { ...base, reason: "text_not_found", count };
2207
2298
  }
2208
2299
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
2209
- await locator.nth(targetIndex).click({ timeout, noWaitAfter: true });
2210
- return { ...base, ok: true, count, target_index: targetIndex, text: matchedText };
2300
+ const clickOptions = action.force === true
2301
+ ? { timeout, noWaitAfter: true, force: true }
2302
+ : { timeout, noWaitAfter: true };
2303
+ await locator.nth(targetIndex).click(clickOptions);
2304
+ return { ...base, ok: true, count, target_index: targetIndex, text: matchedText, force: action.force === true || undefined };
2211
2305
  }
2212
2306
  if (type === "fill" || type === "set_input_value") {
2213
2307
  const locator = page.locator(action.selector);
@@ -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", "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
- declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
8
+ declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text", "window_call"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
11
11
  type RiddleProofProfileSetupActionType = typeof RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES[number];
@@ -20,9 +20,13 @@ interface RiddleProofProfileViewport {
20
20
  interface RiddleProofProfileSetupAction {
21
21
  type: RiddleProofProfileSetupActionType;
22
22
  selector?: string;
23
+ force?: boolean;
23
24
  key?: string;
24
25
  value?: string;
25
26
  value_json?: JsonValue;
27
+ path?: string;
28
+ args?: JsonValue[];
29
+ expect_return?: JsonValue;
26
30
  text?: string;
27
31
  pattern?: string;
28
32
  flags?: string;
package/dist/profile.d.ts CHANGED
@@ -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", "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
- declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text"];
8
+ declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "local_storage", "session_storage", "clear_storage", "wait", "wait_for_selector", "wait_for_text", "window_call"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
11
11
  type RiddleProofProfileSetupActionType = typeof RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES[number];
@@ -20,9 +20,13 @@ interface RiddleProofProfileViewport {
20
20
  interface RiddleProofProfileSetupAction {
21
21
  type: RiddleProofProfileSetupActionType;
22
22
  selector?: string;
23
+ force?: boolean;
23
24
  key?: string;
24
25
  value?: string;
25
26
  value_json?: JsonValue;
27
+ path?: string;
28
+ args?: JsonValue[];
29
+ expect_return?: JsonValue;
26
30
  text?: string;
27
31
  pattern?: string;
28
32
  flags?: string;
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-Q3X6UTPP.js";
22
+ } from "./chunk-ZC4C52ZC.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
295
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
385
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
662
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
295
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
385
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
662
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.49",
3
+ "version": "0.7.51",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",