@riddledc/riddle-proof 0.7.171 → 0.7.172

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
@@ -398,7 +398,7 @@ when body matching overrides sequence order.
398
398
  appears only after a picker, tab, login stub, storage seed, form fill,
399
399
  transport control, or other bounded interaction. Supported setup actions are
400
400
  `click`, `drag`, `press`, `fill`, `set_input_value`, `set_range_value`,
401
- `canvas_signature`, `assert_text_visible`, `assert_text_absent`,
401
+ `deterministic_runtime`, `canvas_signature`, `assert_text_visible`, `assert_text_absent`,
402
402
  `assert_selector_count`, `assert_window_value`, `assert_window_number`,
403
403
  `local_storage`, `session_storage`, `clear_storage`, `clear_console`,
404
404
  `screenshot`, `wait`, `wait_for_selector`, `wait_for_text`, `window_eval`,
@@ -423,6 +423,14 @@ events, and records the requested value plus the browser's actual normalized
423
423
  value, numeric value, `min`, `max`, and `step`. The action is intentionally
424
424
  strict: if the target is not an `input[type="range"]`, setup fails with
425
425
  `not_range_input` instead of silently treating the control like a text field.
426
+ Use `deterministic_runtime` when randomized or clock-driven gameplay needs a
427
+ stable proof path. It can install a deterministic `Math.random` queue with
428
+ `random_queue` / `randomValues`, pin `Date.now()` with `now` / `mockNow`,
429
+ advance the pinned clock with `advance_ms`, append more random values with
430
+ `append: true`, and restore browser originals with `restore: true`. Receipts
431
+ record whether random and clock mocks are active, queue length, clock time, and
432
+ random-queue underflows. Values in `random_queue` must be finite numbers from
433
+ `0` inclusive to `1` exclusive.
426
434
  Use `canvas_signature` for canvas-only proof surfaces. It requires `selector`,
427
435
  reads the selected canvas with `toDataURL("image/png")`, records a sampled hash,
428
436
  canvas dimensions, CSS dimensions, and data length, and can store the result
@@ -49,6 +49,7 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
49
49
  "fill",
50
50
  "set_input_value",
51
51
  "set_range_value",
52
+ "deterministic_runtime",
52
53
  "canvas_signature",
53
54
  "assert_text_visible",
54
55
  "assert_text_absent",
@@ -511,6 +512,23 @@ function profileSetupWindowEvalReceipts(results) {
511
512
  return receipt;
512
513
  });
513
514
  }
515
+ function profileSetupDeterministicRuntimeReceipts(results) {
516
+ return results.filter((result) => profileSetupResultAction(result) === "deterministic_runtime").map((result) => ({
517
+ ordinal: result.ordinal ?? null,
518
+ ok: result.ok !== false,
519
+ random_enabled: result.random_enabled ?? null,
520
+ random_queue_added: result.random_queue_added ?? null,
521
+ random_queue_length: result.random_queue_length ?? null,
522
+ random_queue_mode: result.random_queue_mode ?? null,
523
+ random_underflow_count: result.random_underflow_count ?? null,
524
+ clock_enabled: result.clock_enabled ?? null,
525
+ previous_now: result.previous_now ?? null,
526
+ now: result.now ?? null,
527
+ advance_ms: result.advance_ms ?? null,
528
+ restored: result.restored ?? null,
529
+ reason: result.reason ?? result.error ?? null
530
+ }));
531
+ }
514
532
  function profileSetupReturnSummaryFields(result) {
515
533
  const input = result.return_summary_fields;
516
534
  if (!Array.isArray(input)) return [];
@@ -725,6 +743,8 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
725
743
  const windowEvalStoredTotal = windowEvalReceipts.filter((result) => typeof result.return_stored_to === "string" && result.return_stored_to.trim()).length;
726
744
  const windowEvalCapturedTotal = windowEvalReceipts.filter((result) => result.return_captured === true).length;
727
745
  const sampledWindowEvalReceipts = sampleProfileSetupSummaryItems(windowEvalReceipts, 8);
746
+ const deterministicRuntimeReceipts = profileSetupDeterministicRuntimeReceipts(results);
747
+ const sampledDeterministicRuntimeReceipts = sampleProfileSetupSummaryItems(deterministicRuntimeReceipts, 8);
728
748
  const rangeValueReceipts = profileSetupRangeValueReceipts(results);
729
749
  const sampledRangeValueReceipts = sampleProfileSetupSummaryItems(rangeValueReceipts, 8);
730
750
  const dragReceipts = profileSetupDragReceipts(results);
@@ -784,6 +804,9 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewpo
784
804
  window_eval_captured_total: windowEvalCapturedTotal,
785
805
  window_eval_truncated: windowEvalReceipts.length > sampledWindowEvalReceipts.length,
786
806
  window_eval: sampledWindowEvalReceipts,
807
+ deterministic_runtime_total: deterministicRuntimeReceipts.length,
808
+ deterministic_runtime_truncated: deterministicRuntimeReceipts.length > sampledDeterministicRuntimeReceipts.length,
809
+ deterministic_runtime: sampledDeterministicRuntimeReceipts,
787
810
  set_range_value_total: rangeValueReceipts.length,
788
811
  set_range_value_truncated: rangeValueReceipts.length > sampledRangeValueReceipts.length,
789
812
  set_range_value: sampledRangeValueReceipts,
@@ -846,7 +869,7 @@ function isSupportedCheckType(value) {
846
869
  }
847
870
  function normalizeSetupActionType(value, index) {
848
871
  const normalizedInput = String(value || "").trim().replace(/-/g, "_");
849
- 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 === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : 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 === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
872
+ 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 === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : 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 === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
850
873
  if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
851
874
  return normalized;
852
875
  }
@@ -965,6 +988,39 @@ function normalizeSetupActionScreenshotFullPage(input, type, index) {
965
988
  }
966
989
  return values[0];
967
990
  }
991
+ function normalizeSetupActionRandomQueue(input, index) {
992
+ const rawQueue = valueFromOwn(
993
+ input,
994
+ "random_queue",
995
+ "randomQueue",
996
+ "random_values",
997
+ "randomValues",
998
+ "random_sequence",
999
+ "randomSequence",
1000
+ "math_random",
1001
+ "mathRandom"
1002
+ );
1003
+ if (rawQueue === void 0) return void 0;
1004
+ if (!Array.isArray(rawQueue) || rawQueue.length === 0) {
1005
+ throw new Error(`target.setup_actions[${index}].random_queue must be a non-empty array of numbers from 0 inclusive to 1 exclusive.`);
1006
+ }
1007
+ return rawQueue.map((item, queueIndex) => {
1008
+ const value = numberValue(item);
1009
+ if (value === void 0 || value < 0 || value >= 1) {
1010
+ throw new Error(`target.setup_actions[${index}].random_queue[${queueIndex}] must be a finite number from 0 inclusive to 1 exclusive.`);
1011
+ }
1012
+ return value;
1013
+ });
1014
+ }
1015
+ function normalizeSetupActionNonNegativeNumber(input, index, outputKey, ...keys) {
1016
+ const rawValue = valueFromOwn(input, ...keys);
1017
+ if (rawValue === void 0) return void 0;
1018
+ const value = numberValue(rawValue);
1019
+ if (value === void 0 || value < 0) {
1020
+ throw new Error(`target.setup_actions[${index}].${outputKey} must be a finite non-negative number.`);
1021
+ }
1022
+ return value;
1023
+ }
968
1024
  function normalizeSetupAction(input, index) {
969
1025
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
970
1026
  const type = normalizeSetupActionType(stringValue(input.type), index);
@@ -1025,6 +1081,14 @@ function normalizeSetupAction(input, index) {
1025
1081
  if ((type === "fill" || type === "set_input_value" || type === "set_range_value") && value === void 0 && !hasJsonValue) {
1026
1082
  throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
1027
1083
  }
1084
+ const randomQueue = type === "deterministic_runtime" ? normalizeSetupActionRandomQueue(input, index) : void 0;
1085
+ const deterministicNow = type === "deterministic_runtime" ? normalizeSetupActionNonNegativeNumber(input, index, "now", "now", "date_now", "dateNow", "mock_now", "mockNow", "clock", "timestamp", "time_ms", "timeMs") : void 0;
1086
+ const deterministicAdvanceMs = type === "deterministic_runtime" ? normalizeSetupActionNonNegativeNumber(input, index, "advance_ms", "advance_ms", "advanceMs", "tick_ms", "tickMs", "add_ms", "addMs") : void 0;
1087
+ const deterministicAppend = type === "deterministic_runtime" && booleanValue(valueFromOwn(input, "append", "append_random", "appendRandom")) === true;
1088
+ const deterministicRestore = type === "deterministic_runtime" && booleanValue(valueFromOwn(input, "restore", "reset", "restore_originals", "restoreOriginals")) === true;
1089
+ if (type === "deterministic_runtime" && randomQueue === void 0 && deterministicNow === void 0 && deterministicAdvanceMs === void 0 && !deterministicRestore) {
1090
+ throw new Error(`target.setup_actions[${index}] deterministic_runtime requires random_queue, now, advance_ms, or restore.`);
1091
+ }
1028
1092
  const key = stringValue(input.key);
1029
1093
  let dialogAccept;
1030
1094
  if (type === "dialog_response") {
@@ -1132,6 +1196,11 @@ function normalizeSetupAction(input, index) {
1132
1196
  key,
1133
1197
  value,
1134
1198
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
1199
+ random_queue: randomQueue,
1200
+ now: deterministicNow,
1201
+ advance_ms: deterministicAdvanceMs,
1202
+ append: deterministicAppend || void 0,
1203
+ restore: deterministicRestore || void 0,
1135
1204
  label: stringFromOwn(input, "label", "name", "screenshot_label", "screenshotLabel"),
1136
1205
  script,
1137
1206
  path,
@@ -4208,6 +4277,25 @@ function profileSetupWindowEvalReceipts(results) {
4208
4277
  return receipt;
4209
4278
  });
4210
4279
  }
4280
+ function profileSetupDeterministicRuntimeReceipts(results) {
4281
+ return (results || [])
4282
+ .filter((result) => result && profileSetupResultAction(result) === "deterministic_runtime")
4283
+ .map((result) => ({
4284
+ ordinal: result.ordinal ?? null,
4285
+ ok: result.ok !== false,
4286
+ random_enabled: result.random_enabled ?? null,
4287
+ random_queue_added: result.random_queue_added ?? null,
4288
+ random_queue_length: result.random_queue_length ?? null,
4289
+ random_queue_mode: result.random_queue_mode ?? null,
4290
+ random_underflow_count: result.random_underflow_count ?? null,
4291
+ clock_enabled: result.clock_enabled ?? null,
4292
+ previous_now: result.previous_now ?? null,
4293
+ now: result.now ?? null,
4294
+ advance_ms: result.advance_ms ?? null,
4295
+ restored: result.restored ?? null,
4296
+ reason: result.reason || result.error || null,
4297
+ }));
4298
+ }
4211
4299
  function profileSetupReturnSummaryFields(result) {
4212
4300
  const input = result && result.return_summary_fields;
4213
4301
  if (!Array.isArray(input)) return [];
@@ -4451,6 +4539,8 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewp
4451
4539
  const windowEvalStoredTotal = windowEvalReceipts.filter((result) => typeof result.return_stored_to === "string" && result.return_stored_to.trim()).length;
4452
4540
  const windowEvalCapturedTotal = windowEvalReceipts.filter((result) => result.return_captured === true).length;
4453
4541
  const sampledWindowEvalReceipts = sampleProfileSetupSummaryItems(windowEvalReceipts, 8);
4542
+ const deterministicRuntimeReceipts = profileSetupDeterministicRuntimeReceipts(results);
4543
+ const sampledDeterministicRuntimeReceipts = sampleProfileSetupSummaryItems(deterministicRuntimeReceipts, 8);
4454
4544
  const rangeValueReceipts = profileSetupRangeValueReceipts(results);
4455
4545
  const sampledRangeValueReceipts = sampleProfileSetupSummaryItems(rangeValueReceipts, 8);
4456
4546
  const dragReceipts = profileSetupDragReceipts(results);
@@ -4520,6 +4610,9 @@ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewp
4520
4610
  window_eval_captured_total: windowEvalCapturedTotal,
4521
4611
  window_eval_truncated: windowEvalReceipts.length > sampledWindowEvalReceipts.length,
4522
4612
  window_eval: sampledWindowEvalReceipts,
4613
+ deterministic_runtime_total: deterministicRuntimeReceipts.length,
4614
+ deterministic_runtime_truncated: deterministicRuntimeReceipts.length > sampledDeterministicRuntimeReceipts.length,
4615
+ deterministic_runtime: sampledDeterministicRuntimeReceipts,
4523
4616
  set_range_value_total: rangeValueReceipts.length,
4524
4617
  set_range_value_truncated: rangeValueReceipts.length > sampledRangeValueReceipts.length,
4525
4618
  set_range_value: sampledRangeValueReceipts,
@@ -6141,6 +6234,111 @@ async function executeSetupAction(action, ordinal, viewport) {
6141
6234
  }
6142
6235
  return { ...base, ...setupScopeEvidence(scope), ok: true, storage, reload: action.reload === true };
6143
6236
  }
6237
+ if (type === "deterministic_runtime") {
6238
+ const scope = await setupActionScope(action, timeout);
6239
+ if (!scope.ok) return setupScopeFailure(base, scope);
6240
+ const randomQueue = Array.isArray(action.random_queue)
6241
+ ? action.random_queue
6242
+ : Array.isArray(action.randomQueue)
6243
+ ? action.randomQueue
6244
+ : null;
6245
+ const now = setupFiniteNumber(action.now ?? action.date_now ?? action.dateNow ?? action.mock_now ?? action.mockNow ?? action.clock ?? action.timestamp ?? action.time_ms ?? action.timeMs);
6246
+ const advanceMs = setupFiniteNumber(action.advance_ms ?? action.advanceMs ?? action.tick_ms ?? action.tickMs ?? action.add_ms ?? action.addMs);
6247
+ const append = action.append === true || action.append_random === true || action.appendRandom === true;
6248
+ const restore = action.restore === true || action.reset === true || action.restore_originals === true || action.restoreOriginals === true;
6249
+ const runtimeResult = await scope.context.evaluate((payload) => {
6250
+ const root = window;
6251
+ const stateKey = "__RIDDLE_PROOF_DETERMINISTIC_RUNTIME__";
6252
+ const state = root[stateKey] && typeof root[stateKey] === "object" && !Array.isArray(root[stateKey])
6253
+ ? root[stateKey]
6254
+ : {};
6255
+ root[stateKey] = state;
6256
+ if (typeof state.originalRandom !== "function") state.originalRandom = Math.random;
6257
+ if (typeof state.originalDateNow !== "function") state.originalDateNow = Date.now;
6258
+ const previousNow = typeof state.now === "number" && Number.isFinite(state.now)
6259
+ ? state.now
6260
+ : Date.now === state.originalDateNow
6261
+ ? null
6262
+ : Date.now();
6263
+ if (payload.restore) {
6264
+ Math.random = state.originalRandom;
6265
+ Date.now = state.originalDateNow;
6266
+ delete root[stateKey];
6267
+ return {
6268
+ ok: true,
6269
+ restored: true,
6270
+ random_enabled: false,
6271
+ random_queue_added: 0,
6272
+ random_queue_length: 0,
6273
+ random_queue_mode: null,
6274
+ random_underflow_count: typeof state.randomUnderflowCount === "number" ? state.randomUnderflowCount : 0,
6275
+ clock_enabled: false,
6276
+ previous_now: previousNow,
6277
+ now: null,
6278
+ advance_ms: null,
6279
+ };
6280
+ }
6281
+ let randomQueueAdded = null;
6282
+ let randomQueueMode = null;
6283
+ if (Array.isArray(payload.random_queue)) {
6284
+ const queue = payload.random_queue.filter((value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value < 1);
6285
+ const existing = Array.isArray(state.randomQueue) ? state.randomQueue : [];
6286
+ state.randomQueue = payload.append ? existing.concat(queue) : queue.slice();
6287
+ randomQueueAdded = queue.length;
6288
+ randomQueueMode = payload.append ? "append" : "replace";
6289
+ Math.random = function riddleProofMockRandom() {
6290
+ const activeQueue = Array.isArray(state.randomQueue) ? state.randomQueue : [];
6291
+ if (activeQueue.length) return activeQueue.shift();
6292
+ state.randomUnderflowCount = (typeof state.randomUnderflowCount === "number" ? state.randomUnderflowCount : 0) + 1;
6293
+ return 0;
6294
+ };
6295
+ }
6296
+ if (typeof payload.now === "number" && Number.isFinite(payload.now)) {
6297
+ state.now = payload.now;
6298
+ Date.now = function riddleProofMockDateNow() {
6299
+ return state.now;
6300
+ };
6301
+ }
6302
+ if (typeof payload.advance_ms === "number" && Number.isFinite(payload.advance_ms)) {
6303
+ const baseNow = typeof state.now === "number" && Number.isFinite(state.now)
6304
+ ? state.now
6305
+ : Date.now();
6306
+ state.now = baseNow + payload.advance_ms;
6307
+ Date.now = function riddleProofMockDateNow() {
6308
+ return state.now;
6309
+ };
6310
+ }
6311
+ return {
6312
+ ok: true,
6313
+ restored: false,
6314
+ random_enabled: Math.random !== state.originalRandom,
6315
+ random_queue_added: randomQueueAdded,
6316
+ random_queue_length: Array.isArray(state.randomQueue) ? state.randomQueue.length : 0,
6317
+ random_queue_mode: randomQueueMode,
6318
+ random_underflow_count: typeof state.randomUnderflowCount === "number" ? state.randomUnderflowCount : 0,
6319
+ clock_enabled: Date.now !== state.originalDateNow,
6320
+ previous_now: previousNow,
6321
+ now: typeof state.now === "number" && Number.isFinite(state.now) ? state.now : null,
6322
+ advance_ms: typeof payload.advance_ms === "number" && Number.isFinite(payload.advance_ms) ? payload.advance_ms : null,
6323
+ };
6324
+ }, { random_queue: randomQueue, now, advance_ms: advanceMs, append, restore });
6325
+ return {
6326
+ ...base,
6327
+ ...setupScopeEvidence(scope),
6328
+ ok: runtimeResult && runtimeResult.ok === true,
6329
+ random_enabled: runtimeResult?.random_enabled,
6330
+ random_queue_added: runtimeResult?.random_queue_added,
6331
+ random_queue_length: runtimeResult?.random_queue_length,
6332
+ random_queue_mode: runtimeResult?.random_queue_mode,
6333
+ random_underflow_count: runtimeResult?.random_underflow_count,
6334
+ clock_enabled: runtimeResult?.clock_enabled,
6335
+ previous_now: runtimeResult?.previous_now,
6336
+ now: runtimeResult?.now,
6337
+ advance_ms: runtimeResult?.advance_ms,
6338
+ restored: runtimeResult?.restored,
6339
+ reason: runtimeResult && runtimeResult.ok === true ? undefined : runtimeResult?.reason || "deterministic_runtime_not_applied",
6340
+ };
6341
+ }
6144
6342
  if (type === "window_eval") {
6145
6343
  const script = String(action.script || action.code || action.source || action.body || "");
6146
6344
  const args = Array.isArray(action.args) ? action.args : [];