@riddledc/riddle-proof 0.8.85 → 0.8.87

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.
Files changed (58) hide show
  1. package/dist/advanced/index.d.cts +1 -1
  2. package/dist/advanced/index.d.ts +1 -1
  3. package/dist/advanced/proof-run-engine.d.cts +1 -1
  4. package/dist/advanced/proof-run-engine.d.ts +1 -1
  5. package/dist/change-proof.cjs +2 -9
  6. package/dist/change-proof.d.cts +1 -1
  7. package/dist/change-proof.d.ts +1 -1
  8. package/dist/change-proof.js +2 -2
  9. package/dist/{chunk-AFARUBE4.js → chunk-3W4NGKZ6.js} +11 -11
  10. package/dist/chunk-6S33FQL7.js +14 -0
  11. package/dist/{chunk-FW7CKARF.js → chunk-7W6ZJPHJ.js} +1 -1
  12. package/dist/{chunk-JZKLCY5G.js → chunk-BVYM4WJ5.js} +1 -1
  13. package/dist/chunk-GQOE5Q7C.js +32 -0
  14. package/dist/chunk-H5AALGSA.js +32 -0
  15. package/dist/{chunk-2K3DIK7A.js → chunk-KF6F2RMQ.js} +301 -3979
  16. package/dist/{chunk-O2AO726C.js → chunk-NR2XQXIC.js} +3 -3
  17. package/dist/cli/index.js +5 -5
  18. package/dist/cli.cjs +285 -3999
  19. package/dist/cli.js +5 -5
  20. package/dist/evidence-trust-root.cjs +54 -0
  21. package/dist/evidence-trust-root.d.cts +2 -0
  22. package/dist/evidence-trust-root.d.ts +2 -0
  23. package/dist/evidence-trust-root.js +31 -0
  24. package/dist/index.cjs +346 -3991
  25. package/dist/index.d.cts +8 -1
  26. package/dist/index.d.ts +8 -1
  27. package/dist/index.js +80 -14
  28. package/dist/packet.cjs +54 -0
  29. package/dist/packet.d.cts +2 -0
  30. package/dist/packet.d.ts +2 -0
  31. package/dist/packet.js +31 -0
  32. package/dist/pr-comment.cjs +2 -9
  33. package/dist/pr-comment.d.cts +2 -2
  34. package/dist/pr-comment.d.ts +2 -2
  35. package/dist/pr-comment.js +3 -3
  36. package/dist/profile/index.cjs +277 -3991
  37. package/dist/profile/index.d.cts +2 -3
  38. package/dist/profile/index.d.ts +2 -3
  39. package/dist/profile/index.js +1 -1
  40. package/dist/profile-suggestions.cjs +2 -2
  41. package/dist/profile-suggestions.d.cts +1 -3
  42. package/dist/profile-suggestions.d.ts +1 -3
  43. package/dist/profile-suggestions.js +2 -2
  44. package/dist/profile.cjs +277 -3991
  45. package/dist/profile.d.cts +3 -550
  46. package/dist/profile.d.ts +3 -550
  47. package/dist/profile.js +1 -1
  48. package/dist/{proof-run-engine-DpChFR5H.d.cts → proof-run-engine-Baiv6l3A.d.cts} +3 -3
  49. package/dist/{proof-run-engine-BqRoA3Do.d.ts → proof-run-engine-MiKZt9oY.d.ts} +3 -3
  50. package/dist/proof-run-engine.d.cts +1 -1
  51. package/dist/proof-run-engine.d.ts +1 -1
  52. package/dist/receipts.d.cts +3 -0
  53. package/dist/receipts.d.ts +3 -0
  54. package/dist/rule-trust-root.cjs +36 -0
  55. package/dist/rule-trust-root.d.cts +2 -0
  56. package/dist/rule-trust-root.d.ts +2 -0
  57. package/dist/rule-trust-root.js +13 -0
  58. package/package.json +20 -5
package/dist/cli.cjs CHANGED
@@ -7879,145 +7879,10 @@ async function runCodexExecAgentDoctor(config = {}, runner = createCodexExecJson
7879
7879
  var import_client = require("@riddledc/riddle-proof-riddle-client/client");
7880
7880
 
7881
7881
  // src/profile.ts
7882
- var RIDDLE_PROOF_PROFILE_VERSION = "riddle-proof.profile.v1";
7883
- var RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION = "riddle-proof.profile-evidence.v1";
7884
- var RIDDLE_PROOF_PROFILE_RESULT_VERSION = "riddle-proof.profile-result.v1";
7885
- var RIDDLE_PROOF_PROFILE_STATUSES = [
7886
- "passed",
7887
- "product_regression",
7888
- "proof_insufficient",
7889
- "environment_blocked",
7890
- "configuration_error",
7891
- "needs_human_review"
7892
- ];
7893
- var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
7894
- "route_loaded",
7895
- "url_search_param_equals",
7896
- "url_search_param_absent",
7897
- "selector_visible",
7898
- "selector_absent",
7899
- "selector_count_at_least",
7900
- "selector_count_equals",
7901
- "selector_count_equal",
7902
- "selector_count_eq",
7903
- "dialog_count_equals",
7904
- "dialog_accept_count_equals",
7905
- "dialog_dismiss_count_equals",
7906
- "selector_text_visible",
7907
- "selector_text_absent",
7908
- "selector_text_order",
7909
- "ordered_trace",
7910
- "observe_within",
7911
- "frame_text_visible",
7912
- "frame_url_equals",
7913
- "frame_url_matches",
7914
- "frame_no_horizontal_overflow",
7915
- "text_visible",
7916
- "text_absent",
7917
- "http_status",
7918
- "link_status",
7919
- "artifact_link_status",
7920
- "route_inventory",
7921
- "no_horizontal_overflow",
7922
- "no_mobile_horizontal_overflow",
7923
- "no_fatal_console_errors",
7924
- "no_console_warnings"
7925
- ];
7926
- var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
7927
- "click",
7928
- "tap",
7929
- "tap_until",
7930
- "drag",
7931
- "press",
7932
- "key_down",
7933
- "key_up",
7934
- "fill",
7935
- "set_input_value",
7936
- "set_range_value",
7937
- "deterministic_runtime",
7938
- "canvas_signature",
7939
- "assert_text_visible",
7940
- "assert_text_absent",
7941
- "assert_selector_count",
7942
- "assert_window_value",
7943
- "assert_window_number",
7944
- "local_storage",
7945
- "session_storage",
7946
- "clear_storage",
7947
- "clear_console",
7948
- "dialog_response",
7949
- "screenshot",
7950
- "wait",
7951
- "wait_for_selector",
7952
- "wait_for_text",
7953
- "window_eval",
7954
- "window_call",
7955
- "window_call_until"
7956
- ];
7957
- var RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES = [
7958
- "aborted",
7959
- "accessdenied",
7960
- "addressunreachable",
7961
- "blockedbyclient",
7962
- "blockedbyresponse",
7963
- "connectionaborted",
7964
- "connectionclosed",
7965
- "connectionfailed",
7966
- "connectionrefused",
7967
- "connectionreset",
7968
- "internetdisconnected",
7969
- "namenotresolved",
7970
- "timedout",
7971
- "failed"
7972
- ];
7973
- var RIDDLE_PROOF_ORDERED_TRACE_OPERATORS = [
7974
- "exists",
7975
- "equals",
7976
- "not_equals",
7977
- "truthy",
7978
- "falsy",
7979
- "gt",
7980
- "gte",
7981
- "lt",
7982
- "lte",
7983
- "abs_gt",
7984
- "abs_gte",
7985
- "abs_lt",
7986
- "abs_lte"
7987
- ];
7988
- function uniqueNonEmptyStrings(values) {
7989
- const seen = /* @__PURE__ */ new Set();
7990
- const result = [];
7991
- for (const value of values ?? []) {
7992
- const normalized = typeof value === "string" ? value.trim() : "";
7993
- if (!normalized || seen.has(normalized)) continue;
7994
- seen.add(normalized);
7995
- result.push(normalized);
7996
- }
7997
- return result;
7998
- }
7999
- function deriveRiddleProofArtifactBodyAssertions(input) {
8000
- const artifactText = typeof input.artifact_text === "string" ? input.artifact_text : "";
8001
- const candidates = uniqueNonEmptyStrings(input.candidates);
8002
- const required = uniqueNonEmptyStrings(input.required);
8003
- const mergedCandidates = uniqueNonEmptyStrings([...required, ...candidates]);
8004
- const presentCandidates = mergedCandidates.filter((snippet) => artifactText.includes(snippet));
8005
- const missingCandidates = mergedCandidates.filter((snippet) => !artifactText.includes(snippet));
8006
- const missingRequired = required.filter((snippet) => !artifactText.includes(snippet));
8007
- const warnings = missingCandidates.map((snippet) => `candidate snippet is not present in artifact body: ${snippet}`);
8008
- return {
8009
- version: "riddle-proof.artifact-body-assertions.v1",
8010
- ok: missingRequired.length === 0,
8011
- body_contains: presentCandidates,
8012
- present_candidates: presentCandidates,
8013
- missing_candidates: missingCandidates,
8014
- missing_required: missingRequired,
8015
- warnings
8016
- };
8017
- }
8018
- var DEFAULT_VIEWPORTS = [
8019
- { name: "desktop", width: 1280, height: 800 }
8020
- ];
7882
+ var import_profile2 = require("@riddledc/riddle-proof-core/profile");
7883
+
7884
+ // src/profile-capabilities.ts
7885
+ var import_profile = require("@riddledc/riddle-proof-core/profile");
8021
7886
  function isRecord(value) {
8022
7887
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8023
7888
  }
@@ -8027,113 +7892,12 @@ function stringValue3(value) {
8027
7892
  function hasOwn(value, key) {
8028
7893
  return Object.prototype.hasOwnProperty.call(value, key);
8029
7894
  }
8030
- function stringFromOwn(input, ...keys) {
8031
- for (const key of keys) {
8032
- if (hasOwn(input, key)) {
8033
- const value = input[key];
8034
- if (value !== void 0 && value !== null) return String(value);
8035
- }
8036
- }
8037
- return void 0;
8038
- }
8039
- function valueFromOwn(input, ...keys) {
8040
- for (const key of keys) {
8041
- if (hasOwn(input, key)) return input[key];
8042
- }
8043
- return void 0;
8044
- }
8045
7895
  function numberValue2(value) {
8046
7896
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8047
7897
  }
8048
7898
  function booleanValue2(value) {
8049
7899
  return typeof value === "boolean" ? value : void 0;
8050
7900
  }
8051
- function horizontalBoundsOverflowPx(value) {
8052
- if (!isRecord(value)) return 0;
8053
- let max = maxPositiveNumber(
8054
- value.overflow_px,
8055
- value.overflow,
8056
- value.bounds_overflow_px,
8057
- value.horizontal_overflow_px,
8058
- value.left_overflow_px,
8059
- value.right_overflow_px
8060
- );
8061
- for (const offender of boundsOffendersForEvidence(value)) {
8062
- max = Math.max(max, horizontalOffenderOverflowPx(offender));
8063
- }
8064
- return roundPixels(max);
8065
- }
8066
- function horizontalOffenderOverflowPx(value) {
8067
- if (!isRecord(value)) return 0;
8068
- let max = maxPositiveNumber(
8069
- value.overflow,
8070
- value.overflow_px,
8071
- value.bounds_overflow_px,
8072
- value.horizontal_overflow_px,
8073
- value.left_overflow_px,
8074
- value.right_overflow_px,
8075
- value.leftOverflowPx,
8076
- value.rightOverflowPx
8077
- );
8078
- const clipped = isRecord(value.clipped || value.clip || value.clipping) ? value.clipped || value.clip || value.clipping : void 0;
8079
- if (isRecord(clipped)) {
8080
- max = Math.max(max, maxPositiveNumber(
8081
- clipped.left,
8082
- clipped.right,
8083
- clipped.left_px,
8084
- clipped.right_px,
8085
- clipped.leftPx,
8086
- clipped.rightPx
8087
- ));
8088
- }
8089
- const rect = isRecord(value.rect || value.bounds || value.bounding_rect || value.boundingRect) ? value.rect || value.bounds || value.bounding_rect || value.boundingRect : void 0;
8090
- const viewportWidth = numberValue2(value.viewport_width ?? value.viewportWidth);
8091
- if (isRecord(rect) && viewportWidth !== void 0) {
8092
- const left = numberValue2(rect.left);
8093
- const right = numberValue2(rect.right);
8094
- if (left !== void 0 && left < 0) max = Math.max(max, Math.abs(left));
8095
- if (right !== void 0 && right > viewportWidth) max = Math.max(max, right - viewportWidth);
8096
- }
8097
- return roundPixels(max);
8098
- }
8099
- function boundsOffendersForEvidence(value) {
8100
- if (!isRecord(value)) return [];
8101
- const offenders = [
8102
- ...Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [],
8103
- ...Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [],
8104
- ...Array.isArray(value.bounds_offenders) ? value.bounds_offenders : [],
8105
- ...Array.isArray(value.boundsOffenders) ? value.boundsOffenders : [],
8106
- ...Array.isArray(value.clipped_elements) ? value.clipped_elements : [],
8107
- ...Array.isArray(value.clippedElements) ? value.clippedElements : [],
8108
- ...Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [],
8109
- ...Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []
8110
- ];
8111
- return offenders.filter((item) => isRecord(item)).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
8112
- }
8113
- function maxPositiveNumber(...values) {
8114
- let max = 0;
8115
- for (const value of values) {
8116
- const number = numberValue2(value);
8117
- if (number !== void 0 && number > max) max = number;
8118
- }
8119
- return max;
8120
- }
8121
- function roundPixels(value) {
8122
- return Math.round(value * 100) / 100;
8123
- }
8124
- function timeoutSecValue(value) {
8125
- const number = numberValue2(value);
8126
- return number && number > 0 ? Math.ceil(number) : void 0;
8127
- }
8128
- function jsonRecord(value) {
8129
- if (!isRecord(value)) return void 0;
8130
- return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
8131
- }
8132
- function stringRecord(value) {
8133
- if (!isRecord(value)) return void 0;
8134
- const entries = Object.entries(value).map(([key, child]) => [key, String(child)]).filter(([key]) => key.trim());
8135
- return entries.length ? Object.fromEntries(entries) : void 0;
8136
- }
8137
7901
  function toJsonValue(value) {
8138
7902
  if (value === null || value === void 0) return null;
8139
7903
  if (typeof value === "string" || typeof value === "boolean") return value;
@@ -8276,185 +8040,6 @@ function resolveJsonPath(root, path6) {
8276
8040
  }
8277
8041
  return { exists: true, value: current };
8278
8042
  }
8279
- function assessRiddleProofOrderedTrace(trace, events) {
8280
- const insufficient = (reason, traceLength = Array.isArray(trace) ? trace.length : 0, witnesses2 = [], missingEvent, missingPaths) => ({
8281
- version: "riddle-proof.ordered-trace-assessment.v1",
8282
- status: "proof_insufficient",
8283
- trace_length: traceLength,
8284
- witnesses: witnesses2,
8285
- missing_event: missingEvent,
8286
- missing_paths: missingPaths,
8287
- reason
8288
- });
8289
- const parsePath = (path6) => {
8290
- const segments = [];
8291
- let token = "";
8292
- const pushToken = () => {
8293
- const value = token.trim();
8294
- if (value) segments.push(value);
8295
- token = "";
8296
- };
8297
- for (let index = 0; index < path6.length; index += 1) {
8298
- const char = path6[index];
8299
- if (char === ".") {
8300
- pushToken();
8301
- continue;
8302
- }
8303
- if (char !== "[") {
8304
- token += char;
8305
- continue;
8306
- }
8307
- pushToken();
8308
- const closeIndex = path6.indexOf("]", index + 1);
8309
- if (closeIndex === -1) throw new Error(`unterminated bracket at ${index}`);
8310
- const bracket = path6.slice(index + 1, closeIndex).trim();
8311
- if (!bracket) throw new Error(`empty bracket at ${index}`);
8312
- if (/^\d+$/.test(bracket)) {
8313
- segments.push(Number(bracket));
8314
- } else {
8315
- segments.push(bracket.replace(/^['"]|['"]$/g, ""));
8316
- }
8317
- index = closeIndex;
8318
- }
8319
- pushToken();
8320
- return segments;
8321
- };
8322
- const resolve = (root, path6) => {
8323
- let segments;
8324
- try {
8325
- segments = parsePath(path6);
8326
- } catch {
8327
- return { exists: false };
8328
- }
8329
- let current = root;
8330
- for (const segment of segments) {
8331
- if (Array.isArray(current)) {
8332
- const index = typeof segment === "number" ? segment : /^\d+$/.test(segment) ? Number(segment) : -1;
8333
- if (index < 0 || index >= current.length) return { exists: false };
8334
- current = current[index];
8335
- continue;
8336
- }
8337
- if (typeof segment !== "string" || current === null || typeof current !== "object") return { exists: false };
8338
- if (!Object.hasOwn(current, segment)) return { exists: false };
8339
- current = current[segment];
8340
- }
8341
- return { exists: true, value: current };
8342
- };
8343
- const valuesEqual = (left, right) => {
8344
- if (Object.is(left, right)) return true;
8345
- if (Array.isArray(left) || Array.isArray(right)) {
8346
- return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => valuesEqual(value, right[index]));
8347
- }
8348
- if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
8349
- const leftRecord = left;
8350
- const rightRecord = right;
8351
- const leftKeys = Object.keys(leftRecord).sort();
8352
- const rightKeys = Object.keys(rightRecord).sort();
8353
- return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && valuesEqual(leftRecord[key], rightRecord[key]));
8354
- };
8355
- const numericOperator = (op) => [
8356
- "gt",
8357
- "gte",
8358
- "lt",
8359
- "lte",
8360
- "abs_gt",
8361
- "abs_gte",
8362
- "abs_lt",
8363
- "abs_lte"
8364
- ].includes(op);
8365
- const matches = (value, predicate) => {
8366
- if (predicate.op === "exists") return true;
8367
- if (predicate.op === "equals") return valuesEqual(value, predicate.value);
8368
- if (predicate.op === "not_equals") return !valuesEqual(value, predicate.value);
8369
- if (predicate.op === "truthy") return Boolean(value);
8370
- if (predicate.op === "falsy") return !value;
8371
- const observed = typeof value === "number" ? value : Number.NaN;
8372
- const expected = typeof predicate.value === "number" ? predicate.value : Number.NaN;
8373
- if (!Number.isFinite(observed) || !Number.isFinite(expected)) return false;
8374
- const candidate = predicate.op.startsWith("abs_") ? Math.abs(observed) : observed;
8375
- if (predicate.op === "gt" || predicate.op === "abs_gt") return candidate > expected;
8376
- if (predicate.op === "gte" || predicate.op === "abs_gte") return candidate >= expected;
8377
- if (predicate.op === "lt" || predicate.op === "abs_lt") return candidate < expected;
8378
- return candidate <= expected;
8379
- };
8380
- if (!Array.isArray(trace) || trace.length === 0) return insufficient("trace_missing_or_empty");
8381
- if (!Array.isArray(events) || events.length === 0) return insufficient("events_missing", trace.length);
8382
- for (const event of events) {
8383
- const missingPaths = event.predicates.filter((predicate) => !trace.some((sample) => {
8384
- const resolved = resolve(sample, predicate.path);
8385
- return resolved.exists && (!numericOperator(predicate.op) || typeof resolved.value === "number" && Number.isFinite(resolved.value));
8386
- })).map((predicate) => predicate.path);
8387
- if (missingPaths.length) {
8388
- return insufficient("required_trace_field_missing", trace.length, [], event.label, Array.from(new Set(missingPaths)));
8389
- }
8390
- }
8391
- const witnesses = [];
8392
- let cursor = 0;
8393
- for (const event of events) {
8394
- let witnessIndex = -1;
8395
- for (let index = cursor; index < trace.length; index += 1) {
8396
- if (event.predicates.every((predicate) => {
8397
- const resolved = resolve(trace[index], predicate.path);
8398
- return resolved.exists && matches(resolved.value, predicate);
8399
- })) {
8400
- witnessIndex = index;
8401
- break;
8402
- }
8403
- }
8404
- if (witnessIndex < 0) {
8405
- return {
8406
- version: "riddle-proof.ordered-trace-assessment.v1",
8407
- status: "failed",
8408
- trace_length: trace.length,
8409
- witnesses,
8410
- missing_event: event.label,
8411
- reason: "ordered_event_not_observed"
8412
- };
8413
- }
8414
- witnesses.push({
8415
- label: event.label,
8416
- index: witnessIndex,
8417
- observations: event.predicates.map((predicate) => {
8418
- const observed = resolve(trace[witnessIndex], predicate.path).value;
8419
- return {
8420
- path: predicate.path,
8421
- op: predicate.op,
8422
- expected: predicate.value,
8423
- observed
8424
- };
8425
- })
8426
- });
8427
- cursor = witnessIndex + 1;
8428
- }
8429
- return {
8430
- version: "riddle-proof.ordered-trace-assessment.v1",
8431
- status: "passed",
8432
- trace_length: trace.length,
8433
- witnesses
8434
- };
8435
- }
8436
- function assessRiddleProofOrderedTraceSetupResults(results, setupActionLabel, tracePath, events) {
8437
- const insufficient = (reason) => ({
8438
- version: "riddle-proof.ordered-trace-assessment.v1",
8439
- status: "proof_insufficient",
8440
- trace_length: 0,
8441
- witnesses: [],
8442
- reason
8443
- });
8444
- if (!Array.isArray(results)) return insufficient("setup_results_missing");
8445
- const source = results.find((item) => item && typeof item === "object" && !Array.isArray(item) && item.label === setupActionLabel);
8446
- if (!source) return insufficient("setup_action_result_missing");
8447
- if (!Object.hasOwn(source, "returned")) return insufficient("setup_action_return_missing");
8448
- const segments = tracePath.split(".").map((segment) => segment.trim()).filter(Boolean);
8449
- let trace = source.returned;
8450
- for (const segment of segments) {
8451
- if (trace === null || typeof trace !== "object" || Array.isArray(trace) || !Object.hasOwn(trace, segment)) {
8452
- return insufficient("trace_path_missing");
8453
- }
8454
- trace = trace[segment];
8455
- }
8456
- return assessRiddleProofOrderedTrace(trace, events);
8457
- }
8458
8043
  function evaluateHttpStatusBodyJsonAssertion(root, assertion) {
8459
8044
  const resolved = resolveJsonPath(root, assertion.path);
8460
8045
  const errors = [];
@@ -8510,3498 +8095,290 @@ function evaluateHttpStatusBodyJsonAssertions(bodyText, assertions) {
8510
8095
  }
8511
8096
  return expected.map((assertion) => evaluateHttpStatusBodyJsonAssertion(parsed, assertion));
8512
8097
  }
8513
- function compactProfileSetupSummaryText(value, limit = 160) {
8514
- const text = typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
8515
- if (!text) return void 0;
8516
- if (text.length <= limit) return text;
8517
- return `${text.slice(0, Math.max(0, limit - 15)).trimEnd()}... (${text.length} chars)`;
8518
- }
8519
- function profileSetupResultAction(value) {
8520
- const action = value.action ?? value.type;
8521
- return typeof action === "string" && action ? action : "unknown";
8098
+ function checkLabel(check) {
8099
+ return check.label || check.type;
8522
8100
  }
8523
- function profileSetupFrameUrls(viewport) {
8524
- const urls = [];
8525
- const frames = viewport.frames || {};
8526
- for (const container of Object.values(frames)) {
8527
- if (!isRecord(container) || !Array.isArray(container.frames)) continue;
8528
- for (const frame of container.frames) {
8529
- if (!isRecord(frame)) continue;
8530
- const url = typeof frame.url === "string" ? frame.url : void 0;
8531
- if (url && !urls.includes(url)) urls.push(url);
8101
+ function httpStatusRequestUrl(check, baseUrl) {
8102
+ const rawUrl = check.url || "";
8103
+ if (!rawUrl) return "";
8104
+ try {
8105
+ if (baseUrl && rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
8106
+ const base = new URL(baseUrl);
8107
+ const mountPrefix = previewMountPrefix(base.pathname);
8108
+ if (mountPrefix) {
8109
+ const requested = new URL(rawUrl, base.origin);
8110
+ base.pathname = joinMountedRoutePath(mountPrefix, requested.pathname);
8111
+ base.search = requested.search;
8112
+ base.hash = requested.hash;
8113
+ return base.href;
8114
+ }
8532
8115
  }
8116
+ return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
8117
+ } catch {
8118
+ return rawUrl;
8533
8119
  }
8534
- return urls.slice(0, 10);
8535
- }
8536
- function profileSetupActionCounts(results) {
8537
- const counts = {};
8538
- for (const result of results) {
8539
- const action = profileSetupResultAction(result);
8540
- counts[action] = (counts[action] || 0) + 1;
8541
- }
8542
- return toJsonValue(counts);
8543
8120
  }
8544
- function profileSetupScreenshotLabels(results) {
8545
- return results.filter((result) => profileSetupResultAction(result) === "screenshot" && result.ok !== false && typeof result.screenshot_label === "string").map((result) => result.screenshot_label).filter(Boolean);
8121
+ function httpStatusMethod(check) {
8122
+ return (check.method || "GET").toUpperCase();
8546
8123
  }
8547
- function profileSetupWindowCallUntilReceipts(results) {
8548
- return results.filter((result) => profileSetupResultAction(result) === "window_call_until").map((result) => ({
8549
- ordinal: result.ordinal ?? null,
8550
- ok: result.ok !== false,
8551
- path: result.path ?? null,
8552
- until_path: result.until_path ?? null,
8553
- until_value: result.until_value ?? null,
8554
- until_expected_value: result.until_expected_value ?? null,
8555
- call_count: result.call_count ?? null,
8556
- max_calls: result.max_calls ?? null,
8557
- reason: result.reason ?? result.error ?? null
8558
- }));
8124
+ function httpStatusAllowedStatuses(check) {
8125
+ if (check.allowed_statuses?.length) return check.allowed_statuses;
8126
+ if (check.expected_status !== void 0) return [check.expected_status];
8127
+ return void 0;
8559
8128
  }
8560
- function profileSetupWindowCallReceipts(results) {
8561
- return results.filter((result) => profileSetupResultAction(result) === "window_call").map((result) => {
8562
- const receipt = {
8563
- ordinal: result.ordinal ?? null,
8564
- label: result.label ?? null,
8565
- ok: result.ok !== false,
8566
- path: result.path ?? null,
8567
- return_captured: result.return_captured ?? null,
8568
- return_stored_to: result.return_stored_to ?? null,
8569
- reason: result.reason ?? result.store_reason ?? null
8570
- };
8571
- if (result.error !== void 0) receipt.error = result.error;
8572
- if (result.returned !== void 0) receipt.returned = result.returned;
8573
- if (result.expected_return !== void 0) receipt.expected_return = result.expected_return;
8574
- const returnSummary = profileSetupReturnSummary(result);
8575
- if (returnSummary.length) receipt.return_summary = returnSummary;
8576
- return receipt;
8577
- });
8129
+ function httpStatusIsAllowed(status, check) {
8130
+ if (status === void 0) return false;
8131
+ const allowed = httpStatusAllowedStatuses(check);
8132
+ return allowed?.length ? allowed.includes(status) : status >= 200 && status < 400;
8578
8133
  }
8579
- function profileSetupWindowEvalReceipts(results) {
8580
- return results.filter((result) => profileSetupResultAction(result) === "window_eval").map((result) => {
8581
- const receipt = {
8582
- ordinal: result.ordinal ?? null,
8583
- label: result.label ?? null,
8584
- ok: result.ok !== false,
8585
- script_length: result.script_length ?? null,
8586
- return_captured: result.return_captured ?? null,
8587
- return_stored_to: result.return_stored_to ?? null,
8588
- reason: result.reason ?? result.store_reason ?? null
8589
- };
8590
- if (result.error !== void 0) receipt.error = result.error;
8591
- if (result.returned !== void 0) receipt.returned = result.returned;
8592
- if (result.expected_return !== void 0) receipt.expected_return = result.expected_return;
8593
- const returnSummary = profileSetupReturnSummary(result);
8594
- if (returnSummary.length) receipt.return_summary = returnSummary;
8595
- return receipt;
8596
- });
8134
+ function linkStatusObservedBytes(result) {
8135
+ const bytes = numberValue2(result.bytes);
8136
+ const contentLength = numberValue2(result.content_length);
8137
+ return Math.max(bytes ?? 0, contentLength ?? 0) || void 0;
8597
8138
  }
8598
- function profileSetupDeterministicRuntimeReceipts(results) {
8599
- return results.filter((result) => profileSetupResultAction(result) === "deterministic_runtime").map((result) => ({
8600
- ordinal: result.ordinal ?? null,
8601
- ok: result.ok !== false,
8602
- random_enabled: result.random_enabled ?? null,
8603
- random_queue_added: result.random_queue_added ?? null,
8604
- random_queue_length: result.random_queue_length ?? null,
8605
- random_queue_mode: result.random_queue_mode ?? null,
8606
- random_underflow_count: result.random_underflow_count ?? null,
8607
- clock_enabled: result.clock_enabled ?? null,
8608
- previous_now: result.previous_now ?? null,
8609
- now: result.now ?? null,
8610
- advance_ms: result.advance_ms ?? null,
8611
- restored: result.restored ?? null,
8612
- reason: result.reason ?? result.error ?? null
8613
- }));
8139
+ function normalizeLinkStatusContentType(value) {
8140
+ const normalized = value?.split(";")[0]?.trim().toLowerCase();
8141
+ return normalized || void 0;
8614
8142
  }
8615
- function profileSetupReturnSummaryFields(result) {
8616
- const input = result.return_summary_fields;
8617
- if (!Array.isArray(input)) return [];
8618
- const fields = [];
8619
- for (const item of input) {
8620
- if (typeof item === "string") {
8621
- const path7 = item.trim();
8622
- if (path7) fields.push({ path: path7 });
8623
- continue;
8624
- }
8625
- if (!isRecord(item)) continue;
8626
- const path6 = stringValue3(item.path) ?? stringValue3(item.key) ?? stringValue3(item.json_path) ?? stringValue3(item.jsonPath);
8627
- if (!path6) continue;
8628
- const label = stringValue3(item.label) ?? stringValue3(item.name) ?? stringValue3(item.title);
8629
- fields.push(label ? { path: path6, label } : { path: path6 });
8630
- }
8631
- return fields;
8143
+ function linkStatusContentTypesMatch(actual, expected) {
8144
+ if (!actual || !expected) return false;
8145
+ if (expected.endsWith("/*")) return actual.startsWith(expected.slice(0, -1));
8146
+ if (actual === expected) return true;
8147
+ const yamlTypes = /* @__PURE__ */ new Set(["application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"]);
8148
+ return yamlTypes.has(actual) && yamlTypes.has(expected);
8632
8149
  }
8633
- function profileSetupReturnSummary(result) {
8634
- if (result.returned === void 0) return [];
8635
- return profileSetupReturnSummaryFields(result).map((field) => {
8636
- const resolved = resolveJsonPath(result.returned, field.path);
8637
- const receipt = {
8638
- label: field.label || field.path,
8639
- path: field.path,
8640
- exists: resolved.exists
8641
- };
8642
- if (resolved.exists) receipt.value = toJsonValue(resolved.value);
8643
- if (resolved.error) receipt.reason = resolved.error;
8644
- return receipt;
8150
+ function linkStatusContentTypeOk(result, check) {
8151
+ const expected = check.allowed_content_types;
8152
+ if (!expected?.length) return true;
8153
+ const actual = normalizeLinkStatusContentType(stringValue3(result.content_type));
8154
+ if (!actual) return false;
8155
+ return expected.some((contentType) => {
8156
+ const normalized = normalizeLinkStatusContentType(contentType);
8157
+ if (!normalized) return false;
8158
+ return linkStatusContentTypesMatch(actual, normalized);
8645
8159
  });
8646
8160
  }
8647
- function profileSetupRangeValueReceipts(results) {
8648
- return results.filter((result) => profileSetupResultAction(result) === "set_range_value").map((result) => ({
8649
- ordinal: result.ordinal ?? null,
8650
- ok: result.ok !== false,
8651
- selector: result.selector ?? null,
8652
- frame_selector: result.frame_selector ?? null,
8653
- requested_value: result.requested_value ?? null,
8654
- actual_value: result.actual_value ?? null,
8655
- before_value: result.before_value ?? null,
8656
- value_as_number: result.value_as_number ?? null,
8657
- min: result.min ?? null,
8658
- max: result.max ?? null,
8659
- step: result.step ?? null,
8660
- reason: result.reason ?? result.error ?? null
8661
- }));
8662
- }
8663
- function profileSetupDragReceipts(results) {
8664
- return results.filter((result) => profileSetupResultAction(result) === "drag").map((result) => ({
8665
- ordinal: result.ordinal ?? null,
8666
- ok: result.ok !== false,
8667
- selector: result.selector ?? null,
8668
- frame_selector: result.frame_selector ?? null,
8669
- pointer_type: result.pointer_type ?? null,
8670
- input_dispatch: result.input_dispatch ?? null,
8671
- coordinate_mode: result.coordinate_mode ?? null,
8672
- from_x: result.from_x ?? null,
8673
- from_y: result.from_y ?? null,
8674
- to_x: result.to_x ?? null,
8675
- to_y: result.to_y ?? null,
8676
- steps: result.steps ?? null,
8677
- duration_ms: result.duration_ms ?? null,
8678
- reason: result.reason ?? result.error ?? null
8679
- }));
8680
- }
8681
- function profileSetupTapReceipts(results) {
8682
- return results.filter((result) => profileSetupResultAction(result) === "tap").map((result) => ({
8683
- ordinal: result.ordinal ?? null,
8684
- ok: result.ok !== false,
8685
- selector: result.selector ?? null,
8686
- frame_selector: result.frame_selector ?? null,
8687
- pointer_type: result.pointer_type ?? null,
8688
- input_dispatch: result.input_dispatch ?? null,
8689
- coordinate_mode: result.coordinate_mode ?? null,
8690
- x: result.x ?? null,
8691
- y: result.y ?? null,
8692
- duration_ms: result.duration_ms ?? null,
8693
- reason: result.reason ?? result.error ?? null
8694
- }));
8695
- }
8696
- function profileSetupTapUntilReceipts(results) {
8697
- return results.filter((result) => profileSetupResultAction(result) === "tap_until").map((result) => ({
8698
- ordinal: result.ordinal ?? null,
8699
- ok: result.ok !== false,
8700
- selector: result.selector ?? null,
8701
- frame_selector: result.frame_selector ?? null,
8702
- pointer_type: result.pointer_type ?? null,
8703
- input_dispatch: result.input_dispatch ?? null,
8704
- coordinate_mode: result.coordinate_mode ?? null,
8705
- x: result.x ?? null,
8706
- y: result.y ?? null,
8707
- duration_ms: result.duration_ms ?? null,
8708
- until_path: result.until_path ?? null,
8709
- until_value: result.until_value ?? null,
8710
- until_expected_value: result.until_expected_value ?? null,
8711
- tap_count: result.tap_count ?? null,
8712
- max_taps: result.max_taps ?? result.max_calls ?? null,
8713
- tap_burst_size: result.tap_burst_size ?? null,
8714
- condition_check_count: result.condition_check_count ?? null,
8715
- settle_ms: result.settle_ms ?? null,
8716
- elapsed_ms: result.elapsed_ms ?? null,
8717
- interval_ms: result.interval_ms ?? null,
8718
- timeout_ms: result.timeout_ms ?? null,
8719
- reason: result.reason ?? result.error ?? null
8720
- }));
8161
+ function httpStatusBodyContainsFailures(result, check) {
8162
+ const expected = check.body_contains?.filter(Boolean) ?? [];
8163
+ if (!expected.length) return [];
8164
+ const observed = isRecord(result.body_contains) ? result.body_contains : {};
8165
+ return expected.filter((text) => observed[text] !== true);
8721
8166
  }
8722
- function profileSetupKeyboardReceipts(results) {
8723
- return results.filter((result) => ["press", "key_down", "key_up"].includes(profileSetupResultAction(result))).map((result) => ({
8724
- action: profileSetupResultAction(result),
8725
- ordinal: result.ordinal ?? null,
8726
- ok: result.ok !== false,
8727
- selector: result.selector ?? null,
8728
- frame_selector: result.frame_selector ?? null,
8729
- key: result.key ?? null,
8730
- hold_ms: result.hold_ms ?? null,
8731
- reason: result.reason ?? result.error ?? null
8732
- }));
8167
+ function httpStatusBodyNotContainsFailures(result, check) {
8168
+ const forbidden = check.body_not_contains?.filter(Boolean) ?? [];
8169
+ if (!forbidden.length) return [];
8170
+ const observed = isRecord(result.body_not_contains) ? result.body_not_contains : {};
8171
+ return forbidden.filter((text) => observed[text] !== false);
8733
8172
  }
8734
- function profileSetupCanvasSignatureReceipts(results) {
8735
- return results.filter((result) => profileSetupResultAction(result) === "canvas_signature").map((result) => ({
8736
- ordinal: result.ordinal ?? null,
8737
- ok: result.ok !== false,
8738
- selector: result.selector ?? null,
8739
- frame_selector: result.frame_selector ?? null,
8740
- label: result.label ?? null,
8741
- hash: result.hash ?? null,
8742
- data_length: result.data_length ?? null,
8743
- width: result.width ?? null,
8744
- height: result.height ?? null,
8745
- css_width: result.css_width ?? null,
8746
- css_height: result.css_height ?? null,
8747
- compare_to: result.compare_to ?? null,
8748
- previous_hash: result.previous_hash ?? null,
8749
- changed: result.changed ?? null,
8750
- return_stored_to: result.return_stored_to ?? null,
8751
- reason: result.reason ?? result.error ?? null
8752
- }));
8173
+ function httpStatusBodyNotPatternFailures(result, check) {
8174
+ const forbidden = check.body_not_patterns?.filter(Boolean) ?? [];
8175
+ if (!forbidden.length) return [];
8176
+ const observed = isRecord(result.body_not_patterns) ? result.body_not_patterns : {};
8177
+ return forbidden.filter((pattern) => observed[pattern] !== false);
8753
8178
  }
8754
- function profileSetupCanvasSignatureStableHashGroups(results) {
8755
- const groups = /* @__PURE__ */ new Map();
8756
- for (const receipt of profileSetupCanvasSignatureReceipts(results)) {
8757
- if (receipt.ok === false) continue;
8758
- const hash = stringValue3(receipt.hash);
8759
- if (!hash) continue;
8760
- const selector = stringValue3(receipt.selector) || "canvas";
8761
- const frameSelector = stringValue3(receipt.frame_selector);
8762
- const key = String(frameSelector || "") + "\n" + selector;
8763
- const group = groups.get(key) || { selector, frame_selector: frameSelector, receipts: [] };
8764
- const ordinal = numberValue2(receipt.ordinal);
8765
- const label = stringValue3(receipt.label) || (ordinal === void 0 ? `capture-${group.receipts.length + 1}` : `#${ordinal}`);
8766
- group.receipts.push({ hash, label, ordinal });
8767
- groups.set(key, group);
8768
- }
8769
- const warnings = [];
8770
- for (const group of groups.values()) {
8771
- const receiptsByHash = /* @__PURE__ */ new Map();
8772
- for (const receipt of group.receipts) {
8773
- const hashReceipts = receiptsByHash.get(receipt.hash) || [];
8774
- hashReceipts.push(receipt);
8775
- receiptsByHash.set(receipt.hash, hashReceipts);
8776
- }
8777
- for (const [hash, receipts] of receiptsByHash.entries()) {
8778
- const labels = [...new Set(receipts.map((receipt) => receipt.label))];
8779
- if (receipts.length < 2 || labels.length < 2) continue;
8780
- const visibleLabels = labels.slice(0, 8);
8781
- warnings.push({
8782
- selector: group.selector,
8783
- frame_selector: group.frame_selector ?? null,
8784
- hash,
8785
- count: receipts.length,
8786
- label_count: labels.length,
8787
- labels: visibleLabels,
8788
- omitted_label_count: Math.max(0, labels.length - visibleLabels.length),
8789
- ordinals: receipts.map((receipt) => receipt.ordinal).filter((value) => value !== void 0).slice(0, 12),
8790
- reason: "stable_canvas_signature_hash"
8791
- });
8792
- }
8793
- }
8794
- return warnings;
8795
- }
8796
- function profileSetupClickSequenceReceipts(clickedItems) {
8797
- const nthChildPattern = /:nth-child\((\d+)\)/;
8798
- const nthChildTemplatePattern = /:nth-child\(\d+\)/g;
8799
- const groups = /* @__PURE__ */ new Map();
8800
- for (const item of clickedItems) {
8801
- const selector = stringValue3(item.selector);
8802
- if (!selector) continue;
8803
- const frameSelector = stringValue3(item.frame_selector);
8804
- const clickCountValue = numberValue2(item.click_count);
8805
- const clickCount = clickCountValue === void 0 ? 1 : Math.max(1, Math.min(100, Math.floor(clickCountValue)));
8806
- const ordinal = numberValue2(item.ordinal);
8807
- const match = nthChildPattern.exec(selector);
8808
- const selectorTemplate = match ? selector.replace(nthChildTemplatePattern, ":nth-child(*)") : selector;
8809
- const valueSource = match ? "nth-child" : "same-selector";
8810
- const key = `${valueSource}
8811
- ${frameSelector || ""}
8812
- ${selectorTemplate}`;
8813
- const group = groups.get(key) || {
8814
- selector_template: selectorTemplate,
8815
- frame_selector: frameSelector,
8816
- value_source: valueSource,
8817
- sequence: [],
8818
- ordinals: [],
8819
- result_count: 0,
8820
- click_total: 0
8821
- };
8822
- if (match) {
8823
- const value = Number(match[1]);
8824
- for (let index = 0; index < clickCount; index += 1) group.sequence.push(value);
8825
- }
8826
- if (ordinal !== void 0) group.ordinals.push(ordinal);
8827
- group.result_count += 1;
8828
- group.click_total += clickCount;
8829
- groups.set(key, group);
8830
- }
8831
- return [...groups.values()].filter((group) => group.result_count >= 4).map((group) => {
8832
- const sequence = group.sequence.slice(0, 32);
8833
- const ordinals = group.ordinals.slice(0, 16);
8834
- return {
8835
- selector_template: group.selector_template,
8836
- frame_selector: group.frame_selector ?? null,
8837
- value_source: group.value_source,
8838
- result_count: group.result_count,
8839
- click_total: group.click_total,
8840
- sequence,
8841
- omitted_sequence_count: Math.max(0, group.sequence.length - sequence.length),
8842
- ordinals,
8843
- omitted_ordinal_count: Math.max(0, group.ordinals.length - ordinals.length)
8844
- };
8845
- });
8846
- }
8847
- function sampleProfileSetupSummaryItems(items, limit) {
8848
- if (items.length <= limit) return items;
8849
- const firstCount = Math.floor(limit / 2);
8850
- const lastCount = limit - firstCount;
8851
- return [...items.slice(0, firstCount), ...items.slice(-lastCount)];
8852
- }
8853
- function profileScreenshotLabels(viewports) {
8854
- const labels = [];
8855
- for (const viewport of viewports || []) {
8856
- if (viewport.screenshot_label) labels.push(viewport.screenshot_label);
8857
- labels.push(...profileSetupScreenshotLabels(viewport.setup_action_results || []));
8858
- }
8859
- return labels;
8860
- }
8861
- function profileFinalScreenshotLabels(viewports) {
8862
- return (viewports || []).map((viewport) => viewport.screenshot_label).filter((label) => typeof label === "string" && Boolean(label.trim()));
8863
- }
8864
- function profileAllSetupScreenshotLabels(viewports) {
8865
- return (viewports || []).flatMap((viewport) => profileSetupScreenshotLabels(viewport.setup_action_results || []));
8866
- }
8867
- function profileSetupSummary(viewports, actionCount, expectedActionCountByViewport, finalScreenshotFullPage) {
8868
- const normalizedFinalScreenshotFullPage = finalScreenshotFullPage === void 0 ? void 0 : finalScreenshotFullPage !== false;
8869
- const finalScreenshotCount = viewports.filter((viewport) => typeof viewport.screenshot_label === "string" && viewport.screenshot_label.trim()).length;
8870
- return toJsonValue({
8871
- viewport_count: viewports.length,
8872
- action_count: actionCount ?? null,
8873
- final_screenshot_count: finalScreenshotCount,
8874
- final_screenshot_full_page: normalizedFinalScreenshotFullPage ?? null,
8875
- final_screenshot_mode: normalizedFinalScreenshotFullPage === void 0 ? null : normalizedFinalScreenshotFullPage ? "full_page" : "viewport",
8876
- viewports: viewports.map((viewport) => {
8877
- const expectedActionCount = expectedActionCountByViewport?.get(viewport.name) ?? actionCount;
8878
- const viewportFinalScreenshotFullPage = typeof viewport.screenshot_full_page === "boolean" ? viewport.screenshot_full_page : normalizedFinalScreenshotFullPage;
8879
- const results = viewport.setup_action_results || [];
8880
- const failed = results.filter((result) => result.ok === false && result.optional !== true);
8881
- const optionalFailed = results.filter((result) => result.ok === false && result.optional === true);
8882
- const successfulClicks = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false);
8883
- 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);
8884
- const windowCallUntilReceipts = profileSetupWindowCallUntilReceipts(results);
8885
- 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);
8886
- const sampledWindowCallUntilReceipts = sampleProfileSetupSummaryItems(windowCallUntilReceipts, 8);
8887
- const windowCallReceipts = profileSetupWindowCallReceipts(results);
8888
- const windowCallStoredTotal = windowCallReceipts.filter((result) => typeof result.return_stored_to === "string" && result.return_stored_to.trim()).length;
8889
- const windowCallCapturedTotal = windowCallReceipts.filter((result) => result.return_captured === true).length;
8890
- const sampledWindowCallReceipts = sampleProfileSetupSummaryItems(windowCallReceipts, 8);
8891
- const windowEvalReceipts = profileSetupWindowEvalReceipts(results);
8892
- const windowEvalStoredTotal = windowEvalReceipts.filter((result) => typeof result.return_stored_to === "string" && result.return_stored_to.trim()).length;
8893
- const windowEvalCapturedTotal = windowEvalReceipts.filter((result) => result.return_captured === true).length;
8894
- const sampledWindowEvalReceipts = sampleProfileSetupSummaryItems(windowEvalReceipts, 8);
8895
- const deterministicRuntimeReceipts = profileSetupDeterministicRuntimeReceipts(results);
8896
- const sampledDeterministicRuntimeReceipts = sampleProfileSetupSummaryItems(deterministicRuntimeReceipts, 8);
8897
- const rangeValueReceipts = profileSetupRangeValueReceipts(results);
8898
- const sampledRangeValueReceipts = sampleProfileSetupSummaryItems(rangeValueReceipts, 8);
8899
- const dragReceipts = profileSetupDragReceipts(results);
8900
- const sampledDragReceipts = sampleProfileSetupSummaryItems(dragReceipts, 8);
8901
- const tapReceipts = profileSetupTapReceipts(results);
8902
- const sampledTapReceipts = sampleProfileSetupSummaryItems(tapReceipts, 8);
8903
- const tapUntilReceipts = profileSetupTapUntilReceipts(results);
8904
- const tapUntilTapCounts = tapUntilReceipts.map((result) => typeof result.tap_count === "number" && Number.isFinite(result.tap_count) ? result.tap_count : void 0).filter((value) => value !== void 0);
8905
- const sampledTapUntilReceipts = sampleProfileSetupSummaryItems(tapUntilReceipts, 8);
8906
- const keyboardReceipts = profileSetupKeyboardReceipts(results);
8907
- const sampledKeyboardReceipts = sampleProfileSetupSummaryItems(keyboardReceipts, 8);
8908
- const canvasSignatureReceipts = profileSetupCanvasSignatureReceipts(results);
8909
- const sampledCanvasSignatureReceipts = sampleProfileSetupSummaryItems(canvasSignatureReceipts, 8);
8910
- const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
8911
- const clickCount = typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0;
8912
- return {
8913
- ordinal: result.ordinal ?? null,
8914
- selector: result.selector ?? null,
8915
- frame_selector: result.frame_selector ?? null,
8916
- text: compactProfileSetupSummaryText(result.text),
8917
- ...result.fallback_to_tap === true ? { fallback_to_tap: true } : {},
8918
- ...result.input_dispatch ? { input_dispatch: result.input_dispatch } : {},
8919
- ...result.click_error ? { click_error: compactProfileSetupSummaryText(result.click_error) } : {},
8920
- ...clickCount ? { click_count: clickCount } : {}
8921
- };
8922
- });
8923
- const clicked = sampleProfileSetupSummaryItems(clickedItems, 8);
8924
- const clickSequences = profileSetupClickSequenceReceipts(clickedItems);
8925
- const sampledClickSequences = sampleProfileSetupSummaryItems(clickSequences, 6);
8926
- const text_samples = results.filter((result) => result.ok !== false && typeof result.text === "string" && (profileSetupResultAction(result) === "assert_text_visible" || profileSetupResultAction(result) === "assert_text_absent" || profileSetupResultAction(result) === "wait_for_text")).map((result) => ({
8927
- ordinal: result.ordinal ?? null,
8928
- action: profileSetupResultAction(result),
8929
- frame_selector: result.frame_selector ?? null,
8930
- text: compactProfileSetupSummaryText(result.text)
8931
- })).filter((item) => item.text).slice(-6);
8932
- return {
8933
- name: viewport.name,
8934
- expected_action_count: expectedActionCount ?? null,
8935
- ok: (expectedActionCount === void 0 ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
8936
- result_count: results.length,
8937
- observed_path: viewport.route?.observed ?? null,
8938
- final_url: viewport.url ?? null,
8939
- action_counts: profileSetupActionCounts(results),
8940
- frame_action_count: results.filter((result) => result.frame_selector).length,
8941
- frame_urls: profileSetupFrameUrls(viewport),
8942
- final_screenshot: viewport.screenshot_label ?? null,
8943
- final_screenshot_full_page: viewportFinalScreenshotFullPage ?? null,
8944
- setup_screenshots: profileSetupScreenshotLabels(results),
8945
- clicked_total: clickedItems.length,
8946
- clicked_truncated: clickedItems.length > clicked.length,
8947
- click_sequence_total: clickSequences.length,
8948
- click_sequence_truncated: clickSequences.length > sampledClickSequences.length,
8949
- click_sequences: sampledClickSequences,
8950
- click_count_action_total: clickCountValues.length,
8951
- click_count_value_total: clickCountValues.reduce((sum, value) => sum + value, 0),
8952
- window_call_until_total: windowCallUntilReceipts.length,
8953
- window_call_until_call_total: windowCallUntilCallCounts.reduce((sum, value) => sum + value, 0),
8954
- window_call_until_truncated: windowCallUntilReceipts.length > sampledWindowCallUntilReceipts.length,
8955
- window_call_until: sampledWindowCallUntilReceipts,
8956
- window_call_total: windowCallReceipts.length,
8957
- window_call_stored_total: windowCallStoredTotal,
8958
- window_call_captured_total: windowCallCapturedTotal,
8959
- window_call_truncated: windowCallReceipts.length > sampledWindowCallReceipts.length,
8960
- window_call: sampledWindowCallReceipts,
8961
- window_eval_total: windowEvalReceipts.length,
8962
- window_eval_stored_total: windowEvalStoredTotal,
8963
- window_eval_captured_total: windowEvalCapturedTotal,
8964
- window_eval_truncated: windowEvalReceipts.length > sampledWindowEvalReceipts.length,
8965
- window_eval: sampledWindowEvalReceipts,
8966
- deterministic_runtime_total: deterministicRuntimeReceipts.length,
8967
- deterministic_runtime_truncated: deterministicRuntimeReceipts.length > sampledDeterministicRuntimeReceipts.length,
8968
- deterministic_runtime: sampledDeterministicRuntimeReceipts,
8969
- set_range_value_total: rangeValueReceipts.length,
8970
- set_range_value_truncated: rangeValueReceipts.length > sampledRangeValueReceipts.length,
8971
- set_range_value: sampledRangeValueReceipts,
8972
- drag_total: dragReceipts.length,
8973
- drag_truncated: dragReceipts.length > sampledDragReceipts.length,
8974
- drag: sampledDragReceipts,
8975
- tap_total: tapReceipts.length,
8976
- tap_truncated: tapReceipts.length > sampledTapReceipts.length,
8977
- tap: sampledTapReceipts,
8978
- tap_until_total: tapUntilReceipts.length,
8979
- tap_until_tap_total: tapUntilTapCounts.reduce((sum, value) => sum + value, 0),
8980
- tap_until_truncated: tapUntilReceipts.length > sampledTapUntilReceipts.length,
8981
- tap_until: sampledTapUntilReceipts,
8982
- keyboard_total: keyboardReceipts.length,
8983
- keyboard_truncated: keyboardReceipts.length > sampledKeyboardReceipts.length,
8984
- keyboard: sampledKeyboardReceipts,
8985
- canvas_signature_total: canvasSignatureReceipts.length,
8986
- canvas_signature_truncated: canvasSignatureReceipts.length > sampledCanvasSignatureReceipts.length,
8987
- canvas_signature: sampledCanvasSignatureReceipts,
8988
- canvas_signature_stable_hash_groups: profileSetupCanvasSignatureStableHashGroups(results),
8989
- clicked,
8990
- text_samples,
8991
- failed: failed.map((result) => ({
8992
- ordinal: result.ordinal ?? null,
8993
- action: profileSetupResultAction(result),
8994
- selector: result.selector ?? null,
8995
- frame_selector: result.frame_selector ?? null,
8996
- reason: result.reason ?? result.error ?? null,
8997
- error: result.error ?? null,
8998
- case_insensitive_text: compactProfileSetupSummaryText(result.case_insensitive_text)
8999
- })),
9000
- optional_failed: optionalFailed.map((result) => ({
9001
- ordinal: result.ordinal ?? null,
9002
- action: profileSetupResultAction(result),
9003
- selector: result.selector ?? null,
9004
- frame_selector: result.frame_selector ?? null,
9005
- reason: result.reason ?? result.error ?? null,
9006
- error: result.error ?? null,
9007
- case_insensitive_text: compactProfileSetupSummaryText(result.case_insensitive_text)
9008
- }))
9009
- };
9010
- })
9011
- });
9012
- }
9013
- function normalizeName(value, fallback) {
9014
- const name = stringValue3(value) || fallback;
9015
- return name.replace(/\s+/g, " ").trim();
9016
- }
9017
- function slugifyRiddleProofProfileName(value) {
9018
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "profile";
9019
- }
9020
- function normalizeViewport(input, index) {
9021
- if (!isRecord(input)) throw new Error(`target.viewports[${index}] must be an object.`);
9022
- const width = numberValue2(input.width);
9023
- const height = numberValue2(input.height);
9024
- if (!width || !height || width < 100 || height < 100) {
9025
- throw new Error(`target.viewports[${index}] requires numeric width and height >= 100.`);
9026
- }
9027
- const hasTouch = booleanValue2(valueFromOwn(input, "hasTouch", "has_touch"));
9028
- const isMobile = booleanValue2(valueFromOwn(input, "isMobile", "is_mobile"));
9029
- return {
9030
- name: normalizeName(input.name || input.label, `viewport-${index + 1}`),
9031
- width: Math.round(width),
9032
- height: Math.round(height),
9033
- ...hasTouch === void 0 ? {} : { hasTouch },
9034
- ...isMobile === void 0 ? {} : { isMobile }
9035
- };
9036
- }
9037
- function normalizeViewports(value) {
9038
- if (value === void 0) return [...DEFAULT_VIEWPORTS];
9039
- if (!Array.isArray(value) || value.length === 0) throw new Error("target.viewports must be a non-empty array.");
9040
- return value.map(normalizeViewport);
9041
- }
9042
- function isSupportedCheckType(value) {
9043
- return RIDDLE_PROOF_PROFILE_CHECK_TYPES.includes(value);
9044
- }
9045
- function normalizeSetupActionType(value, index) {
9046
- const normalizedInput = String(value || "").trim().replace(/-/g, "_");
9047
- 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 === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "tap_until" || normalizedInput === "pointer_tap_until" || normalizedInput === "touch_tap_until" || normalizedInput === "canvas_tap_until" || normalizedInput === "tap_repeat_until" || normalizedInput === "repeat_tap_until" ? "tap_until" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "keyboard_down" || normalizedInput === "key_down" || normalizedInput === "keydown" || normalizedInput === "press_down" ? "key_down" : normalizedInput === "keyboard_up" || normalizedInput === "key_up" || normalizedInput === "keyup" || normalizedInput === "press_up" || normalizedInput === "release_key" || normalizedInput === "key_release" ? "key_up" : 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;
9048
- if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
9049
- return normalized;
9050
- }
9051
- throw new Error(`target.setup_actions[${index}].type ${value || "(missing)"} is not supported. Supported actions: ${RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.join(", ")}`);
9052
- }
9053
- function normalizeSetupActionStorage(value, index) {
9054
- if (value === void 0 || value === null || value === "") return void 0;
9055
- const normalized = String(value).trim().replace(/-/g, "_");
9056
- if (normalized === "local" || normalized === "local_storage") return "local";
9057
- if (normalized === "session" || normalized === "session_storage") return "session";
9058
- if (normalized === "both" || normalized === "all") return "both";
9059
- throw new Error(`target.setup_actions[${index}].storage ${String(value)} is not supported. Supported storage values: local, session, both.`);
9060
- }
9061
- function normalizeSetupActionArgs(input, index) {
9062
- const argsInput = valueFromOwn(input, "args", "arguments", "args_json", "argsJson");
9063
- if (argsInput === void 0) return void 0;
9064
- if (!Array.isArray(argsInput)) {
9065
- throw new Error(`target.setup_actions[${index}] window_call/window_eval args must be an array.`);
9066
- }
9067
- return argsInput.map(toJsonValue);
9068
- }
9069
- function normalizeReturnSummaryFields(input, index) {
9070
- const fieldsInput = valueFromOwn(
9071
- input,
9072
- "return_summary_fields",
9073
- "returnSummaryFields",
9074
- "summary_fields",
9075
- "summaryFields",
9076
- "receipt_fields",
9077
- "receiptFields",
9078
- "return_receipt_fields",
9079
- "returnReceiptFields"
9080
- );
9081
- if (fieldsInput === void 0) return void 0;
9082
- if (!Array.isArray(fieldsInput)) {
9083
- throw new Error(`target.setup_actions[${index}].return_summary_fields must be an array.`);
9084
- }
9085
- return fieldsInput.map((field, fieldIndex) => {
9086
- if (typeof field === "string") {
9087
- const path7 = field.trim();
9088
- if (!path7) throw new Error(`target.setup_actions[${index}].return_summary_fields[${fieldIndex}] requires path.`);
9089
- return { path: path7 };
9090
- }
9091
- if (!isRecord(field)) {
9092
- throw new Error(`target.setup_actions[${index}].return_summary_fields[${fieldIndex}] must be a string path or object.`);
9093
- }
9094
- const path6 = stringFromOwn(field, "path", "key", "json_path", "jsonPath");
9095
- if (!path6) throw new Error(`target.setup_actions[${index}].return_summary_fields[${fieldIndex}] requires path.`);
9096
- const label = stringFromOwn(field, "label", "name", "title");
9097
- return label ? { path: path6, label } : { path: path6 };
9098
- });
9099
- }
9100
- function normalizeSetupActionRepeat(input, index) {
9101
- const repeat = numberValue2(valueFromOwn(input, "repeat", "repeat_count", "repeatCount", "times"));
9102
- if (repeat === void 0) return void 0;
9103
- if (!Number.isInteger(repeat) || repeat < 1 || repeat > 100) {
9104
- throw new Error(`target.setup_actions[${index}].repeat must be an integer from 1 to 100.`);
9105
- }
9106
- return repeat;
9107
- }
9108
- function normalizeSetupActionClickCount(input, type, index) {
9109
- const clickCountInput = valueFromOwn(input, "click_count", "clickCount", "clicks");
9110
- if (clickCountInput === void 0) return void 0;
9111
- if (type !== "click") {
9112
- throw new Error(`target.setup_actions[${index}].click_count is only supported for click actions.`);
9113
- }
9114
- const clickCount = numberValue2(clickCountInput);
9115
- if (clickCount === void 0 || !Number.isInteger(clickCount) || clickCount < 1 || clickCount > 10) {
9116
- throw new Error(`target.setup_actions[${index}].click_count must be an integer from 1 to 10.`);
9117
- }
9118
- return clickCount;
9119
- }
9120
- function normalizeSetupActionCoordinateMode(value, index) {
9121
- if (value === void 0 || value === null || value === "") return void 0;
9122
- const normalized = String(value).trim().replace(/-/g, "_").toLowerCase();
9123
- if (normalized === "pixels" || normalized === "pixel" || normalized === "px") return "pixels";
9124
- if (normalized === "ratio" || normalized === "relative" || normalized === "fraction") return "ratio";
9125
- throw new Error(`target.setup_actions[${index}].coordinate_mode ${String(value)} is not supported. Supported coordinate modes: pixels, ratio.`);
9126
- }
9127
- function normalizeSetupActionPointerType(value, type, index) {
9128
- if (value === void 0 || value === null || value === "") return void 0;
9129
- if (type !== "drag" && type !== "tap" && type !== "tap_until") {
9130
- throw new Error(`target.setup_actions[${index}].pointer_type is only supported for drag/tap/tap_until actions.`);
9131
- }
9132
- const normalized = String(value).trim().replace(/-/g, "_").toLowerCase();
9133
- if (normalized === "mouse") return "mouse";
9134
- if (normalized === "touch" || normalized === "finger") return "touch";
9135
- if (normalized === "pen" || normalized === "stylus") return "pen";
9136
- throw new Error(`target.setup_actions[${index}].pointer_type ${String(value)} is not supported. Supported pointer types: mouse, touch, pen.`);
9137
- }
9138
- function normalizeSetupActionScreenshotFullPage(input, type, index) {
9139
- const directFullPage = booleanValue2(valueFromOwn(input, "full_page", "fullPage"));
9140
- const viewportOnly = booleanValue2(valueFromOwn(input, "viewport_only", "viewportOnly", "viewport_screenshot", "viewportScreenshot"));
9141
- if (type !== "screenshot") {
9142
- if (directFullPage !== void 0 || viewportOnly !== void 0 || valueFromOwn(input, "screenshot_mode", "screenshotMode", "capture_mode", "captureMode") !== void 0) {
9143
- throw new Error(`target.setup_actions[${index}].full_page is only supported for screenshot actions.`);
9144
- }
9145
- return void 0;
9146
- }
9147
- const modeInput = stringFromOwn(input, "mode", "screenshot_mode", "screenshotMode", "capture_mode", "captureMode");
9148
- let modeFullPage;
9149
- if (modeInput) {
9150
- const mode = modeInput.trim().toLowerCase().replace(/[-\s]+/g, "_");
9151
- if (mode === "full_page" || mode === "fullpage" || mode === "page" || mode === "document") {
9152
- modeFullPage = true;
9153
- } else if (mode === "viewport" || mode === "view") {
9154
- modeFullPage = false;
9155
- } else {
9156
- throw new Error(`target.setup_actions[${index}].mode ${modeInput} is not supported for screenshot actions. Supported modes: full_page, viewport.`);
9157
- }
9158
- }
9159
- const values = [directFullPage, viewportOnly === void 0 ? void 0 : !viewportOnly, modeFullPage].filter((value) => value !== void 0);
9160
- if (!values.length) return void 0;
9161
- if (values.some((value) => value !== values[0])) {
9162
- throw new Error(`target.setup_actions[${index}] has conflicting screenshot full_page / viewport mode options.`);
9163
- }
9164
- return values[0];
9165
- }
9166
- function normalizeSetupActionRandomQueue(input, index) {
9167
- const rawQueue = valueFromOwn(
9168
- input,
9169
- "random_queue",
9170
- "randomQueue",
9171
- "random_values",
9172
- "randomValues",
9173
- "random_sequence",
9174
- "randomSequence",
9175
- "math_random",
9176
- "mathRandom"
9177
- );
9178
- if (rawQueue === void 0) return void 0;
9179
- if (!Array.isArray(rawQueue) || rawQueue.length === 0) {
9180
- throw new Error(`target.setup_actions[${index}].random_queue must be a non-empty array of numbers from 0 inclusive to 1 exclusive.`);
9181
- }
9182
- return rawQueue.map((item, queueIndex) => {
9183
- const value = numberValue2(item);
9184
- if (value === void 0 || value < 0 || value >= 1) {
9185
- throw new Error(`target.setup_actions[${index}].random_queue[${queueIndex}] must be a finite number from 0 inclusive to 1 exclusive.`);
9186
- }
9187
- return value;
9188
- });
9189
- }
9190
- function normalizeSetupActionNonNegativeNumber(input, index, outputKey, ...keys) {
9191
- const rawValue = valueFromOwn(input, ...keys);
9192
- if (rawValue === void 0) return void 0;
9193
- const value = numberValue2(rawValue);
9194
- if (value === void 0 || value < 0) {
9195
- throw new Error(`target.setup_actions[${index}].${outputKey} must be a finite non-negative number.`);
9196
- }
9197
- return value;
9198
- }
9199
- function normalizeSetupAction(input, index) {
9200
- if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
9201
- const type = normalizeSetupActionType(stringValue3(input.type), index);
9202
- const rawType = String(input.type || "").trim().replace(/-/g, "_").toLowerCase();
9203
- const selector = stringValue3(input.selector);
9204
- const frameSelector = stringFromOwn(input, "frame_selector", "frameSelector", "iframe_selector", "iframeSelector");
9205
- const frameIndex = numberValue2(valueFromOwn(input, "frame_index", "frameIndex", "iframe_index", "iframeIndex"));
9206
- if (frameIndex !== void 0 && (!Number.isInteger(frameIndex) || frameIndex < 0)) {
9207
- throw new Error(`target.setup_actions[${index}].frame_index must be a non-negative integer.`);
9208
- }
9209
- if ((type === "click" || type === "tap" || type === "tap_until" || type === "drag" || type === "fill" || type === "set_input_value" || type === "set_range_value" || type === "canvas_signature" || type === "wait_for_selector" || type === "wait_for_text" || type === "assert_text_visible" || type === "assert_text_absent" || type === "assert_selector_count") && !selector) {
9210
- throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
9211
- }
9212
- const fromX = type === "click" || type === "tap" || type === "tap_until" ? numberValue2(valueFromOwn(input, "from_x", "fromX", "x", "click_x", "clickX", "start_x", "startX", "x1")) : numberValue2(valueFromOwn(input, "from_x", "fromX", "start_x", "startX", "x1"));
9213
- const fromY = type === "click" || type === "tap" || type === "tap_until" ? numberValue2(valueFromOwn(input, "from_y", "fromY", "y", "click_y", "clickY", "start_y", "startY", "y1")) : numberValue2(valueFromOwn(input, "from_y", "fromY", "start_y", "startY", "y1"));
9214
- const toX = numberValue2(valueFromOwn(input, "to_x", "toX", "end_x", "endX", "x2"));
9215
- const toY = numberValue2(valueFromOwn(input, "to_y", "toY", "end_y", "endY", "y2"));
9216
- const coordinateMode = normalizeSetupActionCoordinateMode(valueFromOwn(input, "coordinate_mode", "coordinateMode", "coords", "units"), index);
9217
- const pointerType = normalizeSetupActionPointerType(valueFromOwn(input, "pointer_type", "pointerType", "input_type", "inputType"), type, index);
9218
- const durationMs = numberValue2(input.duration_ms) ?? numberValue2(input.durationMs);
9219
- const holdMs = type === "press" ? normalizeSetupActionNonNegativeNumber(input, index, "hold_ms", "hold_ms", "holdMs", "key_down_ms", "keyDownMs", "down_ms", "downMs") ?? durationMs : void 0;
9220
- if (type === "press" && holdMs !== void 0 && (holdMs < 0 || holdMs > 3e4)) {
9221
- throw new Error(`target.setup_actions[${index}].hold_ms must be a finite number from 0 to 30000.`);
9222
- }
9223
- if (type === "click") {
9224
- const hasClickCoordinate = fromX !== void 0 || fromY !== void 0;
9225
- if (hasClickCoordinate && (fromX === void 0 || fromY === void 0)) {
9226
- throw new Error(`target.setup_actions[${index}] click coordinates require both x and y.`);
9227
- }
9228
- if (hasClickCoordinate && fromX !== void 0 && fromY !== void 0) {
9229
- const clickCoordinates = [fromX, fromY];
9230
- if (coordinateMode === "ratio" && clickCoordinates.some((value2) => value2 < 0 || value2 > 1)) {
9231
- throw new Error(`target.setup_actions[${index}] click ratio coordinates must be between 0 and 1.`);
9232
- }
9233
- if ((coordinateMode === void 0 || coordinateMode === "pixels") && clickCoordinates.some((value2) => value2 < 0)) {
9234
- throw new Error(`target.setup_actions[${index}] click pixel coordinates must be non-negative.`);
9235
- }
9236
- }
9237
- }
9238
- if (type === "tap" || type === "tap_until") {
9239
- const hasTapCoordinate = fromX !== void 0 || fromY !== void 0;
9240
- if (hasTapCoordinate && (fromX === void 0 || fromY === void 0)) {
9241
- throw new Error(`target.setup_actions[${index}] ${type} coordinates require both x and y.`);
9242
- }
9243
- if (hasTapCoordinate && fromX !== void 0 && fromY !== void 0) {
9244
- const tapCoordinates = [fromX, fromY];
9245
- if (coordinateMode === "ratio" && tapCoordinates.some((value2) => value2 < 0 || value2 > 1)) {
9246
- throw new Error(`target.setup_actions[${index}] ${type} ratio coordinates must be between 0 and 1.`);
9247
- }
9248
- if ((coordinateMode === void 0 || coordinateMode === "pixels") && tapCoordinates.some((value2) => value2 < 0)) {
9249
- throw new Error(`target.setup_actions[${index}] ${type} pixel coordinates must be non-negative.`);
9250
- }
9251
- }
9252
- }
9253
- if (type === "drag") {
9254
- if (fromX === void 0 || fromY === void 0 || toX === void 0 || toY === void 0) {
9255
- throw new Error(`target.setup_actions[${index}] drag requires from_x, from_y, to_x, and to_y.`);
9256
- }
9257
- if (coordinateMode === "ratio" && [fromX, fromY, toX, toY].some((value2) => value2 < 0 || value2 > 1)) {
9258
- throw new Error(`target.setup_actions[${index}] drag ratio coordinates must be between 0 and 1.`);
9259
- }
9260
- if ((coordinateMode === void 0 || coordinateMode === "pixels") && [fromX, fromY, toX, toY].some((value2) => value2 < 0)) {
9261
- throw new Error(`target.setup_actions[${index}] drag pixel coordinates must be non-negative.`);
9262
- }
9263
- }
9264
- if (type === "wait_for_text" && !stringValue3(input.text) && !stringValue3(input.pattern)) {
9265
- throw new Error(`target.setup_actions[${index}] wait_for_text requires text or pattern.`);
9266
- }
9267
- if ((type === "assert_text_visible" || type === "assert_text_absent") && !stringValue3(input.text) && !stringValue3(input.pattern)) {
9268
- throw new Error(`target.setup_actions[${index}] ${type} requires text or pattern.`);
9269
- }
9270
- const expectedCount = numberValue2(input.expected_count ?? input.expectedCount ?? input.count);
9271
- if (type === "assert_selector_count" && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
9272
- throw new Error(`target.setup_actions[${index}] ${type} requires non-negative integer expected_count.`);
9273
- }
9274
- const value = stringFromOwn(input, "value", "input_value", "inputValue");
9275
- const hasJsonValue = hasOwn(input, "value_json") || hasOwn(input, "valueJson") || hasOwn(input, "json");
9276
- if ((type === "fill" || type === "set_input_value" || type === "set_range_value") && value === void 0 && !hasJsonValue) {
9277
- throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
9278
- }
9279
- const randomQueue = type === "deterministic_runtime" ? normalizeSetupActionRandomQueue(input, index) : void 0;
9280
- const deterministicNow = type === "deterministic_runtime" ? normalizeSetupActionNonNegativeNumber(input, index, "now", "now", "date_now", "dateNow", "mock_now", "mockNow", "clock", "timestamp", "time_ms", "timeMs") : void 0;
9281
- const deterministicAdvanceMs = type === "deterministic_runtime" ? normalizeSetupActionNonNegativeNumber(input, index, "advance_ms", "advance_ms", "advanceMs", "tick_ms", "tickMs", "add_ms", "addMs") : void 0;
9282
- const deterministicAppend = type === "deterministic_runtime" && booleanValue2(valueFromOwn(input, "append", "append_random", "appendRandom")) === true;
9283
- const deterministicRestore = type === "deterministic_runtime" && booleanValue2(valueFromOwn(input, "restore", "reset", "restore_originals", "restoreOriginals")) === true;
9284
- if (type === "deterministic_runtime" && randomQueue === void 0 && deterministicNow === void 0 && deterministicAdvanceMs === void 0 && !deterministicRestore) {
9285
- throw new Error(`target.setup_actions[${index}] deterministic_runtime requires random_queue, now, advance_ms, or restore.`);
9286
- }
9287
- const key = stringValue3(input.key);
9288
- let dialogAccept;
9289
- if (type === "dialog_response") {
9290
- const acceptInput = valueFromOwn(input, "accept", "accepted", "should_accept", "shouldAccept");
9291
- const responseInput = stringFromOwn(input, "response", "dialog_response", "dialogResponse", "mode");
9292
- const response = String(responseInput || "").trim().toLowerCase().replace(/-/g, "_");
9293
- if (acceptInput !== void 0) {
9294
- dialogAccept = acceptInput !== false && String(acceptInput).toLowerCase() !== "false";
9295
- } else if (rawType === "dismiss_dialog" || rawType === "dismiss_dialogs" || rawType === "cancel_dialog") {
9296
- dialogAccept = false;
9297
- } else if (response === "dismiss" || response === "cancel" || response === "reject" || response === "no" || response === "false") {
9298
- dialogAccept = false;
9299
- } else {
9300
- dialogAccept = true;
9301
- }
9302
- }
9303
- if ((type === "press" || type === "key_down" || type === "key_up") && !key) {
9304
- throw new Error(`target.setup_actions[${index}] ${type} requires key.`);
9305
- }
9306
- if ((type === "local_storage" || type === "session_storage") && !key) {
9307
- throw new Error(`target.setup_actions[${index}] ${type} requires key.`);
9308
- }
9309
- if ((type === "local_storage" || type === "session_storage") && value === void 0 && !hasJsonValue) {
9310
- throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
9311
- }
9312
- const path6 = stringFromOwn(input, "path", "function_path", "functionPath", "window_path", "windowPath", "state_path", "statePath");
9313
- if ((type === "window_call" || type === "window_call_until" || type === "assert_window_value" || type === "assert_window_number") && !path6) {
9314
- throw new Error(`target.setup_actions[${index}] ${type} requires path.`);
9315
- }
9316
- const script = stringFromOwn(input, "script", "code", "source", "body");
9317
- if (type === "window_eval" && !script) {
9318
- throw new Error(`target.setup_actions[${index}] window_eval requires script.`);
9319
- }
9320
- const args = type === "window_call" || type === "window_call_until" || type === "window_eval" ? normalizeSetupActionArgs(input, index) : void 0;
9321
- const hasExpectedValue = hasOwn(input, "expected_value") || hasOwn(input, "expectedValue") || hasOwn(input, "expected") || hasOwn(input, "expect_value") || hasOwn(input, "expectValue") || hasOwn(input, "expect");
9322
- if (type === "assert_window_value" && !hasExpectedValue) {
9323
- throw new Error(`target.setup_actions[${index}] ${type} requires expected_value.`);
9324
- }
9325
- const rawExpectedValue = valueFromOwn(input, "expected_value", "expectedValue", "expected", "expect_value", "expectValue", "expect");
9326
- const minValue = numberValue2(valueFromOwn(input, "min_value", "minValue", "minimum", "min", "at_least", "atLeast", "gte"));
9327
- const maxValue = numberValue2(valueFromOwn(input, "max_value", "maxValue", "maximum", "max", "at_most", "atMost", "lte"));
9328
- if (type === "assert_window_number") {
9329
- if (!hasExpectedValue && minValue === void 0 && maxValue === void 0) {
9330
- throw new Error(`target.setup_actions[${index}] ${type} requires expected_value, min_value, or max_value.`);
9331
- }
9332
- if (hasExpectedValue && numberValue2(rawExpectedValue) === void 0) {
9333
- throw new Error(`target.setup_actions[${index}] ${type} expected_value must be a finite number.`);
9334
- }
9335
- }
9336
- const hasExpectedReturn = hasOwn(input, "expect_return") || hasOwn(input, "expectReturn") || hasOwn(input, "expected_return") || hasOwn(input, "expectedReturn");
9337
- const storeReturnTo = stringFromOwn(
9338
- input,
9339
- "store_return_to",
9340
- "storeReturnTo",
9341
- "save_return_to",
9342
- "saveReturnTo",
9343
- "assign_return_to",
9344
- "assignReturnTo",
9345
- "return_state_path",
9346
- "returnStatePath",
9347
- "store_signature_to",
9348
- "storeSignatureTo",
9349
- "signature_path",
9350
- "signaturePath"
9351
- );
9352
- const captureReturn = input.capture_return === false || input.captureReturn === false || input.include_return === false || input.includeReturn === false || input.omit_return === true || input.omitReturn === true ? false : void 0;
9353
- const untilPath = stringFromOwn(input, "until_path", "untilPath", "until_state_path", "untilStatePath", "until_window_path", "untilWindowPath", "until");
9354
- 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");
9355
- if (type === "window_call_until" || type === "tap_until") {
9356
- if (!untilPath) {
9357
- throw new Error(`target.setup_actions[${index}] ${type} requires until_path.`);
9358
- }
9359
- if (!hasUntilExpectedValue) {
9360
- throw new Error(`target.setup_actions[${index}] ${type} requires until_expected_value.`);
9361
- }
9362
- }
9363
- const maxCalls = numberValue2(valueFromOwn(input, "max_calls", "maxCalls", "max_attempts", "maxAttempts", "attempts", "max_taps", "maxTaps", "tap_limit", "tapLimit"));
9364
- if ((type === "window_call_until" || type === "tap_until") && (maxCalls === void 0 || !Number.isInteger(maxCalls) || maxCalls < 1 || maxCalls > 100)) {
9365
- throw new Error(`target.setup_actions[${index}].max_calls must be an integer from 1 to 100.`);
9366
- }
9367
- const tapBurstSize = type === "tap_until" ? numberValue2(valueFromOwn(input, "tap_burst_size", "tapBurstSize", "burst_size", "burstSize", "check_every_taps", "checkEveryTaps", "predicate_interval_taps", "predicateIntervalTaps")) : void 0;
9368
- if (type === "tap_until" && tapBurstSize !== void 0 && (!Number.isInteger(tapBurstSize) || tapBurstSize < 1 || tapBurstSize > 100)) {
9369
- throw new Error(`target.setup_actions[${index}].tap_burst_size must be an integer from 1 to 100.`);
9370
- }
9371
- const settleMs = type === "tap_until" ? numberValue2(valueFromOwn(input, "settle_ms", "settleMs", "predicate_settle_ms", "predicateSettleMs", "post_burst_wait_ms", "postBurstWaitMs", "after_burst_ms", "afterBurstMs", "settle_after_tap_ms", "settleAfterTapMs")) : void 0;
9372
- if (type === "tap_until" && settleMs !== void 0 && (!Number.isInteger(settleMs) || settleMs < 0 || settleMs > 1e4)) {
9373
- throw new Error(`target.setup_actions[${index}].settle_ms must be an integer from 0 to 10000.`);
9374
- }
9375
- const intervalMs = numberValue2(valueFromOwn(input, "interval_ms", "intervalMs", "poll_ms", "pollMs", "call_interval_ms", "callIntervalMs"));
9376
- if ((type === "window_call_until" || type === "tap_until") && intervalMs !== void 0 && (!Number.isInteger(intervalMs) || intervalMs < 0 || intervalMs > 5e3)) {
9377
- throw new Error(`target.setup_actions[${index}].interval_ms must be an integer from 0 to 5000.`);
9378
- }
9379
- const steps = numberValue2(input.steps);
9380
- if (type === "drag" && steps !== void 0 && (!Number.isInteger(steps) || steps < 1 || steps > 100)) {
9381
- throw new Error(`target.setup_actions[${index}].steps must be an integer from 1 to 100.`);
9382
- }
9383
- return {
9384
- type,
9385
- selector,
9386
- frame_selector: frameSelector,
9387
- frame_index: frameIndex,
9388
- full_page: normalizeSetupActionScreenshotFullPage(input, type, index),
9389
- force: type === "click" && (input.force === true || input.force_click === true || input.forceClick === true),
9390
- fallback_to_tap: type === "click" && (input.fallback_to_tap === true || input.fallbackToTap === true || input.fallback_to_pointer_tap === true || input.fallbackToPointerTap === true || input.pointer_fallback === true || input.pointerFallback === true) || void 0,
9391
- click_count: normalizeSetupActionClickCount(input, type, index),
9392
- coordinate_mode: coordinateMode,
9393
- pointer_type: pointerType,
9394
- from_x: fromX,
9395
- from_y: fromY,
9396
- to_x: toX,
9397
- to_y: toY,
9398
- duration_ms: durationMs,
9399
- steps,
9400
- key,
9401
- hold_ms: holdMs,
9402
- value,
9403
- value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
9404
- random_queue: randomQueue,
9405
- now: deterministicNow,
9406
- advance_ms: deterministicAdvanceMs,
9407
- append: deterministicAppend || void 0,
9408
- restore: deterministicRestore || void 0,
9409
- label: stringFromOwn(input, "label", "name", "screenshot_label", "screenshotLabel"),
9410
- script,
9411
- path: path6,
9412
- args,
9413
- expect_return: hasExpectedReturn ? toJsonValue(valueFromOwn(input, "expect_return", "expectReturn", "expected_return", "expectedReturn")) : void 0,
9414
- store_return_to: storeReturnTo,
9415
- capture_return: captureReturn,
9416
- return_summary_fields: normalizeReturnSummaryFields(input, index),
9417
- compare_to: stringFromOwn(input, "compare_to", "compareTo", "previous_signature_path", "previousSignaturePath", "previous_path", "previousPath", "changed_from", "changedFrom"),
9418
- expect_changed: booleanValue2(valueFromOwn(input, "expect_changed", "expectChanged", "should_change", "shouldChange", "changed")),
9419
- until_path: untilPath,
9420
- until_expected_value: hasUntilExpectedValue ? toJsonValue(valueFromOwn(input, "until_expected_value", "untilExpectedValue", "until_expected", "untilExpected", "until_value", "untilValue", "expected_value", "expectedValue", "expected")) : void 0,
9421
- expected_path: stringFromOwn(input, "expected_path", "expectedPath", "expected_terminal_path", "expectedTerminalPath"),
9422
- expected_url: stringFromOwn(input, "expected_url", "expectedUrl", "expected_terminal_url", "expectedTerminalUrl"),
9423
- max_calls: maxCalls,
9424
- tap_burst_size: tapBurstSize,
9425
- settle_ms: settleMs,
9426
- interval_ms: intervalMs,
9427
- expected_value: hasExpectedValue ? toJsonValue(rawExpectedValue) : void 0,
9428
- min_value: minValue,
9429
- max_value: maxValue,
9430
- text: stringValue3(input.text),
9431
- pattern: stringValue3(input.pattern),
9432
- flags: stringValue3(input.flags),
9433
- accept: dialogAccept,
9434
- prompt_text: stringFromOwn(input, "prompt_text", "promptText", "prompt_value", "promptValue"),
9435
- message_text: stringFromOwn(input, "message_text", "messageText", "dialog_text", "dialogText"),
9436
- message_pattern: stringFromOwn(input, "message_pattern", "messagePattern", "dialog_pattern", "dialogPattern"),
9437
- index: numberValue2(valueFromOwn(input, "index", "target_index", "targetIndex")),
9438
- expected_count: expectedCount,
9439
- ms: numberValue2(input.ms) ?? numberValue2(input.wait_ms) ?? numberValue2(input.waitMs),
9440
- timeout_ms: numberValue2(input.timeout_ms) ?? numberValue2(input.timeoutMs),
9441
- after_ms: numberValue2(input.after_ms) ?? numberValue2(input.afterMs),
9442
- repeat: normalizeSetupActionRepeat(input, index),
9443
- reload: input.reload === true,
9444
- storage: normalizeSetupActionStorage(input.storage, index),
9445
- viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `target.setup_actions[${index}] viewports`),
9446
- optional: input.optional === true || input.required === false,
9447
- continue_on_failure: input.continue_on_failure === true || input.continueOnFailure === true
9448
- };
9449
- }
9450
- function normalizeSetupActions(value) {
9451
- if (value === void 0) return void 0;
9452
- if (!Array.isArray(value)) throw new Error("target.setup_actions must be an array.");
9453
- return value.map(normalizeSetupAction);
9454
- }
9455
- function normalizeTargetScreenshotFullPage(input) {
9456
- const directFullPage = booleanValue2(valueFromOwn(
9457
- input,
9458
- "screenshot_full_page",
9459
- "screenshotFullPage",
9460
- "final_screenshot_full_page",
9461
- "finalScreenshotFullPage",
9462
- "full_page_screenshots",
9463
- "fullPageScreenshots"
9464
- ));
9465
- const viewportOnly = booleanValue2(valueFromOwn(
9466
- input,
9467
- "viewport_screenshots",
9468
- "viewportScreenshots",
9469
- "viewport_only_screenshots",
9470
- "viewportOnlyScreenshots"
9471
- ));
9472
- const modeInput = stringFromOwn(input, "screenshot_mode", "screenshotMode", "final_screenshot_mode", "finalScreenshotMode");
9473
- let modeFullPage;
9474
- if (modeInput) {
9475
- const mode = modeInput.trim().toLowerCase().replace(/[-\s]+/g, "_");
9476
- if (mode === "full_page" || mode === "fullpage" || mode === "page" || mode === "document") {
9477
- modeFullPage = true;
9478
- } else if (mode === "viewport" || mode === "view") {
9479
- modeFullPage = false;
9480
- } else {
9481
- throw new Error(`target.screenshot_mode ${modeInput} is not supported. Supported modes: full_page, viewport.`);
9482
- }
9483
- }
9484
- const values = [directFullPage, viewportOnly === void 0 ? void 0 : !viewportOnly, modeFullPage].filter((value) => value !== void 0);
9485
- if (!values.length) return void 0;
9486
- if (values.some((value) => value !== values[0])) {
9487
- throw new Error("target has conflicting screenshot full_page / viewport mode options.");
9488
- }
9489
- return values[0];
9490
- }
9491
- function normalizeNetworkMock(input, index) {
9492
- if (!isRecord(input)) throw new Error(`target.network_mocks[${index}] must be an object.`);
9493
- const url = stringValue3(input.url) || stringValue3(input.glob) || stringValue3(input.pattern);
9494
- if (!url) throw new Error(`target.network_mocks[${index}] requires url.`);
9495
- const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
9496
- const responsesInput = input.responses ?? input.sequence;
9497
- const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
9498
- const requestBody = normalizeNetworkMockRequestBodyConstraints(input, `target.network_mocks[${index}]`);
9499
- const requiredHitCount = numberValue2(
9500
- input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
9501
- );
9502
- if (requiredHitCount !== void 0 && (!Number.isInteger(requiredHitCount) || requiredHitCount < 1)) {
9503
- throw new Error(`target.network_mocks[${index}].required_hit_count must be a positive integer.`);
9504
- }
9505
- const forbidden = input.forbidden === true || input.must_not_hit === true || input.mustNotHit === true || input.should_not_run === true || input.shouldNotRun === true;
9506
- const configuredMaxHitCount = numberValue2(
9507
- input.max_hit_count ?? input.maxHitCount ?? input.max_hits ?? input.maxHits
9508
- );
9509
- if (configuredMaxHitCount !== void 0 && (!Number.isInteger(configuredMaxHitCount) || configuredMaxHitCount < 0)) {
9510
- throw new Error(`target.network_mocks[${index}].max_hit_count must be a non-negative integer.`);
9511
- }
9512
- if (forbidden && configuredMaxHitCount !== void 0 && configuredMaxHitCount !== 0) {
9513
- throw new Error(`target.network_mocks[${index}].forbidden cannot be combined with max_hit_count greater than 0.`);
9514
- }
9515
- const maxHitCount = forbidden ? 0 : configuredMaxHitCount;
9516
- const required = input.required === false || maxHitCount === 0 && input.required !== true ? false : true;
9517
- const effectiveRequiredHitCount = required ? requiredHitCount ?? (Array.isArray(responses) && responses.length ? responses.length : 1) : 0;
9518
- if (maxHitCount !== void 0 && effectiveRequiredHitCount > maxHitCount) {
9519
- throw new Error(`target.network_mocks[${index}].max_hit_count cannot be less than its required hit count.`);
9520
- }
9521
- const sequenceScopeInput = stringValue3(
9522
- input.sequence_scope ?? input.sequenceScope ?? input.response_sequence_scope ?? input.responseSequenceScope
9523
- );
9524
- let sequenceScope;
9525
- if (sequenceScopeInput) {
9526
- const normalizedScope = sequenceScopeInput.toLowerCase().replace(/[-\s]+/g, "_");
9527
- if (normalizedScope === "global" || normalizedScope === "profile" || normalizedScope === "run") {
9528
- sequenceScope = "global";
9529
- } else if (normalizedScope === "viewport" || normalizedScope === "per_viewport" || normalizedScope === "viewport_scoped") {
9530
- sequenceScope = "viewport";
9531
- } else {
9532
- throw new Error(`target.network_mocks[${index}].sequence_scope must be "global" or "viewport".`);
9533
- }
9534
- }
9535
- return {
9536
- ...payload,
9537
- label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
9538
- url,
9539
- method: stringValue3(input.method)?.toUpperCase(),
9540
- responses,
9541
- repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
9542
- sequence_scope: sequenceScope,
9543
- required_hit_count: requiredHitCount,
9544
- max_hit_count: maxHitCount,
9545
- forbidden,
9546
- required,
9547
- capture_request_body: requestBody.capture_request_body,
9548
- request_body_contains: requestBody.request_body_contains,
9549
- request_body_patterns: requestBody.request_body_patterns,
9550
- request_body_not_contains: requestBody.request_body_not_contains,
9551
- request_body_not_patterns: requestBody.request_body_not_patterns
9552
- };
9553
- }
9554
- function normalizeNetworkMockRequestBodyConstraints(input, label) {
9555
- const requestBodyContains = normalizeStringList(
9556
- input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
9557
- `${label}.request_body_contains`
9558
- );
9559
- const requestBodyPatterns = normalizeStringList(
9560
- input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
9561
- `${label}.request_body_patterns`
9562
- );
9563
- validateRegexPatterns(requestBodyPatterns, `${label}.request_body_patterns`);
9564
- const requestBodyNotContains = normalizeStringList(
9565
- input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
9566
- `${label}.request_body_not_contains`
9567
- );
9568
- const requestBodyNotPatterns = normalizeStringList(
9569
- input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
9570
- `${label}.request_body_not_patterns`
9571
- );
9572
- validateRegexPatterns(requestBodyNotPatterns, `${label}.request_body_not_patterns`);
9573
- return {
9574
- capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
9575
- request_body_contains: requestBodyContains,
9576
- request_body_patterns: requestBodyPatterns,
9577
- request_body_not_contains: requestBodyNotContains,
9578
- request_body_not_patterns: requestBodyNotPatterns
9579
- };
9580
- }
9581
- function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
9582
- const status = numberValue2(input.status) ?? defaults.status ?? 200;
9583
- if (!Number.isInteger(status) || status < 100 || status > 599) {
9584
- throw new Error(`${label}.status must be an HTTP status code.`);
9585
- }
9586
- const body = stringValue3(input.body) ?? stringValue3(input.body_text) ?? stringValue3(input.bodyText) ?? defaults.body;
9587
- const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
9588
- const requestBody = normalizeNetworkMockRequestBodyConstraints(input, label);
9589
- const abort = normalizeNetworkMockAbort(input, label, defaults.abort, defaults.abort_error_code);
9590
- return {
9591
- label: stringValue3(input.label) || stringValue3(input.name) || defaults.label,
9592
- status,
9593
- content_type: stringValue3(input.content_type) || stringValue3(input.contentType) || defaults.content_type,
9594
- headers: stringRecord(input.headers) || defaults.headers,
9595
- body,
9596
- body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : defaults.body_json,
9597
- delay_ms: normalizeNetworkMockDelay(input, label, defaults.delay_ms),
9598
- abort: abort.abort,
9599
- abort_error_code: abort.abort_error_code,
9600
- capture_request_body: requestBody.capture_request_body,
9601
- request_body_contains: requestBody.request_body_contains,
9602
- request_body_patterns: requestBody.request_body_patterns,
9603
- request_body_not_contains: requestBody.request_body_not_contains,
9604
- request_body_not_patterns: requestBody.request_body_not_patterns
9605
- };
9606
- }
9607
- function normalizeNetworkMockAbort(input, label, defaultAbort, defaultErrorCode) {
9608
- const abortInput = input.abort ?? input.route_abort ?? input.routeAbort;
9609
- const explicitAbort = typeof abortInput === "string" ? abortInput.trim().length > 0 : abortInput === true;
9610
- const abort = explicitAbort || defaultAbort === true;
9611
- if (!abort) return {};
9612
- const codeInput = stringValue3(
9613
- typeof abortInput === "string" ? abortInput : input.abort_error_code ?? input.abortErrorCode ?? input.abort_error ?? input.abortError ?? input.network_error ?? input.networkError
9614
- ) || defaultErrorCode || "failed";
9615
- const abort_error_code = codeInput.toLowerCase();
9616
- if (!RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES.includes(abort_error_code)) {
9617
- throw new Error(`${label}.abort_error_code must be one of ${RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES.join(", ")}.`);
9618
- }
9619
- return { abort: true, abort_error_code };
9620
- }
9621
- function normalizeNetworkMockDelay(input, label, defaultValue) {
9622
- const value = numberValue2(
9623
- input.delay_ms ?? input.delayMs ?? input.wait_ms ?? input.waitMs ?? input.latency_ms ?? input.latencyMs
9624
- ) ?? defaultValue;
9625
- if (value === void 0) return void 0;
9626
- if (!Number.isInteger(value) || value < 0 || value > 6e4) {
9627
- throw new Error(`${label}.delay_ms must be an integer from 0 to 60000.`);
9628
- }
9629
- return value;
9630
- }
9631
- function normalizeNetworkMockResponses(value, mockIndex, defaults) {
9632
- if (value === void 0) return void 0;
9633
- if (!Array.isArray(value)) throw new Error(`target.network_mocks[${mockIndex}].responses must be an array.`);
9634
- if (!value.length) throw new Error(`target.network_mocks[${mockIndex}].responses must not be empty.`);
9635
- const responseDefaults = {
9636
- label: void 0,
9637
- status: defaults.status,
9638
- content_type: defaults.content_type,
9639
- headers: defaults.headers,
9640
- body: defaults.body,
9641
- body_json: defaults.body_json,
9642
- delay_ms: defaults.delay_ms,
9643
- abort: defaults.abort,
9644
- abort_error_code: defaults.abort_error_code
9645
- };
9646
- return value.map((response, responseIndex) => {
9647
- if (!isRecord(response)) {
9648
- throw new Error(`target.network_mocks[${mockIndex}].responses[${responseIndex}] must be an object.`);
9649
- }
9650
- return normalizeNetworkMockResponsePayload(
9651
- response,
9652
- `target.network_mocks[${mockIndex}].responses[${responseIndex}]`,
9653
- responseDefaults
9654
- );
9655
- });
9656
- }
9657
- function normalizeNetworkMocks(value) {
9658
- if (value === void 0) return void 0;
9659
- if (!Array.isArray(value)) throw new Error("target.network_mocks must be an array.");
9660
- return value.map(normalizeNetworkMock);
9661
- }
9662
- function profileStringList(value) {
9663
- return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
9664
- }
9665
- function responseHasRequestBodyContract(response) {
9666
- if (!response) return false;
9667
- return profileStringList(response.request_body_contains).length > 0 || profileStringList(response.request_body_patterns).length > 0 || profileStringList(response.request_body_not_contains).length > 0 || profileStringList(response.request_body_not_patterns).length > 0;
9668
- }
9669
- function responseHasOnlyPositiveTextSelector(response) {
9670
- if (!response) return false;
9671
- return profileStringList(response.request_body_contains).length > 0 && profileStringList(response.request_body_patterns).length === 0 && profileStringList(response.request_body_not_contains).length === 0 && profileStringList(response.request_body_not_patterns).length === 0;
9672
- }
9673
- function responseMayShadowLaterRequestBodyMatch(earlier, later) {
9674
- if (!responseHasOnlyPositiveTextSelector(earlier) || !responseHasRequestBodyContract(later)) return false;
9675
- const earlierContains = profileStringList(earlier.request_body_contains);
9676
- const laterContains = profileStringList(later.request_body_contains);
9677
- if (!laterContains.length) return false;
9678
- return earlierContains.every((fragment) => laterContains.includes(fragment));
9679
- }
9680
- function responseLabel(response, index) {
9681
- return response.label ? `"${response.label}"` : `responses[${index}]`;
9682
- }
9683
- function collectRiddleProofProfileWarnings(profile) {
9684
- const warnings = [];
9685
- for (const [mockIndex, mock] of (profile.target.network_mocks || []).entries()) {
9686
- const responses = Array.isArray(mock.responses) ? mock.responses : [];
9687
- if (responses.length < 2) continue;
9688
- for (let laterIndex = 1; laterIndex < responses.length; laterIndex += 1) {
9689
- const later = responses[laterIndex];
9690
- if (!responseHasRequestBodyContract(later)) continue;
9691
- for (let earlierIndex = 0; earlierIndex < laterIndex; earlierIndex += 1) {
9692
- const earlier = responses[earlierIndex];
9693
- if (!responseMayShadowLaterRequestBodyMatch(earlier, later)) continue;
9694
- const mockLabel = mock.label ? ` (${mock.label})` : "";
9695
- warnings.push(
9696
- `target.network_mocks[${mockIndex}]${mockLabel} ${responseLabel(earlier, earlierIndex)} can shadow ${responseLabel(later, laterIndex)} because the earlier request_body_contains fragments are a subset of the later response and have no negative or pattern disambiguator. First matching request-body response wins.`
9697
- );
9698
- break;
9699
- }
9700
- }
9701
- }
9702
- return warnings;
9703
- }
9704
- function profileStatusProbeCounts(profile) {
9705
- let httpStatus = 0;
9706
- let linkStatus = 0;
9707
- for (const check of profile.checks || []) {
9708
- if (check.type === "http_status") httpStatus += 1;
9709
- if (check.type === "link_status" || check.type === "artifact_link_status") linkStatus += 1;
9710
- }
9711
- return { httpStatus, linkStatus };
9712
- }
9713
- function riddleApiStrictSafetyWarnings(blocker) {
9714
- const rawWarnings = Array.isArray(blocker?.warnings) ? blocker.warnings.filter((warning) => typeof warning === "string") : [];
9715
- const errorText = typeof blocker?.error === "string" ? blocker.error.toLowerCase() : "";
9716
- const hasSafetyError = errorText.includes("potentially unsafe operations");
9717
- const hasDirectNetworkWarning = rawWarnings.some((warning) => warning.toLowerCase().includes("direct network operations"));
9718
- return hasSafetyError || hasDirectNetworkWarning ? rawWarnings : [];
9719
- }
9720
- function collectRiddleProofProfileEnvironmentBlockedWarnings(profile, blocker) {
9721
- const warnings = collectRiddleProofProfileWarnings(profile);
9722
- const safetyWarnings = riddleApiStrictSafetyWarnings(blocker);
9723
- if (!safetyWarnings.length) return warnings;
9724
- const counts = profileStatusProbeCounts(profile);
9725
- const statusProbeCount = counts.httpStatus + counts.linkStatus;
9726
- if (!statusProbeCount) return warnings;
9727
- const parts = [
9728
- counts.httpStatus ? `${counts.httpStatus} http_status` : "",
9729
- counts.linkStatus ? `${counts.linkStatus} link_status/artifact_link_status` : ""
9730
- ].filter(Boolean);
9731
- warnings.push(
9732
- `Riddle API strict script validation blocked a hosted profile runner with ${parts.join(" and ")} check(s). These checks intentionally collect endpoint/link evidence from inside the generated browser runner; hosted run-profile defaults to --strict=false, so omit --strict=true or rerun with --strict=false for trusted generated profile runs.`
9733
- );
9734
- return warnings;
9735
- }
9736
- function normalizeRouteInventoryPath(value, label) {
9737
- const path6 = stringValue3(value);
9738
- if (!path6) throw new Error(`${label} requires path.`);
9739
- if (!path6.startsWith("/")) throw new Error(`${label}.path must start with /.`);
9740
- return normalizeRoutePath2(path6);
9741
- }
9742
- function normalizeRouteInventoryRoute(input, index) {
9743
- if (typeof input === "string") return { path: normalizeRouteInventoryPath(input, `checks route_inventory expected_routes[${index}]`) };
9744
- if (!isRecord(input)) throw new Error(`checks route_inventory expected_routes[${index}] must be a string or object.`);
9745
- return {
9746
- name: stringValue3(input.name),
9747
- path: normalizeRouteInventoryPath(input.path, `checks route_inventory expected_routes[${index}]`)
9748
- };
9749
- }
9750
- function normalizeRouteInventoryRoutes(value, index) {
9751
- if (value === void 0) return void 0;
9752
- if (!Array.isArray(value) || value.length === 0) {
9753
- throw new Error(`checks[${index}] route_inventory expected_routes must be a non-empty array.`);
9754
- }
9755
- const routes = value.map(normalizeRouteInventoryRoute);
9756
- const seen = /* @__PURE__ */ new Set();
9757
- for (const route of routes) {
9758
- if (seen.has(route.path)) throw new Error(`checks[${index}] route_inventory expected_routes contains duplicate path ${route.path}.`);
9759
- seen.add(route.path);
9760
- }
9761
- return routes;
9762
- }
9763
- function normalizeExpectedTexts(value, index) {
9764
- if (value === void 0) return void 0;
9765
- if (!Array.isArray(value) || value.length === 0) {
9766
- throw new Error(`checks[${index}] selector_text_order expected_texts must be a non-empty array.`);
9767
- }
9768
- const texts = value.map((item) => String(item).replace(/\s+/g, " ").trim()).filter(Boolean);
9769
- if (!texts.length) throw new Error(`checks[${index}] selector_text_order expected_texts must contain non-empty strings.`);
9770
- return texts;
9771
- }
9772
- function normalizeStringList(value, label) {
9773
- if (value === void 0) return void 0;
9774
- if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
9775
- const values = value.map((item) => String(item).replace(/\s+/g, " ").trim()).filter(Boolean);
9776
- if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
9777
- return values;
9778
- }
9779
- function normalizeHttpStatus(value, label) {
9780
- if (value === void 0) return void 0;
9781
- const status = numberValue2(value);
9782
- if (status === void 0 || !Number.isInteger(status) || status < 100 || status > 599) {
9783
- throw new Error(`${label} must be an HTTP status code.`);
9784
- }
9785
- return status;
9786
- }
9787
- function normalizeHttpStatuses(value, label) {
9788
- if (value === void 0) return void 0;
9789
- if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
9790
- if (!value.length) throw new Error(`${label} must not be empty.`);
9791
- const statuses = value.map((item, index) => normalizeHttpStatus(item, `${label}[${index}]`));
9792
- return Array.from(new Set(statuses));
9793
- }
9794
- function normalizePositiveInteger(value, label, max) {
9795
- if (value === void 0) return void 0;
9796
- const number = numberValue2(value);
9797
- if (number === void 0 || !Number.isInteger(number) || number < 1 || max !== void 0 && number > max) {
9798
- throw new Error(`${label} must be an integer from 1 to ${max ?? "unbounded"}.`);
9799
- }
9800
- return number;
9801
- }
9802
- function validateRegexPatterns(patterns, label) {
9803
- for (const pattern of patterns || []) {
9804
- try {
9805
- new RegExp(pattern);
9806
- } catch (error) {
9807
- const message = error instanceof Error ? error.message : String(error);
9808
- throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
9809
- }
9810
- }
9811
- }
9812
- function normalizeHttpStatusBodyJsonAssertions(value, label) {
9813
- if (value === void 0) return void 0;
9814
- if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
9815
- if (!value.length) throw new Error(`${label} must not be empty.`);
9816
- return value.map((item, index) => {
9817
- const itemLabel = `${label}[${index}]`;
9818
- if (typeof item === "string") {
9819
- const path7 = stringValue3(item);
9820
- if (!path7) throw new Error(`${itemLabel} path must not be empty.`);
9821
- return { path: path7, exists: true };
9822
- }
9823
- if (!isRecord(item)) throw new Error(`${itemLabel} must be an object or JSON path string.`);
9824
- const path6 = stringFromOwn(item, "path", "json_path", "jsonPath", "key");
9825
- if (!path6) throw new Error(`${itemLabel}.path is required.`);
9826
- const assertion = {
9827
- label: stringValue3(item.label),
9828
- path: path6
9829
- };
9830
- const exists = booleanValue2(valueFromOwn(item, "exists", "present"));
9831
- if (exists !== void 0) assertion.exists = exists;
9832
- const type = stringValue3(valueFromOwn(item, "type", "value_type", "valueType"));
9833
- if (type !== void 0) {
9834
- const allowedTypes = ["array", "boolean", "null", "number", "object", "string"];
9835
- if (!allowedTypes.includes(type)) {
9836
- throw new Error(`${itemLabel}.type must be one of ${allowedTypes.join(", ")}.`);
9837
- }
9838
- assertion.type = type;
9839
- }
9840
- const equalsValue = valueFromOwn(item, "equals", "expected", "expected_value", "expectedValue", "value");
9841
- if (equalsValue !== void 0) assertion.equals = toJsonValue(equalsValue);
9842
- const notEqualsValue = valueFromOwn(item, "not_equals", "notEquals", "forbidden", "forbidden_value", "forbiddenValue");
9843
- if (notEqualsValue !== void 0) assertion.not_equals = toJsonValue(notEqualsValue);
9844
- const containsValue = valueFromOwn(item, "contains", "includes", "contains_value", "containsValue", "include");
9845
- if (containsValue !== void 0) assertion.contains = toJsonValue(containsValue);
9846
- if (assertion.exists === void 0 && assertion.type === void 0 && !hasOwn(assertion, "equals") && !hasOwn(assertion, "not_equals") && !hasOwn(assertion, "contains")) {
9847
- assertion.exists = true;
9848
- }
9849
- return assertion;
9850
- });
9851
- }
9852
- function isDialogCountCheckType(type) {
9853
- return type === "dialog_count_equals" || type === "dialog_accept_count_equals" || type === "dialog_dismiss_count_equals";
9854
- }
9855
- function dialogCountFieldForCheckType(type) {
9856
- if (type === "dialog_accept_count_equals") return "dialog_accept_count";
9857
- if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
9858
- return "dialog_count";
9859
- }
9860
- function normalizeOrderedTraceEvents(value, label) {
9861
- if (value === void 0) return void 0;
9862
- if (!Array.isArray(value) || !value.length) throw new Error(`${label} must be a non-empty array.`);
9863
- const seenLabels = /* @__PURE__ */ new Set();
9864
- return value.map((item, eventIndex) => {
9865
- const eventLabel = `${label}[${eventIndex}]`;
9866
- if (!isRecord(item)) throw new Error(`${eventLabel} must be an object.`);
9867
- const name = stringFromOwn(item, "label", "name", "event");
9868
- if (!name) throw new Error(`${eventLabel}.label is required.`);
9869
- if (seenLabels.has(name)) throw new Error(`${eventLabel}.label must be unique.`);
9870
- seenLabels.add(name);
9871
- const predicatesInput = item.predicates ?? item.all ?? item.where;
9872
- if (!Array.isArray(predicatesInput) || !predicatesInput.length) {
9873
- throw new Error(`${eventLabel}.predicates must be a non-empty array.`);
9874
- }
9875
- const predicates = predicatesInput.map((predicateInput, predicateIndex) => {
9876
- const predicateLabel = `${eventLabel}.predicates[${predicateIndex}]`;
9877
- if (!isRecord(predicateInput)) throw new Error(`${predicateLabel} must be an object.`);
9878
- const path6 = stringFromOwn(predicateInput, "path", "field", "key");
9879
- if (!path6) throw new Error(`${predicateLabel}.path is required.`);
9880
- const op = stringFromOwn(predicateInput, "op", "operator");
9881
- if (!op || !RIDDLE_PROOF_ORDERED_TRACE_OPERATORS.includes(op)) {
9882
- throw new Error(`${predicateLabel}.op must be one of ${RIDDLE_PROOF_ORDERED_TRACE_OPERATORS.join(", ")}.`);
9883
- }
9884
- const requiresValue = !["exists", "truthy", "falsy"].includes(op);
9885
- const hasValue = hasOwn(predicateInput, "value") || hasOwn(predicateInput, "expected");
9886
- if (requiresValue && !hasValue) throw new Error(`${predicateLabel}.value is required for ${op}.`);
9887
- const value2 = hasOwn(predicateInput, "value") ? predicateInput.value : predicateInput.expected;
9888
- if (["gt", "gte", "lt", "lte", "abs_gt", "abs_gte", "abs_lt", "abs_lte"].includes(op)) {
9889
- if (typeof value2 !== "number" || !Number.isFinite(value2)) {
9890
- throw new Error(`${predicateLabel}.value must be a finite number for ${op}.`);
9891
- }
9892
- if (op.startsWith("abs_") && value2 < 0) {
9893
- throw new Error(`${predicateLabel}.value must be non-negative for ${op}.`);
9894
- }
9895
- }
9896
- return {
9897
- path: path6,
9898
- op,
9899
- value: requiresValue ? toJsonValue(value2) : void 0
9900
- };
9901
- });
9902
- return { label: name, predicates };
9903
- });
9904
- }
9905
- function normalizeCheck(input, index) {
9906
- if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
9907
- const type = stringValue3(input.type);
9908
- if (!type) throw new Error(`checks[${index}].type is required.`);
9909
- if (!isSupportedCheckType(type)) {
9910
- throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
9911
- }
9912
- const isDialogCountCheck = isDialogCountCheckType(type);
9913
- if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent" || type === "observe_within" && !stringValue3(input.text) && !stringValue3(input.pattern)) && !stringValue3(input.selector)) {
9914
- throw new Error(`checks[${index}] ${type} requires selector.`);
9915
- }
9916
- if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue3(input.selector)) {
9917
- throw new Error(`checks[${index}] ${type} requires selector.`);
9918
- }
9919
- if (type === "frame_text_visible" && !stringValue3(input.text) && !stringValue3(input.pattern)) {
9920
- throw new Error(`checks[${index}] frame_text_visible requires text or pattern.`);
9921
- }
9922
- const expectedUrl = stringFromOwn(input, "expected_url", "expectedUrl", "url", "expected_value", "expectedValue", "value");
9923
- if (type === "frame_url_equals" && expectedUrl === void 0) {
9924
- throw new Error(`checks[${index}] frame_url_equals requires expected_url.`);
9925
- }
9926
- if (type === "frame_url_matches" && !stringValue3(input.pattern)) {
9927
- throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
9928
- }
9929
- if ((type === "text_visible" || type === "text_absent" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue3(input.text) && !stringValue3(input.pattern)) {
9930
- throw new Error(`checks[${index}] ${type} requires text or pattern.`);
9931
- }
9932
- if ((type === "url_search_param_equals" || type === "url_search_param_absent") && !stringValue3(input.param) && !stringValue3(input.search_param) && !stringValue3(input.searchParam) && !stringValue3(input.key)) {
9933
- throw new Error(`checks[${index}] ${type} requires param.`);
9934
- }
9935
- const expectedValue = stringFromOwn(input, "expected_value", "expectedValue", "value");
9936
- if (type === "url_search_param_equals" && expectedValue === void 0) {
9937
- throw new Error(`checks[${index}] url_search_param_equals requires expected_value.`);
9938
- }
9939
- const minCount = numberValue2(input.min_count) ?? numberValue2(input.minCount);
9940
- if (type === "selector_count_at_least" && minCount === void 0) {
9941
- throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
9942
- }
9943
- const expectedCount = numberValue2(input.expected_count) ?? numberValue2(input.expectedCount) ?? numberValue2(input.count);
9944
- if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || isDialogCountCheck) && expectedCount === void 0) {
9945
- throw new Error(`checks[${index}] ${type} requires expected_count.`);
9946
- }
9947
- if (isDialogCountCheck && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
9948
- throw new Error(`checks[${index}] ${type} expected_count must be a non-negative integer.`);
9949
- }
9950
- const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
9951
- if (type === "selector_text_order") {
9952
- if (!stringValue3(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
9953
- if (!expectedTexts?.length) throw new Error(`checks[${index}] selector_text_order requires expected_texts.`);
9954
- }
9955
- const setupActionLabel = stringFromOwn(input, "setup_action_label", "setupActionLabel", "source_action_label", "sourceActionLabel");
9956
- const tracePath = stringFromOwn(input, "trace_path", "tracePath", "path");
9957
- const orderedTraceEvents = normalizeOrderedTraceEvents(
9958
- input.events ?? input.sequence ?? input.ordered_events ?? input.orderedEvents,
9959
- `checks[${index}].events`
9960
- );
9961
- if (type === "ordered_trace") {
9962
- if (!setupActionLabel) throw new Error(`checks[${index}] ordered_trace requires setup_action_label.`);
9963
- if (!tracePath) throw new Error(`checks[${index}] ordered_trace requires trace_path.`);
9964
- if (!orderedTraceEvents?.length) throw new Error(`checks[${index}] ordered_trace requires events.`);
9965
- }
9966
- const expectedRoutes = normalizeRouteInventoryRoutes(input.expected_routes ?? input.expectedRoutes, index);
9967
- if (type === "route_inventory" && !expectedRoutes?.length) {
9968
- throw new Error(`checks[${index}] route_inventory requires expected_routes.`);
9969
- }
9970
- const isHttpStatusCheck = type === "http_status";
9971
- const isLinkStatusCheck = type === "link_status" || type === "artifact_link_status";
9972
- const isStatusCheck = isHttpStatusCheck || isLinkStatusCheck;
9973
- const requestUrl = stringFromOwn(input, "url", "endpoint_url", "endpointUrl", "endpoint", "request_url", "requestUrl", "path");
9974
- if (isHttpStatusCheck && !requestUrl) {
9975
- throw new Error(`checks[${index}] http_status requires url.`);
9976
- }
9977
- const method = stringFromOwn(input, "method", "http_method", "httpMethod")?.toUpperCase();
9978
- if (isHttpStatusCheck && method && !/^[A-Z]+$/.test(method)) {
9979
- throw new Error(`checks[${index}] http_status method must contain only letters.`);
9980
- }
9981
- const hasBodyJson = hasOwn(input, "body_json") || hasOwn(input, "bodyJson") || hasOwn(input, "json");
9982
- const expectedStatus = isStatusCheck ? normalizeHttpStatus(input.expected_status ?? input.expectedStatus ?? input.status, `checks[${index}] expected_status`) : void 0;
9983
- const allowedStatuses = isStatusCheck ? normalizeHttpStatuses(
9984
- input.allowed_statuses ?? input.allowedStatuses ?? input.expected_statuses ?? input.expectedStatuses,
9985
- `checks[${index}] allowed_statuses`
9986
- ) : void 0;
9987
- const maxLinks = isLinkStatusCheck ? normalizePositiveInteger(input.max_links ?? input.maxLinks ?? input.limit, `checks[${index}] max_links`, 500) : void 0;
9988
- const minBytes = isStatusCheck ? normalizePositiveInteger(input.min_bytes ?? input.minBytes ?? input.min_response_bytes ?? input.minResponseBytes, `checks[${index}] min_bytes`) : void 0;
9989
- const expectedContentType = isStatusCheck ? stringValue3(input.expected_content_type) || stringValue3(input.expectedContentType) || stringValue3(input.content_type) || stringValue3(input.contentType) : void 0;
9990
- const allowedContentTypes = isStatusCheck ? normalizeStringList(
9991
- input.allowed_content_types ?? input.allowedContentTypes ?? input.expected_content_types ?? input.expectedContentTypes,
9992
- `checks[${index}] allowed_content_types`
9993
- ) ?? (expectedContentType ? [expectedContentType] : void 0) : void 0;
9994
- const bodyContains = isHttpStatusCheck ? normalizeStringList(
9995
- input.body_contains ?? input.bodyContains ?? input.expected_body_contains ?? input.expectedBodyContains ?? input.response_body_contains ?? input.responseBodyContains ?? input.body_includes ?? input.bodyIncludes,
9996
- `checks[${index}] body_contains`
9997
- ) : void 0;
9998
- const bodyNotContains = isHttpStatusCheck ? normalizeStringList(
9999
- input.body_not_contains ?? input.bodyNotContains ?? input.expected_body_not_contains ?? input.expectedBodyNotContains ?? input.response_body_not_contains ?? input.responseBodyNotContains ?? input.body_absent ?? input.bodyAbsent,
10000
- `checks[${index}] body_not_contains`
10001
- ) : void 0;
10002
- const bodyNotPatterns = isHttpStatusCheck ? normalizeStringList(
10003
- input.body_not_patterns ?? input.bodyNotPatterns ?? input.expected_body_not_patterns ?? input.expectedBodyNotPatterns ?? input.response_body_not_patterns ?? input.responseBodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
10004
- `checks[${index}] body_not_patterns`
10005
- ) : void 0;
10006
- if (bodyNotPatterns?.length) validateRegexPatterns(bodyNotPatterns, `checks[${index}] body_not_patterns`);
10007
- const bodyJsonAssertions = isHttpStatusCheck ? normalizeHttpStatusBodyJsonAssertions(
10008
- input.body_json_assertions ?? input.bodyJsonAssertions ?? input.json_body_assertions ?? input.jsonBodyAssertions ?? input.json_assertions ?? input.jsonAssertions ?? input.response_json_assertions ?? input.responseJsonAssertions,
10009
- `checks[${index}] body_json_assertions`
10010
- ) : void 0;
10011
- if (isLinkStatusCheck) {
10012
- if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
10013
- throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
10014
- }
10015
- if (expectedCount !== void 0 && (!Number.isInteger(expectedCount) || expectedCount < 0)) {
10016
- throw new Error(`checks[${index}] ${type} expected_count must be a non-negative integer.`);
10017
- }
10018
- }
10019
- return {
10020
- type,
10021
- label: stringValue3(input.label),
10022
- expected_path: stringValue3(input.expected_path),
10023
- param: stringValue3(input.param) || stringValue3(input.search_param) || stringValue3(input.searchParam) || stringValue3(input.key),
10024
- expected_value: expectedValue,
10025
- expected_url: expectedUrl,
10026
- expected_routes: expectedRoutes,
10027
- selector: stringValue3(input.selector),
10028
- url: isHttpStatusCheck ? requestUrl : void 0,
10029
- method: isHttpStatusCheck ? method || "GET" : void 0,
10030
- headers: isHttpStatusCheck ? stringRecord(input.headers) : void 0,
10031
- body: isHttpStatusCheck ? stringValue3(input.body) : void 0,
10032
- body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
10033
- body_contains: bodyContains,
10034
- body_not_contains: bodyNotContains,
10035
- body_not_patterns: bodyNotPatterns,
10036
- body_json_assertions: bodyJsonAssertions,
10037
- expected_texts: expectedTexts,
10038
- setup_action_label: type === "ordered_trace" ? setupActionLabel : void 0,
10039
- trace_path: type === "ordered_trace" ? tracePath : void 0,
10040
- events: type === "ordered_trace" ? orderedTraceEvents : void 0,
10041
- link_selector: stringValue3(input.link_selector) || stringValue3(input.linkSelector),
10042
- source_selector: stringValue3(input.source_selector) || stringValue3(input.sourceSelector),
10043
- route_path_prefix: stringValue3(input.route_path_prefix) || stringValue3(input.routePathPrefix),
10044
- route_ready_selector: stringValue3(input.route_ready_selector) || stringValue3(input.routeReadySelector),
10045
- route_ready_text: stringValue3(input.route_ready_text) || stringValue3(input.routeReadyText),
10046
- route_ready_pattern: stringValue3(input.route_ready_pattern) || stringValue3(input.routeReadyPattern),
10047
- route_ready_flags: stringValue3(input.route_ready_flags) || stringValue3(input.routeReadyFlags),
10048
- text: stringValue3(input.text),
10049
- pattern: stringValue3(input.pattern),
10050
- flags: stringValue3(input.flags),
10051
- viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
10052
- allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
10053
- allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
10054
- allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
10055
- allowed_page_error_patterns: normalizeStringList(input.allowed_page_error_patterns ?? input.allowedPageErrorPatterns ?? input.allow_page_error_patterns ?? input.allowPageErrorPatterns, `checks[${index}] allowed_page_error_patterns`),
10056
- min_count: minCount,
10057
- expected_count: expectedCount,
10058
- expected_status: expectedStatus,
10059
- allowed_statuses: allowedStatuses,
10060
- max_links: maxLinks,
10061
- same_origin_only: isLinkStatusCheck ? input.same_origin_only === true || input.sameOriginOnly === true : void 0,
10062
- dedupe: isLinkStatusCheck ? input.dedupe === false ? false : true : void 0,
10063
- require_nonzero_bytes: isStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
10064
- min_bytes: minBytes,
10065
- allowed_content_types: allowedContentTypes,
10066
- allow_get_fallback: isLinkStatusCheck ? input.allow_get_fallback === false || input.allowGetFallback === false ? false : true : void 0,
10067
- max_overflow_px: numberValue2(input.max_overflow_px),
10068
- timeout_ms: numberValue2(input.timeout_ms) ?? numberValue2(input.timeoutMs) ?? numberValue2(input.within_ms) ?? numberValue2(input.withinMs),
10069
- run_direct_routes: input.run_direct_routes === false || input.runDirectRoutes === false ? false : true,
10070
- run_clickthroughs: input.run_clickthroughs === false || input.runClickthroughs === false ? false : true,
10071
- run_all_viewports: input.run_all_viewports === true || input.runAllViewports === true,
10072
- require_unique_routes: input.require_unique_routes === false || input.requireUniqueRoutes === false ? false : true,
10073
- allow_unexpected_routes: input.allow_unexpected_routes === true || input.allowUnexpectedRoutes === true,
10074
- save_route_screenshots: input.save_route_screenshots === true || input.saveRouteScreenshots === true
10075
- };
10076
- }
10077
- function normalizeFailurePolicy(input) {
10078
- const defaults = {
10079
- product_regression: "fail",
10080
- proof_insufficient: "fail",
10081
- environment_blocked: "neutral",
10082
- configuration_error: "fail",
10083
- needs_human_review: "fail"
10084
- };
10085
- if (!isRecord(input)) return defaults;
10086
- const next = { ...defaults };
10087
- for (const [key, value] of Object.entries(input)) {
10088
- if (!RIDDLE_PROOF_PROFILE_STATUSES.includes(key)) continue;
10089
- if (value === "fail" || value === "neutral" || value === "review") {
10090
- next[key] = value;
10091
- }
10092
- }
10093
- return next;
10094
- }
10095
- function normalizeRiddleProofProfile(input, options = {}) {
10096
- if (!isRecord(input)) throw new Error("profile must be a JSON object.");
10097
- const version = stringValue3(input.version) || RIDDLE_PROOF_PROFILE_VERSION;
10098
- if (version !== RIDDLE_PROOF_PROFILE_VERSION) {
10099
- throw new Error(`Unsupported profile version ${version}. Expected ${RIDDLE_PROOF_PROFILE_VERSION}.`);
10100
- }
10101
- const targetInput = isRecord(input.target) ? input.target : {};
10102
- const checks = Array.isArray(input.checks) ? input.checks.map(normalizeCheck) : [];
10103
- if (!checks.length) throw new Error("profile.checks must contain at least one check.");
10104
- const targetUrl = stringValue3(options.url) || stringValue3(targetInput.url);
10105
- const route = stringValue3(options.route) || stringValue3(targetInput.route);
10106
- if (!targetUrl && !route) throw new Error("profile.target requires url or route, or pass --url.");
10107
- const setupActions = normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions);
10108
- for (const [index, check] of checks.entries()) {
10109
- if (check.type !== "ordered_trace") continue;
10110
- const matches = (setupActions || []).filter((action) => action.label === check.setup_action_label);
10111
- if (matches.length !== 1) {
10112
- throw new Error(`checks[${index}] ordered_trace setup_action_label must match exactly one target.setup_actions label.`);
10113
- }
10114
- if (!["window_eval", "window_call", "window_call_until"].includes(matches[0].type)) {
10115
- throw new Error(`checks[${index}] ordered_trace source action must capture a window_eval, window_call, or window_call_until return.`);
10116
- }
10117
- if (matches[0].capture_return === false) {
10118
- throw new Error(`checks[${index}] ordered_trace source action must not set capture_return to false.`);
10119
- }
10120
- }
10121
- return {
10122
- version: RIDDLE_PROOF_PROFILE_VERSION,
10123
- name: normalizeName(input.name, "riddle-proof-profile"),
10124
- target: {
10125
- url: targetUrl,
10126
- route,
10127
- viewports: options.viewports?.length ? options.viewports : normalizeViewports(targetInput.viewports),
10128
- auth: stringValue3(targetInput.auth) || "none",
10129
- timeout_sec: timeoutSecValue(targetInput.timeout_sec) ?? timeoutSecValue(targetInput.timeoutSec) ?? timeoutSecValue(targetInput.riddle_timeout_sec) ?? timeoutSecValue(targetInput.riddleTimeoutSec),
10130
- wait_for_selector: stringValue3(targetInput.wait_for_selector) || stringValue3(targetInput.waitForSelector),
10131
- wait_ms: numberValue2(targetInput.wait_ms) ?? numberValue2(targetInput.waitMs),
10132
- screenshot_full_page: normalizeTargetScreenshotFullPage(targetInput),
10133
- setup_actions: setupActions,
10134
- network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
10135
- },
10136
- checks,
10137
- artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
10138
- baseline_policy: stringValue3(input.baseline_policy) || stringValue3(input.baselinePolicy) || "invariant_only",
10139
- failure_policy: normalizeFailurePolicy(input.failure_policy || input.failurePolicy),
10140
- metadata: jsonRecord(input.metadata)
10141
- };
10142
- }
10143
- function resolveRiddleProofProfileTargetUrl(profile) {
10144
- const route = profile.target.route || "";
10145
- const targetUrl = profile.target.url || "";
10146
- if (targetUrl && route) return resolveRiddleProofProfileRouteUrl(targetUrl, route);
10147
- if (/^https?:\/\//i.test(route)) return route;
10148
- if (targetUrl) return targetUrl;
10149
- throw new Error("profile target URL could not be resolved.");
10150
- }
10151
- function resolveRiddleProofProfileTimeoutSec(profile, requestedTimeoutSec) {
10152
- return timeoutSecValue(requestedTimeoutSec) ?? timeoutSecValue(profile.target.timeout_sec);
10153
- }
10154
- function routeForViewport(viewport, targetUrl) {
10155
- if (viewport?.route) {
10156
- return {
10157
- ...viewport.route,
10158
- matched: viewport.route.matched || routePathMatches(viewport.route.observed, viewport.route.expected_path, targetUrl)
10159
- };
10160
- }
10161
- return {
10162
- requested: "",
10163
- observed: "",
10164
- matched: false,
10165
- error: "missing viewport evidence"
10166
- };
10167
- }
10168
- function checkLabel(check) {
10169
- return check.label || check.type;
10170
- }
10171
- function selectorKey(check) {
10172
- return check.selector || "";
10173
- }
10174
- function linkStatusSelector(check) {
10175
- return check.selector || check.link_selector || "a[href]";
10176
- }
10177
- function httpStatusRequestUrl(check, baseUrl) {
10178
- const rawUrl = check.url || "";
10179
- if (!rawUrl) return "";
10180
- try {
10181
- if (baseUrl && rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
10182
- const base = new URL(baseUrl);
10183
- const mountPrefix = previewMountPrefix(base.pathname);
10184
- if (mountPrefix) {
10185
- const requested = new URL(rawUrl, base.origin);
10186
- base.pathname = joinMountedRoutePath(mountPrefix, requested.pathname);
10187
- base.search = requested.search;
10188
- base.hash = requested.hash;
10189
- return base.href;
10190
- }
10191
- }
10192
- return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
10193
- } catch {
10194
- return rawUrl;
10195
- }
10196
- }
10197
- function httpStatusMethod(check) {
10198
- return (check.method || "GET").toUpperCase();
10199
- }
10200
- function httpStatusKey(check, baseUrl) {
10201
- return `${httpStatusMethod(check)} ${httpStatusRequestUrl(check, baseUrl)}`;
10202
- }
10203
- function httpStatusAllowedStatuses(check) {
10204
- if (check.allowed_statuses?.length) return check.allowed_statuses;
10205
- if (check.expected_status !== void 0) return [check.expected_status];
10206
- return void 0;
10207
- }
10208
- function linkStatusAllowedStatuses(check) {
10209
- return httpStatusAllowedStatuses(check);
10210
- }
10211
- function httpStatusIsAllowed(status, check) {
10212
- if (status === void 0) return false;
10213
- const allowed = httpStatusAllowedStatuses(check);
10214
- return allowed?.length ? allowed.includes(status) : status >= 200 && status < 400;
10215
- }
10216
- function linkStatusObservedBytes(result) {
10217
- const bytes = numberValue2(result.bytes);
10218
- const contentLength = numberValue2(result.content_length);
10219
- return Math.max(bytes ?? 0, contentLength ?? 0) || void 0;
10220
- }
10221
- function normalizeLinkStatusContentType(value) {
10222
- const normalized = value?.split(";")[0]?.trim().toLowerCase();
10223
- return normalized || void 0;
10224
- }
10225
- function linkStatusContentTypesMatch(actual, expected) {
10226
- if (!actual || !expected) return false;
10227
- if (expected.endsWith("/*")) return actual.startsWith(expected.slice(0, -1));
10228
- if (actual === expected) return true;
10229
- const yamlTypes = /* @__PURE__ */ new Set(["application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"]);
10230
- return yamlTypes.has(actual) && yamlTypes.has(expected);
10231
- }
10232
- function linkStatusContentTypeOk(result, check) {
10233
- const expected = check.allowed_content_types;
10234
- if (!expected?.length) return true;
10235
- const actual = normalizeLinkStatusContentType(stringValue3(result.content_type));
10236
- if (!actual) return false;
10237
- return expected.some((contentType) => {
10238
- const normalized = normalizeLinkStatusContentType(contentType);
10239
- if (!normalized) return false;
10240
- return linkStatusContentTypesMatch(actual, normalized);
10241
- });
10242
- }
10243
- function httpStatusBodyContainsFailures(result, check) {
10244
- const expected = check.body_contains?.filter(Boolean) ?? [];
10245
- if (!expected.length) return [];
10246
- const observed = isRecord(result.body_contains) ? result.body_contains : {};
10247
- return expected.filter((text) => observed[text] !== true);
10248
- }
10249
- function httpStatusBodyNotContainsFailures(result, check) {
10250
- const forbidden = check.body_not_contains?.filter(Boolean) ?? [];
10251
- if (!forbidden.length) return [];
10252
- const observed = isRecord(result.body_not_contains) ? result.body_not_contains : {};
10253
- return forbidden.filter((text) => observed[text] !== false);
10254
- }
10255
- function httpStatusBodyNotPatternFailures(result, check) {
10256
- const forbidden = check.body_not_patterns?.filter(Boolean) ?? [];
10257
- if (!forbidden.length) return [];
10258
- const observed = isRecord(result.body_not_patterns) ? result.body_not_patterns : {};
10259
- return forbidden.filter((pattern) => observed[pattern] !== false);
10260
- }
10261
- function httpStatusBodyJsonAssertionFailures(result, check) {
10262
- const expected = check.body_json_assertions?.filter((assertion) => assertion.path) ?? [];
10263
- if (!expected.length) return [];
10264
- if (!Array.isArray(result.body_json_assertions)) {
10265
- return expected.map((assertion) => ({
10266
- label: assertion.label || assertion.path,
10267
- path: assertion.path,
10268
- ok: false,
10269
- exists: false,
10270
- observed_type: "missing",
10271
- errors: ["body_json_assertions evidence missing"]
10272
- }));
10273
- }
10274
- return result.body_json_assertions.filter((assertion) => isRecord(assertion) && assertion.ok !== true).map((assertion) => ({
10275
- label: stringValue3(assertion.label) || stringValue3(assertion.path) || "json assertion",
10276
- path: stringValue3(assertion.path) || "",
10277
- ok: false,
10278
- exists: assertion.exists === true,
10279
- observed: hasOwn(assertion, "observed") ? toJsonValue(assertion.observed) : void 0,
10280
- observed_sample: hasOwn(assertion, "observed_sample") ? toJsonValue(assertion.observed_sample) : void 0,
10281
- observed_length: numberValue2(assertion.observed_length),
10282
- observed_key_count: numberValue2(assertion.observed_key_count),
10283
- observed_omitted_count: numberValue2(assertion.observed_omitted_count),
10284
- observed_type: stringValue3(assertion.observed_type) || "missing",
10285
- expected_exists: booleanValue2(assertion.expected_exists),
10286
- equals: hasOwn(assertion, "equals") ? toJsonValue(assertion.equals) : void 0,
10287
- not_equals: hasOwn(assertion, "not_equals") ? toJsonValue(assertion.not_equals) : void 0,
10288
- contains: hasOwn(assertion, "contains") ? toJsonValue(assertion.contains) : void 0,
10289
- type: stringValue3(assertion.type),
10290
- errors: Array.isArray(assertion.errors) ? assertion.errors.map(String) : void 0
10291
- }));
10292
- }
10293
- function linkStatusResultOk(result, check) {
10294
- const status = numberValue2(result.status);
10295
- if (!httpStatusIsAllowed(status, check)) return false;
10296
- if (stringValue3(result.error)) return false;
10297
- if (result.ok === false) return false;
10298
- if (!linkStatusContentTypeOk(result, check)) return false;
10299
- if (check.require_nonzero_bytes) {
10300
- const observedBytes = linkStatusObservedBytes(result);
10301
- if (observedBytes === void 0 || observedBytes <= 0) return false;
10302
- }
10303
- if (check.min_bytes !== void 0) {
10304
- const observedBytes = linkStatusObservedBytes(result);
10305
- if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
10306
- }
10307
- if (httpStatusBodyContainsFailures(result, check).length) return false;
10308
- if (httpStatusBodyNotContainsFailures(result, check).length) return false;
10309
- if (httpStatusBodyNotPatternFailures(result, check).length) return false;
10310
- if (httpStatusBodyJsonAssertionFailures(result, check).length) return false;
10311
- return true;
10312
- }
10313
- function responseHeader(response, name) {
10314
- const getter = response.headers?.get;
10315
- if (typeof getter !== "function") return null;
10316
- return getter.call(response.headers, name);
10317
- }
10318
- function responseContentLength(response) {
10319
- const value = responseHeader(response, "content-length");
10320
- return value && /^\d+$/.test(value) ? Number(value) : null;
10321
- }
10322
- async function responseBodyText(response) {
10323
- if (typeof response.arrayBuffer === "function") {
10324
- const buffer = await response.arrayBuffer();
10325
- return {
10326
- text: new TextDecoder().decode(buffer),
10327
- bytes: buffer.byteLength
10328
- };
10329
- }
10330
- if (typeof response.text === "function") {
10331
- const text = await response.text();
10332
- return {
10333
- text,
10334
- bytes: new TextEncoder().encode(text).byteLength
10335
- };
10336
- }
10337
- return { text: "", bytes: null };
10338
- }
10339
- function httpStatusRequestBody(check) {
10340
- const headers = isRecord(check.headers) ? Object.fromEntries(Object.entries(check.headers).map(([key, value]) => [key, String(value)]).filter(([key]) => key.trim())) : {};
10341
- if (check.body_json !== void 0) {
10342
- if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
10343
- headers["content-type"] = "application/json";
10344
- }
10345
- return { headers, body: JSON.stringify(check.body_json) };
10346
- }
10347
- if (typeof check.body === "string") {
10348
- return { headers, body: check.body };
10349
- }
10350
- return { headers };
10351
- }
10352
- async function preflightHttpStatusCheck(check, index, targetUrl, fetchImpl) {
10353
- const url = httpStatusRequestUrl(check, targetUrl);
10354
- const method = httpStatusMethod(check);
10355
- const request = httpStatusRequestBody(check);
10356
- const init = {
10357
- method,
10358
- redirect: "follow",
10359
- cache: "no-store",
10360
- headers: request.headers
10361
- };
10362
- if (request.body !== void 0 && method !== "GET" && method !== "HEAD") {
10363
- init.body = request.body;
10364
- }
10365
- const result = {
10366
- status: null,
10367
- content_type: null,
10368
- content_length: null,
10369
- bytes: null
10370
- };
10371
- let error = null;
10372
- let statusText = "";
10373
- try {
10374
- const response = await fetchImpl(url, init);
10375
- result.status = typeof response.status === "number" && Number.isFinite(response.status) ? response.status : null;
10376
- statusText = typeof response.statusText === "string" ? response.statusText : "";
10377
- result.content_type = responseHeader(response, "content-type");
10378
- result.content_length = responseContentLength(response);
10379
- const shouldReadBody = check.require_nonzero_bytes === true || typeof check.min_bytes === "number" || Boolean(check.body_contains?.length) || Boolean(check.body_not_contains?.length) || Boolean(check.body_not_patterns?.length) || Boolean(check.body_json_assertions?.length);
10380
- if (shouldReadBody && method !== "HEAD") {
10381
- const body = await responseBodyText(response);
10382
- result.bytes = body.bytes;
10383
- if (check.body_contains?.length) {
10384
- result.body_contains = Object.fromEntries(check.body_contains.filter(Boolean).map((snippet) => [snippet, body.text.includes(snippet)]));
10385
- }
10386
- if (check.body_not_contains?.length) {
10387
- result.body_not_contains = Object.fromEntries(check.body_not_contains.filter(Boolean).map((snippet) => [snippet, body.text.includes(snippet)]));
10388
- }
10389
- if (check.body_not_patterns?.length) {
10390
- result.body_not_patterns = Object.fromEntries(check.body_not_patterns.filter(Boolean).map((pattern) => [pattern, new RegExp(pattern).test(body.text)]));
10391
- }
10392
- if (check.body_json_assertions?.length) {
10393
- result.body_json_assertions = evaluateHttpStatusBodyJsonAssertions(body.text, check.body_json_assertions);
10394
- }
10395
- }
10396
- } catch (caught) {
10397
- error = String(caught instanceof Error ? caught.message : caught).slice(0, 500);
10398
- result.error = error;
10399
- }
10400
- const bodyContainsMissing = httpStatusBodyContainsFailures(result, check);
10401
- const bodyNotContainsFound = httpStatusBodyNotContainsFailures(result, check);
10402
- const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(result, check);
10403
- const bodyJsonAssertionsFailed = httpStatusBodyJsonAssertionFailures(result, check);
10404
- const ok = !error && linkStatusResultOk(result, check);
10405
- return {
10406
- index,
10407
- label: checkLabel(check) || `checks[${index}]`,
10408
- url,
10409
- method,
10410
- ok,
10411
- status: numberValue2(result.status) ?? null,
10412
- status_text: statusText,
10413
- error,
10414
- content_type: stringValue3(result.content_type) ?? null,
10415
- content_length: numberValue2(result.content_length) ?? null,
10416
- bytes: numberValue2(result.bytes) ?? null,
10417
- body_contains: isRecord(result.body_contains) ? Object.fromEntries(Object.entries(result.body_contains).map(([key, value]) => [key, value === true])) : null,
10418
- body_contains_missing: bodyContainsMissing,
10419
- body_not_contains: isRecord(result.body_not_contains) ? Object.fromEntries(Object.entries(result.body_not_contains).map(([key, value]) => [key, value === true])) : null,
10420
- body_not_contains_found: bodyNotContainsFound,
10421
- body_not_patterns: isRecord(result.body_not_patterns) ? Object.fromEntries(Object.entries(result.body_not_patterns).map(([key, value]) => [key, value === true])) : null,
10422
- body_not_patterns_found: bodyNotPatternsFound,
10423
- body_json_assertions: Array.isArray(result.body_json_assertions) ? result.body_json_assertions : null,
10424
- body_json_assertions_failed: bodyJsonAssertionsFailed
10425
- };
10426
- }
10427
- async function preflightRiddleProofProfileHttpStatusChecks(profile, options = {}) {
10428
- const targetUrl = options.target_url || resolveRiddleProofProfileTargetUrl(profile);
10429
- const fetchImpl = options.fetchImpl ?? globalThis.fetch;
10430
- if (typeof fetchImpl !== "function") {
10431
- throw new Error("profile http_status preflight requires fetch support or options.fetchImpl.");
10432
- }
10433
- const httpStatusChecks = profile.checks.map((check, index) => ({ check, index })).filter((item) => item.check.type === "http_status");
10434
- const checks = await Promise.all(httpStatusChecks.map((item) => preflightHttpStatusCheck(item.check, item.index, targetUrl, fetchImpl)));
10435
- const failed = checks.filter((check) => !check.ok).length;
10436
- return {
10437
- version: "riddle-proof.profile-http-status-preflight.v1",
10438
- ok: failed === 0,
10439
- profile_name: profile.name,
10440
- target_url: targetUrl,
10441
- checked: checks.length,
10442
- failed,
10443
- checks,
10444
- summary: failed === 0 ? `${profile.name} http_status preflight passed ${checks.length} check(s).` : `${profile.name} http_status preflight failed ${failed} of ${checks.length} check(s).`
10445
- };
10446
- }
10447
- function httpStatusEvidenceForCheck(viewport, check) {
10448
- const evidence = viewport.http_statuses?.[httpStatusKey(check, viewport.url)];
10449
- return isRecord(evidence) ? evidence : void 0;
10450
- }
10451
- function summarizeHttpStatusEvidence(viewport, check) {
10452
- const key = httpStatusKey(check, viewport.url);
10453
- const statusEvidence = httpStatusEvidenceForCheck(viewport, check);
10454
- if (!statusEvidence) {
10455
- return {
10456
- viewport: viewport.name,
10457
- key,
10458
- url: httpStatusRequestUrl(check, viewport.url),
10459
- method: httpStatusMethod(check),
10460
- ok: false,
10461
- status: null,
10462
- failures: [{ code: "http_status_evidence_missing" }]
10463
- };
10464
- }
10465
- const failures = [];
10466
- const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
10467
- const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
10468
- const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
10469
- const bodyJsonAssertionsFailed = httpStatusBodyJsonAssertionFailures(statusEvidence, check);
10470
- if (!linkStatusResultOk(statusEvidence, check)) {
10471
- failures.push({
10472
- code: "http_status_failed",
10473
- url: stringValue3(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
10474
- status: numberValue2(statusEvidence.status) ?? null,
10475
- method: stringValue3(statusEvidence.method) ?? httpStatusMethod(check),
10476
- error: stringValue3(statusEvidence.error) ?? null,
10477
- content_type: stringValue3(statusEvidence.content_type) ?? null,
10478
- bytes: linkStatusObservedBytes(statusEvidence) ?? null,
10479
- allowed_statuses: httpStatusAllowedStatuses(check) ?? ["2xx", "3xx"],
10480
- min_bytes: check.min_bytes ?? null,
10481
- allowed_content_types: check.allowed_content_types ?? null,
10482
- body_contains: check.body_contains ?? null,
10483
- body_contains_missing: bodyContainsMissing,
10484
- body_not_contains: check.body_not_contains ?? null,
10485
- body_not_contains_found: bodyNotContainsFound,
10486
- body_not_patterns: check.body_not_patterns ?? null,
10487
- body_not_patterns_found: bodyNotPatternsFound,
10488
- body_json_assertions: check.body_json_assertions ?? null,
10489
- body_json_assertions_failed: bodyJsonAssertionsFailed.map((assertion) => toJsonValue(assertion)),
10490
- body_sample: stringValue3(statusEvidence.body_sample) ?? null
10491
- });
10492
- }
10493
- return {
10494
- viewport: viewport.name,
10495
- key,
10496
- url: stringValue3(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
10497
- method: stringValue3(statusEvidence.method) ?? httpStatusMethod(check),
10498
- status: numberValue2(statusEvidence.status) ?? null,
10499
- status_text: stringValue3(statusEvidence.status_text) ?? null,
10500
- ok: failures.length === 0,
10501
- error: stringValue3(statusEvidence.error) ?? null,
10502
- content_type: stringValue3(statusEvidence.content_type) ?? null,
10503
- content_length: numberValue2(statusEvidence.content_length) ?? null,
10504
- bytes: linkStatusObservedBytes(statusEvidence) ?? null,
10505
- body_contains: isRecord(statusEvidence.body_contains) ? toJsonValue(statusEvidence.body_contains) : null,
10506
- body_contains_missing: bodyContainsMissing,
10507
- body_not_contains: isRecord(statusEvidence.body_not_contains) ? toJsonValue(statusEvidence.body_not_contains) : null,
10508
- body_not_contains_found: bodyNotContainsFound,
10509
- body_not_patterns: isRecord(statusEvidence.body_not_patterns) ? toJsonValue(statusEvidence.body_not_patterns) : null,
10510
- body_not_patterns_found: bodyNotPatternsFound,
10511
- body_json_assertions: Array.isArray(statusEvidence.body_json_assertions) ? toJsonValue(statusEvidence.body_json_assertions) : null,
10512
- body_json_assertions_failed: bodyJsonAssertionsFailed.map((assertion) => toJsonValue(assertion)),
10513
- body_sample: stringValue3(statusEvidence.body_sample) ?? null,
10514
- failures
10515
- };
10516
- }
10517
- function linkStatusEvidenceForCheck(viewport, check) {
10518
- const evidence = viewport.link_statuses?.[linkStatusSelector(check)];
10519
- return isRecord(evidence) ? evidence : void 0;
10520
- }
10521
- function summarizeLinkStatusEvidence(viewport, check) {
10522
- const linkEvidence = linkStatusEvidenceForCheck(viewport, check);
10523
- if (!linkEvidence) {
10524
- return {
10525
- viewport: viewport.name,
10526
- selector: linkStatusSelector(check),
10527
- total_count: 0,
10528
- ok_count: 0,
10529
- failed_count: 1,
10530
- failures: [{ code: "link_status_evidence_missing" }]
10531
- };
10532
- }
10533
- const results = Array.isArray(linkEvidence.results) ? linkEvidence.results.filter(isRecord) : [];
10534
- const totalCount = numberValue2(linkEvidence.total_count) ?? results.length;
10535
- const resultCount = numberValue2(linkEvidence.result_count) ?? totalCount;
10536
- const storedResultCount = numberValue2(linkEvidence.stored_result_count) ?? results.length;
10537
- const omittedResultCount = numberValue2(linkEvidence.omitted_result_count) ?? Math.max(0, resultCount - storedResultCount);
10538
- const omittedSuccessCount = numberValue2(linkEvidence.omitted_success_count) ?? 0;
10539
- const okCount = results.filter((result) => linkStatusResultOk(result, check)).length;
10540
- const failures = results.filter((result) => !linkStatusResultOk(result, check)).map((result) => ({
10541
- code: "link_status_failed",
10542
- url: stringValue3(result.url) ?? null,
10543
- status: numberValue2(result.status) ?? null,
10544
- method: stringValue3(result.method) ?? null,
10545
- error: stringValue3(result.error) ?? null,
10546
- content_type: stringValue3(result.content_type) ?? null,
10547
- bytes: linkStatusObservedBytes(result) ?? null,
10548
- min_bytes: check.min_bytes ?? null,
10549
- allowed_content_types: check.allowed_content_types ?? null
10550
- }));
10551
- if (stringValue3(linkEvidence.error)) {
10552
- failures.push({ code: "link_status_capture_failed", error: stringValue3(linkEvidence.error) ?? "" });
10553
- }
10554
- const recordedFailedCount = numberValue2(linkEvidence.failed_count) ?? 0;
10555
- if (!failures.length && recordedFailedCount > 0) {
10556
- const recordedFailures = Array.isArray(linkEvidence.failures) ? linkEvidence.failures.slice(0, 20) : [];
10557
- failures.push({
10558
- code: "link_status_recorded_failures",
10559
- failed_count: recordedFailedCount,
10560
- failures: toJsonValue(recordedFailures)
10561
- });
10562
- }
10563
- if (linkEvidence.truncated === true) {
10564
- failures.push({
10565
- code: "link_status_probe_truncated",
10566
- discovered_count: numberValue2(linkEvidence.discovered_count) ?? totalCount,
10567
- max_links: numberValue2(linkEvidence.max_links) ?? check.max_links ?? 100
10568
- });
10569
- }
10570
- if (check.expected_count !== void 0 && totalCount !== check.expected_count) {
10571
- failures.push({ code: "link_status_count_mismatch", expected: check.expected_count, actual: totalCount });
10572
- }
10573
- if (check.min_count !== void 0 && totalCount < check.min_count) {
10574
- failures.push({ code: "link_status_count_below_minimum", min_count: check.min_count, actual: totalCount });
10575
- }
10576
- const statusCounts = isRecord(linkEvidence.status_counts) ? linkEvidence.status_counts : {};
10577
- return {
10578
- viewport: viewport.name,
10579
- selector: linkStatusSelector(check),
10580
- total_count: totalCount,
10581
- discovered_count: numberValue2(linkEvidence.discovered_count) ?? totalCount,
10582
- ok_count: numberValue2(linkEvidence.ok_count) ?? okCount,
10583
- failed_count: failures.length,
10584
- truncated: linkEvidence.truncated === true,
10585
- max_links: numberValue2(linkEvidence.max_links) ?? check.max_links ?? 100,
10586
- result_count: resultCount,
10587
- stored_result_count: storedResultCount,
10588
- omitted_result_count: omittedResultCount,
10589
- omitted_success_count: omittedSuccessCount,
10590
- results_compacted: linkEvidence.results_compacted === true || omittedResultCount > 0,
10591
- min_bytes: check.min_bytes ?? null,
10592
- allowed_content_types: check.allowed_content_types ?? null,
10593
- status_counts: statusCounts,
10594
- failures: failures.slice(0, 20)
10595
- };
10596
- }
10597
- function textKey(check) {
10598
- return check.pattern ? `pattern:${check.pattern}/${check.flags || ""}` : `text:${check.text || ""}`;
10599
- }
10600
- function observeWithinTimeoutMs(check) {
10601
- const raw = check.timeout_ms;
10602
- if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) return Math.min(Math.round(raw), 6e4);
10603
- return 2e3;
10604
- }
10605
- function observeWithinKey(check) {
10606
- const target = check.selector ? `selector:${check.selector}` : "page";
10607
- const expectation = check.pattern ? `pattern:${check.pattern}/${check.flags || ""}` : check.text ? `text:${check.text}` : "visible";
10608
- return `${target}|${expectation}|within:${observeWithinTimeoutMs(check)}`;
10609
- }
10610
- function textSequenceForCheck(viewport, check) {
10611
- const key = selectorKey(check);
10612
- const sequence = viewport.text_sequences?.[key];
10613
- if (isRecord(sequence)) {
10614
- const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
10615
- const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
10616
- const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
10617
- const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
10618
- let candidates;
10619
- if (check.type === "selector_text_visible") {
10620
- candidates = visibleMatchTexts.length ? visibleMatchTexts : visibleTexts;
10621
- } else if (check.type === "selector_text_absent") {
10622
- candidates = matchTexts.length ? matchTexts : texts;
10623
- } else {
10624
- candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
10625
- }
10626
- return candidates.map((text) => String(text).replace(/\s+/g, " ").trim()).filter(Boolean);
10627
- }
10628
- return [];
10629
- }
10630
- function textOrderMatch(texts, expectedTexts) {
10631
- const positions = [];
10632
- let startAt = 0;
10633
- for (const expected of expectedTexts) {
10634
- const index = texts.findIndex((text, offset) => offset >= startAt && text.includes(expected));
10635
- if (index < 0) return { matched: false, positions };
10636
- positions.push(index);
10637
- startAt = index + 1;
10638
- }
10639
- return { matched: true, positions };
10640
- }
10641
- function frameEvidenceForSelector(viewport, selector) {
10642
- const container = viewport.frames?.[selector];
10643
- if (!isRecord(container)) return [];
10644
- const frames = Array.isArray(container.frames) ? container.frames : [];
10645
- return frames.filter((frame) => isRecord(frame));
10646
- }
10647
- function frameTextSample(frame) {
10648
- const parts = [
10649
- frame.title,
10650
- frame.text_sample,
10651
- frame.body_text_sample,
10652
- frame.body_text,
10653
- frame.text
10654
- ];
10655
- return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
10656
- }
10657
- function summarizeRouteInventory(viewport, inventory) {
10658
- const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
10659
- const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
10660
- const sourceLinkCount = numberValue2(inventory.source_link_count) ?? numberValue2(inventory.home_game_link_count) ?? null;
10661
- const sourceUniqueLinkCount = numberValue2(inventory.source_unique_link_count) ?? numberValue2(inventory.home_unique_game_link_count) ?? null;
10662
- const sourceCandidateCount = numberValue2(inventory.source_candidate_count);
10663
- const sourceCandidateUniqueLinkCount = numberValue2(inventory.source_candidate_unique_link_count);
10664
- const sourceLinkScope = stringValue3(inventory.source_link_scope);
10665
- const duplicateSourceLinks = Array.isArray(inventory.duplicate_source_link_paths) ? inventory.duplicate_source_link_paths.map((path6) => String(path6)) : [];
10666
- const duplicateSourceLinkCount = numberValue2(inventory.duplicate_source_link_count) ?? (sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null);
10667
- const failures = Array.isArray(inventory.failures) ? inventory.failures : [];
10668
- return {
10669
- viewport,
10670
- source_link_scope: sourceLinkScope ?? null,
10671
- source_candidate_count: sourceCandidateCount ?? sourceLinkCount,
10672
- source_candidate_unique_link_count: sourceCandidateUniqueLinkCount ?? sourceUniqueLinkCount,
10673
- source_link_count: sourceLinkCount,
10674
- source_unique_link_count: sourceUniqueLinkCount,
10675
- duplicate_source_link_count: duplicateSourceLinkCount,
10676
- duplicate_source_links: duplicateSourceLinks,
10677
- direct_route_count: directRoutes.length,
10678
- clickthrough_count: clickthroughs.length,
10679
- failure_count: failures.length
10680
- };
10681
- }
10682
- function routeInventoryExpectedRouteSummaries(value, fallback) {
10683
- const rawRoutes = Array.isArray(value) ? value : Array.isArray(fallback) ? fallback : [];
10684
- return rawRoutes.map((route) => {
10685
- if (typeof route === "string") {
10686
- const path7 = route.trim();
10687
- return path7 ? { path: path7 } : void 0;
10688
- }
10689
- if (!isRecord(route)) return void 0;
10690
- const path6 = stringValue3(route.path);
10691
- if (!path6) return void 0;
10692
- const name = stringValue3(route.name);
10693
- return name ? { name, path: path6 } : { path: path6 };
10694
- }).filter((route) => Boolean(route));
10695
- }
10696
- function matchText(sample, check) {
10697
- if (check.pattern) {
10698
- try {
10699
- return new RegExp(check.pattern, check.flags || "").test(sample);
10700
- } catch {
10701
- return false;
10702
- }
10703
- }
10704
- return sample.includes(check.text || "");
10705
- }
10706
- function compactTextEvidenceSample(value) {
10707
- return String(value || "").replace(/\s+/g, " ").trim();
10708
- }
10709
- function textSampleAroundMatch(sample, index, length) {
10710
- if (index < 0) return void 0;
10711
- const source = String(sample || "");
10712
- const context = 120;
10713
- const start = Math.max(0, index - context);
10714
- const end = Math.min(source.length, index + Math.max(length, 1) + context);
10715
- const prefix = start > 0 ? "..." : "";
10716
- const suffix = end < source.length ? "..." : "";
10717
- const compacted = compactTextEvidenceSample(`${prefix}${source.slice(start, end)}${suffix}`);
10718
- return compacted ? compacted.slice(0, 240) : void 0;
10719
- }
10720
- function textMatchSamples(sample, check) {
10721
- const source = String(sample || "");
10722
- if (!source) return [];
10723
- if (check.pattern) {
10724
- try {
10725
- const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
10726
- const match = new RegExp(check.pattern, flags).exec(source);
10727
- const sampleText2 = match ? textSampleAroundMatch(source, match.index, match[0]?.length || 1) : void 0;
10728
- return sampleText2 ? [sampleText2] : [];
10729
- } catch {
10730
- return [];
10731
- }
10732
- }
10733
- const text = check.text || "";
10734
- if (!text) return [];
10735
- const index = source.indexOf(text);
10736
- const sampleText = textSampleAroundMatch(source, index, text.length);
10737
- return sampleText ? [sampleText] : [];
10738
- }
10739
- function textCaseInsensitiveSamples(sample, check) {
10740
- const source = String(sample || "");
10741
- const text = check.pattern ? "" : check.text || "";
10742
- if (!source || !text) return [];
10743
- const index = source.toLowerCase().indexOf(text.toLowerCase());
10744
- const sampleText = textSampleAroundMatch(source, index, text.length);
10745
- return sampleText ? [sampleText] : [];
10746
- }
10747
- function textCaseInsensitiveSequenceSamples(texts, check) {
10748
- const text = check.pattern ? "" : compactTextEvidenceSample(check.text || "");
10749
- if (!text) return [];
10750
- const expected = text.toLowerCase();
10751
- return texts.map((candidate) => compactTextEvidenceSample(candidate)).filter((candidate) => candidate && candidate.toLowerCase().includes(expected)).slice(0, 3).map((candidate) => candidate.slice(0, 240));
10752
- }
10753
- function textCaseInsensitiveFailureSamples(viewport, check) {
10754
- const key = textKey(check);
10755
- const captured = viewport.text_case_insensitive_samples?.[key] || [];
10756
- const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
10757
- if (capturedSamples.length) return capturedSamples.slice(0, 3);
10758
- return textCaseInsensitiveSamples(viewport.body_text_sample || "", check).slice(0, 3);
10759
- }
10760
- function textCheckFailureSamples(viewport, check) {
10761
- const key = textKey(check);
10762
- const captured = viewport.text_match_samples?.[key] || [];
10763
- const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
10764
- if (capturedSamples.length) return capturedSamples.slice(0, 3);
10765
- const matchedSamples = textMatchSamples(viewport.body_text_sample || "", check);
10766
- if (matchedSamples.length) return matchedSamples.slice(0, 3);
10767
- const fallback = compactTextEvidenceSample(viewport.body_text_sample || "").slice(0, 240);
10768
- return fallback ? [fallback] : [];
10769
- }
10770
- function allowedMessageSample(input) {
10771
- if (!isRecord(input)) return String(input || "");
10772
- const parts = [
10773
- input.text,
10774
- input.message,
10775
- isRecord(input.location) ? input.location.url : void 0
10776
- ];
10777
- return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
10778
- }
10779
- function matchesAllowedMessage(input, texts, patterns) {
10780
- const sample = allowedMessageSample(input);
10781
- if (texts?.some((text) => sample.includes(text))) return true;
10782
- for (const pattern of patterns || []) {
10783
- try {
10784
- if (new RegExp(pattern).test(sample)) return true;
10785
- } catch {
10786
- continue;
10787
- }
10788
- }
10789
- return false;
10790
- }
10791
- function consoleEventLocationUrl(input) {
10792
- if (!isRecord(input) || !isRecord(input.location)) return void 0;
10793
- return stringValue3(input.location.url);
10794
- }
10795
- function expectedFailedNetworkMockEvents(evidence) {
10796
- return (evidence.network_mocks || []).filter((event) => {
10797
- if (!isRecord(event) || event.ok === false) return false;
10798
- const status = numberValue2(event.status);
10799
- const isHttpFailure = status !== void 0 && status >= 400;
10800
- const isAbortedMock = event.abort === true && Boolean(stringValue3(event.abort_error_code));
10801
- return (isHttpFailure || isAbortedMock) && Boolean(stringValue3(event.url));
10802
- });
10803
- }
10804
- function matchingExpectedFailedNetworkMockConsoleEvent(event, evidence) {
10805
- const sample = allowedMessageSample(event);
10806
- if (!/Failed to load resource/i.test(sample)) return void 0;
10807
- const eventUrl = consoleEventLocationUrl(event);
10808
- if (!eventUrl) return void 0;
10809
- return expectedFailedNetworkMockEvents(evidence).find((mockEvent) => {
10810
- const status = numberValue2(mockEvent.status);
10811
- const abortErrorCode = stringValue3(mockEvent.abort_error_code);
10812
- return stringValue3(mockEvent.url) === eventUrl && (status !== void 0 && sample.includes(String(status)) || Boolean(abortErrorCode) && /Failed to load resource/i.test(sample));
10813
- });
10814
- }
10815
- function isExpectedFailedNetworkMockConsoleEvent(event, evidence) {
10816
- return Boolean(matchingExpectedFailedNetworkMockConsoleEvent(event, evidence));
10817
- }
10818
- function expectedFailedNetworkMockConsoleEventSummary(event, evidence) {
10819
- const match = matchingExpectedFailedNetworkMockConsoleEvent(event, evidence);
10820
- const sample = allowedMessageSample(event);
10821
- return {
10822
- url: consoleEventLocationUrl(event) ?? null,
10823
- status: match ? numberValue2(match.status) ?? null : null,
10824
- abort_error_code: match ? stringValue3(match.abort_error_code) ?? null : null,
10825
- label: match ? stringValue3(match.label) ?? null : null,
10826
- response_label: match ? stringValue3(match.response_label) ?? null : null,
10827
- text: isRecord(event) && typeof event.text === "string" ? event.text.slice(0, 300) : sample.slice(0, 300)
10828
- };
10829
- }
10830
- function normalizeRoutePath2(path6) {
10831
- const value = path6 || "/";
10832
- if (value === "/") return "/";
10833
- return value.replace(/\/+$/, "") || "/";
10834
- }
10835
- function previewMountPrefix(pathname) {
10836
- const value = pathname || "/";
10837
- const apiPreview = value.match(/^(\/s\/[^/]+)(?:\/|$)/);
10838
- if (apiPreview) return apiPreview[1];
10839
- const internalPreview = value.match(/^(\/preview\/[^/]+\/[^/]+)(?:\/|$)/);
10840
- return internalPreview ? internalPreview[1] : "";
10841
- }
10842
- function joinMountedRoutePath(mountPrefix, routePath) {
10843
- const route = routePath.startsWith("/") ? routePath : `/${routePath}`;
10844
- if (!mountPrefix) return route;
10845
- if (route === mountPrefix || route.startsWith(`${mountPrefix}/`)) return route;
10846
- return `${mountPrefix}${route}`;
10847
- }
10848
- function resolveRiddleProofProfileRouteUrl(targetUrl, route) {
10849
- if (/^https?:\/\//i.test(route)) return route;
10850
- if (!targetUrl) return route;
10851
- if (route.startsWith("/")) {
10852
- const base = new URL(targetUrl);
10853
- const mountPrefix = previewMountPrefix(base.pathname);
10854
- if (mountPrefix) {
10855
- const routeParts = new URL(route, base.origin);
10856
- base.pathname = joinMountedRoutePath(mountPrefix, routeParts.pathname);
10857
- base.search = routeParts.search;
10858
- base.hash = routeParts.hash;
10859
- return base.href;
10860
- }
10861
- }
10862
- return new URL(route, targetUrl).href;
10863
- }
10864
- function mountedExpectedRoutePath(targetUrl, expected) {
10865
- if (!targetUrl || !expected || !expected.startsWith("/")) return expected;
10866
- const mountPrefix = previewMountPrefix(new URL(targetUrl).pathname);
10867
- return mountPrefix ? joinMountedRoutePath(mountPrefix, expected) : expected;
10868
- }
10869
- function routePathMatches(observed, expected, targetUrl) {
10870
- const normalizedObserved = normalizeRoutePath2(observed);
10871
- const normalizedExpected = normalizeRoutePath2(expected);
10872
- if (normalizedObserved === normalizedExpected) return true;
10873
- return normalizedObserved === normalizeRoutePath2(mountedExpectedRoutePath(targetUrl, expected));
10874
- }
10875
- function successfulRoute(route, targetUrl) {
10876
- const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
10877
- return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
10878
- }
10879
- function routeReadinessFailed(route, targetUrl) {
10880
- const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
10881
- const httpOk = route.http_status === null || route.http_status === void 0 || route.http_status < 400;
10882
- return matched && httpOk && Boolean(route.error);
10883
- }
10884
- function compactRouteError(error) {
10885
- const text = typeof error === "string" ? error.replace(/\s+/g, " ").trim() : "";
10886
- return text ? text.slice(0, 240) : void 0;
10887
- }
10888
- function routeLoadedFailureMessage(failedRoutes, expectedPath, targetUrl) {
10889
- const readinessFailures = failedRoutes.filter((route) => routeReadinessFailed(route, targetUrl));
10890
- if (readinessFailures.length === failedRoutes.length && readinessFailures.length) {
10891
- const sample = compactRouteError(readinessFailures.find((route) => route.error)?.error);
10892
- return `Route matched ${expectedPath}, but readiness failed in ${readinessFailures.length} viewport(s)${sample ? `: ${sample}` : "."}`;
10893
- }
10894
- if (readinessFailures.length) {
10895
- const sample = compactRouteError(readinessFailures.find((route) => route.error)?.error);
10896
- return `Route did not become ready as ${expectedPath} in ${failedRoutes.length} viewport(s); ${readinessFailures.length} matched path and HTTP but failed readiness${sample ? `: ${sample}` : "."}`;
10897
- }
10898
- return `Route did not load as ${expectedPath} in ${failedRoutes.length} viewport(s).`;
10899
- }
10900
- function parseEvidenceUrl(value, targetUrl) {
10901
- if (!value) return void 0;
10902
- try {
10903
- return targetUrl ? new URL(value, targetUrl) : new URL(value);
10904
- } catch {
10905
- try {
10906
- return new URL(value);
10907
- } catch {
10908
- return void 0;
10909
- }
10910
- }
10911
- }
10912
- function observedUrlForViewport(viewport, targetUrl) {
10913
- return parseEvidenceUrl(viewport.url, targetUrl) || parseEvidenceUrl(viewport.route?.observed, targetUrl) || parseEvidenceUrl(viewport.route?.requested, targetUrl) || parseEvidenceUrl(targetUrl, void 0);
10914
- }
10915
- function searchParamObservation(viewport, targetUrl, param) {
10916
- const url = observedUrlForViewport(viewport, targetUrl);
10917
- const values = url ? url.searchParams.getAll(param) : [];
10918
- return {
10919
- viewport: viewport.name,
10920
- url: url?.href || null,
10921
- value: values.length ? values[0] : null,
10922
- values,
10923
- present: values.length > 0
10924
- };
10925
- }
10926
- function viewportsForCheck(check, viewports) {
10927
- if (!check.viewports?.length) return viewports;
10928
- const names = new Set(check.viewports);
10929
- return viewports.filter((viewport) => names.has(viewport.name));
10930
- }
10931
- function setupActionAppliesToViewport(action, viewportName) {
10932
- if (!action.viewports?.length) return true;
10933
- return Boolean(viewportName && action.viewports.includes(viewportName));
10934
- }
10935
- function setupActionsForViewport(actions, viewportName) {
10936
- return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
10937
- }
10938
- function assessCheckFromEvidence(check, evidence) {
10939
- const viewports = viewportsForCheck(check, evidence.viewports || []);
10940
- if (!viewports.length) {
10941
- return {
10942
- type: check.type,
10943
- label: checkLabel(check),
10944
- status: "failed",
10945
- evidence: {
10946
- expected_viewports: check.viewports || []
10947
- },
10948
- message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
10949
- };
10950
- }
10951
- if (isDialogCountCheckType(check.type)) {
10952
- const field = dialogCountFieldForCheckType(check.type);
10953
- const expectedCount = check.expected_count ?? 0;
10954
- const actualCount = numberValue2(evidence.dom_summary?.[field]) ?? 0;
10955
- return {
10956
- type: check.type,
10957
- label: checkLabel(check),
10958
- status: actualCount === expectedCount ? "passed" : "failed",
10959
- evidence: {
10960
- field,
10961
- expected_count: expectedCount,
10962
- count: actualCount
10963
- },
10964
- message: actualCount === expectedCount ? void 0 : `${field} did not equal ${expectedCount}; observed ${actualCount}.`
10965
- };
10966
- }
10967
- if (check.type === "route_loaded") {
10968
- const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
10969
- const routes = viewports.map((viewport) => ({
10970
- ...viewport.route,
10971
- expected_path: expectedPath,
10972
- matched: routePathMatches(viewport.route.observed, expectedPath, evidence.target_url) || viewport.route.matched
10973
- }));
10974
- const failed = routes.filter((route) => !successfulRoute(route, evidence.target_url));
10975
- return {
10976
- type: check.type,
10977
- label: checkLabel(check),
10978
- status: failed.length ? "failed" : "passed",
10979
- evidence: {
10980
- expected_path: expectedPath,
10981
- observed_paths: viewports.map((viewport) => viewport.route.observed),
10982
- http_statuses: viewports.map((viewport) => viewport.route.http_status ?? null),
10983
- route_errors: viewports.map((viewport) => viewport.route.error ?? null)
10984
- },
10985
- message: failed.length ? routeLoadedFailureMessage(failed, expectedPath, evidence.target_url) : void 0
10986
- };
10987
- }
10988
- if (check.type === "url_search_param_equals") {
10989
- const param = check.param || "";
10990
- const expectedValue = check.expected_value ?? "";
10991
- const observations = viewports.map((viewport) => searchParamObservation(viewport, evidence.target_url, param));
10992
- const failed = observations.filter((observation) => observation.value !== expectedValue);
10993
- return {
10994
- type: check.type,
10995
- label: checkLabel(check),
10996
- status: failed.length ? "failed" : "passed",
10997
- evidence: {
10998
- param,
10999
- expected_value: expectedValue,
11000
- observed_values: observations.map((observation) => observation.value),
11001
- observed_all_values: observations.map((observation) => observation.values),
11002
- observed_urls: observations.map((observation) => observation.url),
11003
- viewports: observations.map((observation) => toJsonValue(observation))
11004
- },
11005
- message: failed.length ? `URL search param ${param} did not equal ${JSON.stringify(expectedValue)} in ${failed.length} viewport(s).` : void 0
11006
- };
11007
- }
11008
- if (check.type === "url_search_param_absent") {
11009
- const param = check.param || "";
11010
- const observations = viewports.map((viewport) => searchParamObservation(viewport, evidence.target_url, param));
11011
- const failed = observations.filter((observation) => observation.present);
11012
- return {
11013
- type: check.type,
11014
- label: checkLabel(check),
11015
- status: failed.length ? "failed" : "passed",
11016
- evidence: {
11017
- param,
11018
- observed_values: observations.map((observation) => observation.value),
11019
- observed_all_values: observations.map((observation) => observation.values),
11020
- observed_urls: observations.map((observation) => observation.url),
11021
- viewports: observations.map((observation) => toJsonValue(observation))
11022
- },
11023
- message: failed.length ? `URL search param ${param} was present in ${failed.length} viewport(s).` : void 0
11024
- };
11025
- }
11026
- if (check.type === "selector_visible") {
11027
- const key = selectorKey(check);
11028
- const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.visible_count || 0) < 1);
11029
- return {
11030
- type: check.type,
11031
- label: checkLabel(check),
11032
- status: failed.length ? "failed" : "passed",
11033
- evidence: {
11034
- selector: key,
11035
- visible_counts: viewports.map((viewport) => viewport.selectors?.[key]?.visible_count || 0)
11036
- },
11037
- message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
11038
- };
11039
- }
11040
- if (check.type === "selector_absent") {
11041
- const key = selectorKey(check);
11042
- const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) > 0);
11043
- return {
11044
- type: check.type,
11045
- label: checkLabel(check),
11046
- status: failed.length ? "failed" : "passed",
11047
- evidence: {
11048
- selector: key,
11049
- counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
11050
- },
11051
- message: failed.length ? `Selector ${key} was present in ${failed.length} viewport(s).` : void 0
11052
- };
11053
- }
11054
- if (check.type === "selector_count_at_least") {
11055
- const key = selectorKey(check);
11056
- const minCount = check.min_count ?? 1;
11057
- const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) < minCount);
11058
- return {
11059
- type: check.type,
11060
- label: checkLabel(check),
11061
- status: failed.length ? "failed" : "passed",
11062
- evidence: {
11063
- selector: key,
11064
- min_count: minCount,
11065
- counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
11066
- },
11067
- message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
11068
- };
11069
- }
11070
- if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
11071
- const key = selectorKey(check);
11072
- const expectedCount = check.expected_count ?? 0;
11073
- const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) !== expectedCount);
11074
- return {
11075
- type: check.type,
11076
- label: checkLabel(check),
11077
- status: failed.length ? "failed" : "passed",
11078
- evidence: {
11079
- selector: key,
11080
- expected_count: expectedCount,
11081
- counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
11082
- },
11083
- message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
11084
- };
11085
- }
11086
- if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
11087
- const key = selectorKey(check);
11088
- const expectedVisible = check.type === "selector_text_visible";
11089
- const results = viewports.map((viewport) => {
11090
- const texts = textSequenceForCheck(viewport, check);
11091
- const matches = texts.filter((text) => matchText(text, check));
11092
- const matched = matches.length > 0;
11093
- const failedAgainstExpectation = matched !== expectedVisible;
11094
- const sampleTexts = matches.length ? matches : failedAgainstExpectation ? texts : [];
11095
- const caseInsensitiveSamples = failedAgainstExpectation && expectedVisible && !matched ? textCaseInsensitiveSequenceSamples(texts, check) : [];
11096
- return {
11097
- viewport: viewport.name,
11098
- selector_count: viewport.selectors?.[key]?.count || 0,
11099
- visible_count: viewport.selectors?.[key]?.visible_count || 0,
11100
- matched_count: matches.length,
11101
- matched,
11102
- samples: sampleTexts.slice(0, 3).map((text) => text.slice(0, 240)),
11103
- case_insensitive_samples: caseInsensitiveSamples
11104
- };
11105
- });
11106
- const failed = results.filter((result) => result.matched !== expectedVisible).length;
11107
- return {
11108
- type: check.type,
11109
- label: checkLabel(check),
11110
- status: failed ? "failed" : "passed",
11111
- evidence: {
11112
- selector: key,
11113
- text: check.text || null,
11114
- pattern: check.pattern || null,
11115
- viewports: results.map((result) => toJsonValue(result))
11116
- },
11117
- message: failed ? `Selector ${key} text assertion failed in ${failed} viewport(s).` : void 0
11118
- };
11119
- }
11120
- if (check.type === "selector_text_order") {
11121
- const key = selectorKey(check);
11122
- const expectedTexts = check.expected_texts || [];
11123
- const results = viewports.map((viewport) => {
11124
- const texts = textSequenceForCheck(viewport, check);
11125
- const match = textOrderMatch(texts, expectedTexts);
11126
- return {
11127
- viewport: viewport.name,
11128
- matched: match.matched,
11129
- matched_positions: match.positions,
11130
- visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240))
11131
- };
11132
- });
11133
- const failed = results.filter((result) => !result.matched).length;
11134
- return {
11135
- type: check.type,
11136
- label: checkLabel(check),
11137
- status: failed ? "failed" : "passed",
11138
- evidence: {
11139
- selector: key,
11140
- expected_texts: expectedTexts,
11141
- viewports: results.map((result) => toJsonValue(result))
11142
- },
11143
- message: failed ? `Selector ${key} text order failed in ${failed} viewport(s).` : void 0
11144
- };
11145
- }
11146
- if (check.type === "ordered_trace") {
11147
- const assessments = viewports.map((viewport) => ({
11148
- viewport: viewport.name,
11149
- assessment: assessRiddleProofOrderedTraceSetupResults(
11150
- viewport.setup_action_results,
11151
- check.setup_action_label || "",
11152
- check.trace_path || "",
11153
- check.events || []
11154
- )
8179
+ function httpStatusBodyJsonAssertionFailures(result, check) {
8180
+ const expected = check.body_json_assertions?.filter((assertion) => assertion.path) ?? [];
8181
+ if (!expected.length) return [];
8182
+ if (!Array.isArray(result.body_json_assertions)) {
8183
+ return expected.map((assertion) => ({
8184
+ label: assertion.label || assertion.path,
8185
+ path: assertion.path,
8186
+ ok: false,
8187
+ exists: false,
8188
+ observed_type: "missing",
8189
+ errors: ["body_json_assertions evidence missing"]
11155
8190
  }));
11156
- const insufficient = assessments.filter((item) => item.assessment.status === "proof_insufficient");
11157
- const failed = assessments.filter((item) => item.assessment.status === "failed");
11158
- const status = insufficient.length ? "proof_insufficient" : failed.length ? "failed" : "passed";
11159
- return {
11160
- type: check.type,
11161
- label: checkLabel(check),
11162
- status,
11163
- evidence: {
11164
- setup_action_label: check.setup_action_label || "",
11165
- trace_path: check.trace_path || "",
11166
- events: toJsonValue((check.events || []).map((event) => event.label)),
11167
- viewports: toJsonValue(assessments)
11168
- },
11169
- message: insufficient.length ? `Ordered trace evidence was insufficient in ${insufficient.length} viewport(s).` : failed.length ? `Ordered trace did not contain the required event sequence in ${failed.length} viewport(s).` : void 0
11170
- };
11171
- }
11172
- if (check.type === "observe_within") {
11173
- const key = observeWithinKey(check);
11174
- const timeoutMs = observeWithinTimeoutMs(check);
11175
- const results = viewports.map((viewport) => {
11176
- const observation = viewport.observations?.[key];
11177
- const matched = observation?.matched === true;
11178
- return {
11179
- viewport: viewport.name,
11180
- matched,
11181
- elapsed_ms: numberValue2(observation?.elapsed_ms) ?? null,
11182
- timeout_ms: numberValue2(observation?.timeout_ms) ?? timeoutMs,
11183
- attempts: numberValue2(observation?.attempts) ?? null,
11184
- selector_count: numberValue2(observation?.selector_count) ?? null,
11185
- visible_count: numberValue2(observation?.visible_count) ?? null,
11186
- matched_count: numberValue2(observation?.matched_count) ?? null,
11187
- sample: stringValue3(observation?.sample) ?? null,
11188
- error: stringValue3(observation?.error) ?? null
11189
- };
11190
- });
11191
- const failed = results.filter((result) => !result.matched).length;
11192
- return {
11193
- type: check.type,
11194
- label: checkLabel(check),
11195
- status: failed ? "failed" : "passed",
11196
- evidence: {
11197
- selector: check.selector || null,
11198
- text: check.text || null,
11199
- pattern: check.pattern || null,
11200
- timeout_ms: timeoutMs,
11201
- viewports: results.map((result) => toJsonValue(result))
11202
- },
11203
- message: failed ? `Observation did not match within ${timeoutMs}ms in ${failed} viewport(s).` : void 0
11204
- };
11205
- }
11206
- if (check.type === "frame_text_visible") {
11207
- const key = selectorKey(check);
11208
- const results = viewports.map((viewport) => {
11209
- const frames = frameEvidenceForSelector(viewport, key);
11210
- const matches = frames.map((frame, index) => ({ index, frame, matched: matchText(frameTextSample(frame), check) })).filter((item) => item.matched);
11211
- return {
11212
- viewport: viewport.name,
11213
- frame_count: frames.length,
11214
- matched_count: matches.length,
11215
- matched: matches.length > 0,
11216
- urls: frames.map((frame) => String(frame.url || "")).filter(Boolean).slice(0, 10),
11217
- samples: matches.map((item) => frameTextSample(item.frame).replace(/\s+/g, " ").trim().slice(0, 240)).slice(0, 3)
11218
- };
11219
- });
11220
- const failed = results.filter((result) => !result.matched).length;
11221
- return {
11222
- type: check.type,
11223
- label: checkLabel(check),
11224
- status: failed ? "failed" : "passed",
11225
- evidence: {
11226
- selector: key,
11227
- text: check.text || null,
11228
- pattern: check.pattern || null,
11229
- viewports: results.map((result) => toJsonValue(result))
11230
- },
11231
- message: failed ? `Frame selector ${key} did not contain expected text in ${failed} viewport(s).` : void 0
11232
- };
11233
- }
11234
- if (check.type === "frame_url_equals" || check.type === "frame_url_matches") {
11235
- const key = selectorKey(check);
11236
- const expectedUrl = check.expected_url || check.expected_value || "";
11237
- const results = viewports.map((viewport) => {
11238
- const frames = frameEvidenceForSelector(viewport, key);
11239
- const urls = frames.map((frame) => String(frame.url || "")).filter(Boolean);
11240
- const matches = urls.filter((url) => check.type === "frame_url_equals" ? url === expectedUrl : matchText(url, check));
11241
- return {
11242
- viewport: viewport.name,
11243
- frame_count: frames.length,
11244
- matched_count: matches.length,
11245
- matched: matches.length > 0,
11246
- urls: urls.slice(0, 10)
11247
- };
11248
- });
11249
- const failed = results.filter((result) => !result.matched).length;
11250
- return {
11251
- type: check.type,
11252
- label: checkLabel(check),
11253
- status: failed ? "failed" : "passed",
11254
- evidence: {
11255
- selector: key,
11256
- expected_url: check.type === "frame_url_equals" ? expectedUrl : null,
11257
- pattern: check.type === "frame_url_matches" ? check.pattern || null : null,
11258
- viewports: results.map((result) => toJsonValue(result))
11259
- },
11260
- message: failed ? `Frame selector ${key} URL assertion failed in ${failed} viewport(s).` : void 0
11261
- };
11262
- }
11263
- if (check.type === "frame_no_horizontal_overflow") {
11264
- const key = selectorKey(check);
11265
- const maxOverflow = check.max_overflow_px ?? 4;
11266
- const results = viewports.map((viewport) => {
11267
- const frames = frameEvidenceForSelector(viewport, key);
11268
- const frameOverflows = frames.map((frame, index) => ({
11269
- index,
11270
- url: String(frame.url || ""),
11271
- overflow_px: horizontalBoundsOverflowPx(frame),
11272
- offender_count: boundsOffendersForEvidence(frame).length
11273
- }));
11274
- const maxFrameOverflow = frameOverflows.reduce((max, frame) => Math.max(max, frame.overflow_px), 0);
11275
- return {
11276
- viewport: viewport.name,
11277
- frame_count: frames.length,
11278
- max_overflow_px: roundPixels(maxFrameOverflow),
11279
- failed_frame_count: frameOverflows.filter((frame) => frame.overflow_px > maxOverflow).length,
11280
- frames: frameOverflows.map((frame) => toJsonValue(frame))
11281
- };
11282
- });
11283
- const failed = results.filter((result) => result.frame_count < 1 || result.failed_frame_count > 0).length;
11284
- return {
11285
- type: check.type,
11286
- label: checkLabel(check),
11287
- status: failed ? "failed" : "passed",
11288
- evidence: {
11289
- selector: key,
11290
- max_overflow_px: maxOverflow,
11291
- viewports: results.map((result) => toJsonValue(result))
11292
- },
11293
- message: failed ? `Frame selector ${key} overflow exceeded ${maxOverflow}px or was missing in ${failed} viewport(s).` : void 0
11294
- };
11295
- }
11296
- if (check.type === "text_visible" || check.type === "text_absent") {
11297
- const key = textKey(check);
11298
- const expectedVisible = check.type === "text_visible";
11299
- const results = viewports.map((viewport) => {
11300
- const fromEvidence = viewport.text_matches?.[key];
11301
- const matched = typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
11302
- const failedAgainstExpectation = matched !== expectedVisible;
11303
- return {
11304
- viewport: viewport.name,
11305
- matched,
11306
- samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
11307
- case_insensitive_samples: failedAgainstExpectation && expectedVisible && !matched ? textCaseInsensitiveFailureSamples(viewport, check) : []
11308
- };
11309
- });
11310
- const matches = results.map((result) => result.matched);
11311
- const failed = matches.filter((matched) => matched !== expectedVisible).length;
11312
- return {
11313
- type: check.type,
11314
- label: checkLabel(check),
11315
- status: failed ? "failed" : "passed",
11316
- evidence: {
11317
- text: check.text || null,
11318
- pattern: check.pattern || null,
11319
- matches,
11320
- viewports: results.map((result) => toJsonValue(result))
11321
- },
11322
- message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
11323
- };
11324
- }
11325
- if (check.type === "http_status") {
11326
- const url = httpStatusRequestUrl(check, evidence.target_url);
11327
- const method = httpStatusMethod(check);
11328
- const summaries = viewports.map((viewport) => summarizeHttpStatusEvidence(viewport, check));
11329
- const failed = summaries.filter((summary) => Array.isArray(summary.failures) && summary.failures.length > 0);
11330
- return {
11331
- type: check.type,
11332
- label: checkLabel(check),
11333
- status: failed.length ? "failed" : "passed",
11334
- evidence: {
11335
- url,
11336
- method,
11337
- allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
11338
- require_nonzero_bytes: check.require_nonzero_bytes === true,
11339
- min_bytes: check.min_bytes ?? null,
11340
- allowed_content_types: check.allowed_content_types ?? null,
11341
- body_contains: check.body_contains ?? [],
11342
- body_not_contains: check.body_not_contains ?? [],
11343
- body_not_patterns: check.body_not_patterns ?? [],
11344
- body_json_assertions: toJsonValue(check.body_json_assertions ?? []),
11345
- viewports: summaries.map((summary) => toJsonValue(summary)),
11346
- failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue3(summary.viewport) ?? null, failure })) : [])
11347
- },
11348
- message: failed.length ? `HTTP status failed in ${failed.length} viewport(s).` : void 0
11349
- };
11350
- }
11351
- if (check.type === "link_status" || check.type === "artifact_link_status") {
11352
- const selector = linkStatusSelector(check);
11353
- const summaries = viewports.map((viewport) => summarizeLinkStatusEvidence(viewport, check));
11354
- const failed = summaries.filter((summary) => (numberValue2(summary.failed_count) ?? 0) > 0);
11355
- return {
11356
- type: check.type,
11357
- label: checkLabel(check),
11358
- status: failed.length ? "failed" : "passed",
11359
- evidence: {
11360
- selector,
11361
- expected_count: check.expected_count ?? null,
11362
- min_count: check.min_count ?? null,
11363
- allowed_statuses: linkStatusAllowedStatuses(check) || ["2xx", "3xx"],
11364
- require_nonzero_bytes: check.require_nonzero_bytes === true,
11365
- min_bytes: check.min_bytes ?? null,
11366
- allowed_content_types: check.allowed_content_types ?? null,
11367
- viewports: summaries.map((summary) => toJsonValue(summary)),
11368
- failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue3(summary.viewport) ?? null, failure })) : [])
11369
- },
11370
- message: failed.length ? `Link status failed in ${failed.length} viewport(s).` : void 0
11371
- };
11372
- }
11373
- if (check.type === "route_inventory") {
11374
- const inventories = viewports.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory })).filter((item) => isRecord(item.inventory));
11375
- if (!inventories.length) {
11376
- return {
11377
- type: check.type,
11378
- label: checkLabel(check),
11379
- status: "failed",
11380
- evidence: {
11381
- expected_count: check.expected_routes?.length || 0,
11382
- expected_routes: routeInventoryExpectedRouteSummaries(void 0, check.expected_routes)
11383
- },
11384
- message: "No route inventory evidence was captured."
11385
- };
11386
- }
11387
- const viewportSummaries = inventories.map((item) => summarizeRouteInventory(item.viewport, item.inventory));
11388
- const failures = inventories.flatMap((item) => Array.isArray(item.inventory?.failures) ? item.inventory.failures.map((failure) => toJsonValue({ viewport: item.viewport, failure })) : []);
11389
- const first = inventories[0]?.inventory;
11390
- const directRoutes = Array.isArray(first?.direct_routes) ? first.direct_routes : [];
11391
- const clickthroughs = Array.isArray(first?.clickthroughs) ? first.clickthroughs : [];
11392
- const sourceLinkCount = numberValue2(first?.source_link_count) ?? numberValue2(first?.home_game_link_count) ?? null;
11393
- const sourceUniqueLinkCount = numberValue2(first?.source_unique_link_count) ?? numberValue2(first?.home_unique_game_link_count) ?? null;
11394
- const sourceCandidateCount = numberValue2(first?.source_candidate_count);
11395
- const sourceCandidateUniqueLinkCount = numberValue2(first?.source_candidate_unique_link_count);
11396
- const sourceLinkScope = stringValue3(first?.source_link_scope);
11397
- const duplicateSourceLinks = Array.isArray(first?.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path6) => String(path6)) : [];
11398
- const duplicateSourceLinkCount = numberValue2(first?.duplicate_source_link_count) ?? (sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null);
11399
- const expectedRoutes = routeInventoryExpectedRouteSummaries(first?.expected_routes, check.expected_routes);
11400
- return {
11401
- type: check.type,
11402
- label: checkLabel(check),
11403
- status: failures.length ? "failed" : "passed",
11404
- evidence: {
11405
- expected_count: check.expected_routes?.length || 0,
11406
- expected_routes: expectedRoutes,
11407
- source_link_scope: sourceLinkScope ?? null,
11408
- source_candidate_count: sourceCandidateCount ?? sourceLinkCount,
11409
- source_candidate_unique_link_count: sourceCandidateUniqueLinkCount ?? sourceUniqueLinkCount,
11410
- source_link_count: sourceLinkCount,
11411
- source_unique_link_count: sourceUniqueLinkCount,
11412
- duplicate_source_link_count: duplicateSourceLinkCount,
11413
- duplicate_source_links: duplicateSourceLinks,
11414
- duplicates_allowed: check.require_unique_routes === false,
11415
- homepage_link_count: numberValue2(first?.home_game_link_count) ?? sourceLinkCount,
11416
- homepage_unique_link_count: numberValue2(first?.home_unique_game_link_count) ?? sourceUniqueLinkCount,
11417
- direct_route_count: directRoutes.length,
11418
- clickthrough_count: clickthroughs.length,
11419
- viewport_count: inventories.length,
11420
- viewports: viewportSummaries.map((summary) => toJsonValue(summary)),
11421
- failures
11422
- },
11423
- message: failures.length ? `Route inventory failed with ${failures.length} issue(s).` : void 0
11424
- };
11425
- }
11426
- if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
11427
- const maxOverflow = check.max_overflow_px ?? 4;
11428
- const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
11429
- if (!applicable.length) {
11430
- const mobileOnly = check.type === "no_mobile_horizontal_overflow";
11431
- return {
11432
- type: check.type,
11433
- label: checkLabel(check),
11434
- status: mobileOnly ? "skipped" : "failed",
11435
- evidence: { max_overflow_px: maxOverflow, viewports: [] },
11436
- message: mobileOnly ? "No mobile viewport evidence was captured; mobile overflow check skipped." : "No applicable viewport evidence was captured for overflow check."
11437
- };
11438
- }
11439
- const failed = applicable.filter((viewport) => horizontalBoundsOverflowPx(viewport) > maxOverflow);
11440
- return {
11441
- type: check.type,
11442
- label: checkLabel(check),
11443
- status: failed.length ? "failed" : "passed",
11444
- evidence: {
11445
- max_overflow_px: maxOverflow,
11446
- overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
11447
- bounds_overflow_px: applicable.map((viewport) => horizontalBoundsOverflowPx(viewport)),
11448
- overflow_offender_counts: applicable.map((viewport) => boundsOffendersForEvidence(viewport).length),
11449
- viewports: applicable.map((viewport) => viewport.name)
11450
- },
11451
- message: failed.length ? `Horizontal bounds overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
11452
- };
11453
- }
11454
- if (check.type === "no_fatal_console_errors") {
11455
- const fatalConsoleEvents = (evidence.console?.events || []).filter((event) => event.type === "error" || event.type === "assert");
11456
- const explicitlyAllowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
11457
- const expectedNetworkMockConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns) && isExpectedFailedNetworkMockConsoleEvent(event, evidence));
11458
- const allowedConsoleEvents = fatalConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns) || isExpectedFailedNetworkMockConsoleEvent(event, evidence));
11459
- const unallowedConsoleEvents = fatalConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns) && !isExpectedFailedNetworkMockConsoleEvent(event, evidence));
11460
- const pageErrors = evidence.page_errors || [];
11461
- const allowedPageErrors = pageErrors.filter((error) => matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
11462
- const unallowedPageErrors = pageErrors.filter((error) => !matchesAllowedMessage(error, check.allowed_page_error_texts, check.allowed_page_error_patterns));
11463
- const fatalCount = unallowedConsoleEvents.length + unallowedPageErrors.length;
11464
- return {
11465
- type: check.type,
11466
- label: checkLabel(check),
11467
- status: fatalCount ? "failed" : "passed",
11468
- evidence: {
11469
- console_fatal_count: unallowedConsoleEvents.length,
11470
- page_error_count: unallowedPageErrors.length,
11471
- total_console_fatal_count: fatalConsoleEvents.length,
11472
- total_page_error_count: pageErrors.length,
11473
- allowed_console_fatal_count: allowedConsoleEvents.length,
11474
- explicitly_allowed_console_fatal_count: explicitlyAllowedConsoleEvents.length,
11475
- allowed_expected_network_mock_console_count: expectedNetworkMockConsoleEvents.length,
11476
- allowed_expected_network_mock_console_events: expectedNetworkMockConsoleEvents.slice(0, 5).map((event) => expectedFailedNetworkMockConsoleEventSummary(event, evidence)),
11477
- allowed_page_error_count: allowedPageErrors.length,
11478
- allowed_console_texts: check.allowed_console_texts || [],
11479
- allowed_console_patterns: check.allowed_console_patterns || [],
11480
- allowed_page_error_texts: check.allowed_page_error_texts || [],
11481
- allowed_page_error_patterns: check.allowed_page_error_patterns || []
11482
- },
11483
- message: fatalCount ? `${fatalCount} fatal browser error(s) were captured.` : void 0
11484
- };
11485
- }
11486
- if (check.type === "no_console_warnings") {
11487
- const warningConsoleEvents = (evidence.console?.events || []).filter((event) => event.type === "warning" || event.type === "warn");
11488
- const allowedConsoleEvents = warningConsoleEvents.filter((event) => matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
11489
- const unallowedConsoleEvents = warningConsoleEvents.filter((event) => !matchesAllowedMessage(event, check.allowed_console_texts, check.allowed_console_patterns));
11490
- return {
11491
- type: check.type,
11492
- label: checkLabel(check),
11493
- status: unallowedConsoleEvents.length ? "failed" : "passed",
11494
- evidence: {
11495
- console_warning_count: unallowedConsoleEvents.length,
11496
- total_console_warning_count: warningConsoleEvents.length,
11497
- allowed_console_warning_count: allowedConsoleEvents.length,
11498
- allowed_console_texts: check.allowed_console_texts || [],
11499
- allowed_console_patterns: check.allowed_console_patterns || [],
11500
- unallowed_console_warning_samples: unallowedConsoleEvents.slice(0, 5).map((event) => allowedMessageSample(event)),
11501
- allowed_console_warning_samples: allowedConsoleEvents.slice(0, 5).map((event) => allowedMessageSample(event))
11502
- },
11503
- message: unallowedConsoleEvents.length ? `${unallowedConsoleEvents.length} console warning(s) were captured.` : void 0
11504
- };
11505
- }
11506
- return {
11507
- type: check.type,
11508
- label: checkLabel(check),
11509
- status: "needs_human_review",
11510
- evidence: {},
11511
- message: "Unsupported check type."
11512
- };
11513
- }
11514
- function assessSetupActionsFromEvidence(profile, evidence) {
11515
- if (!profile.target.setup_actions?.length) return void 0;
11516
- const actionCount = profile.target.setup_actions.length;
11517
- const failed = [];
11518
- const viewports = evidence.viewports || [];
11519
- const expectedActionCountByViewport = /* @__PURE__ */ new Map();
11520
- for (const viewport of viewports) {
11521
- const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
11522
- expectedActionCountByViewport.set(viewport.name, expectedActionCount);
11523
- const results = viewport.setup_action_results || [];
11524
- for (const result of results) {
11525
- if (result.ok === false && result.optional !== true) {
11526
- failed.push({
11527
- viewport: viewport.name,
11528
- action: result.action ?? result.type ?? null,
11529
- selector: result.selector ?? null,
11530
- frame_selector: result.frame_selector ?? null,
11531
- frame_index: result.frame_index ?? null,
11532
- reason: result.reason ?? result.error ?? null
11533
- });
11534
- }
11535
- }
11536
- if (results.length < expectedActionCount && results.every((result) => result.ok !== false || result.optional === true)) {
11537
- failed.push({
11538
- viewport: viewport.name,
11539
- action: "setup_actions",
11540
- selector: null,
11541
- reason: `missing setup action results: ${results.length}/${expectedActionCount}`
11542
- });
11543
- }
11544
- }
11545
- return {
11546
- type: "setup_actions_succeeded",
11547
- label: "setup actions succeeded",
11548
- status: failed.length ? "failed" : "passed",
11549
- evidence: {
11550
- action_count: actionCount,
11551
- viewports: viewports.map((viewport) => ({
11552
- name: viewport.name,
11553
- expected_action_count: expectedActionCountByViewport.get(viewport.name) ?? actionCount,
11554
- ok: (viewport.setup_action_results || []).length >= (expectedActionCountByViewport.get(viewport.name) ?? actionCount) && (viewport.setup_action_results || []).every((result) => result.ok !== false || result.optional === true),
11555
- result_count: (viewport.setup_action_results || []).length
11556
- })),
11557
- setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountByViewport, profile.target.screenshot_full_page),
11558
- failed
11559
- },
11560
- message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
11561
- };
11562
- }
11563
- function assessNetworkMocksFromEvidence(profile, evidence) {
11564
- const mocks = profile.target.network_mocks || [];
11565
- if (!mocks.length) return void 0;
11566
- const events = evidence.network_mocks || [];
11567
- const failed = [];
11568
- const requiredMocks = mocks.filter((mock) => mock.required !== false);
11569
- const hitCountForMock = (mock) => events.filter((event) => event.label === mock.label && event.ok !== false).length;
11570
- for (const mock of requiredMocks) {
11571
- const hits = hitCountForMock(mock);
11572
- const requiredHitCount = requiredNetworkMockHitCount(mock);
11573
- if (hits < requiredHitCount) {
11574
- failed.push({
11575
- label: mock.label,
11576
- url: mock.url,
11577
- method: mock.method || null,
11578
- reason: hits < 1 ? "required_mock_not_hit" : "required_mock_hit_count_not_met",
11579
- required_hit_count: requiredHitCount,
11580
- hit_count: hits
11581
- });
11582
- }
11583
- }
11584
- for (const mock of mocks) {
11585
- if (mock.max_hit_count === void 0) continue;
11586
- const hits = hitCountForMock(mock);
11587
- if (hits > mock.max_hit_count) {
11588
- failed.push({
11589
- label: mock.label,
11590
- url: mock.url,
11591
- method: mock.method || null,
11592
- reason: mock.max_hit_count === 0 ? "forbidden_mock_hit" : "mock_hit_count_exceeded",
11593
- max_hit_count: mock.max_hit_count,
11594
- hit_count: hits
11595
- });
11596
- }
11597
- }
11598
- for (const event of events) {
11599
- if (event.ok === false) {
11600
- failed.push({
11601
- label: event.label ?? null,
11602
- url: event.url ?? null,
11603
- method: event.method ?? null,
11604
- reason: event.reason ?? event.error ?? "mock_failed"
11605
- });
11606
- }
11607
- if (event.request_body_matches === false) {
11608
- failed.push({
11609
- label: event.label ?? null,
11610
- response_label: event.response_label ?? null,
11611
- hit_index: event.hit_index ?? null,
11612
- response_index: event.response_index ?? null,
11613
- url: event.url ?? null,
11614
- method: event.method ?? null,
11615
- reason: "request_body_mismatch",
11616
- request_body_failures: event.request_body_failures ?? [],
11617
- request_body_length: event.request_body_length ?? null,
11618
- request_body_sample: event.request_body_sample ?? null
11619
- });
11620
- }
11621
8191
  }
11622
- return {
11623
- type: "network_mocks_succeeded",
11624
- label: "network mocks succeeded",
11625
- status: failed.length ? "failed" : "passed",
11626
- evidence: {
11627
- mock_count: mocks.length,
11628
- required_count: requiredMocks.length,
11629
- hit_count: events.filter((event) => event.ok !== false).length,
11630
- hits_by_label: Object.fromEntries(mocks.map((mock) => [
11631
- mock.label,
11632
- hitCountForMock(mock)
11633
- ])),
11634
- required_hits_by_label: Object.fromEntries(requiredMocks.map((mock) => [
11635
- mock.label,
11636
- requiredNetworkMockHitCount(mock)
11637
- ])),
11638
- max_hits_by_label: Object.fromEntries(mocks.filter((mock) => mock.max_hit_count !== void 0).map((mock) => [mock.label, mock.max_hit_count ?? null])),
11639
- response_hits_by_label: networkMockResponseHitsByLabel(events),
11640
- failed
11641
- },
11642
- message: failed.length ? `Network mocks failed or hit-count contracts failed for ${failed.length} mock(s).` : void 0
11643
- };
8192
+ return result.body_json_assertions.filter((assertion) => isRecord(assertion) && assertion.ok !== true).map((assertion) => ({
8193
+ label: stringValue3(assertion.label) || stringValue3(assertion.path) || "json assertion",
8194
+ path: stringValue3(assertion.path) || "",
8195
+ ok: false,
8196
+ exists: assertion.exists === true,
8197
+ observed: hasOwn(assertion, "observed") ? toJsonValue(assertion.observed) : void 0,
8198
+ observed_sample: hasOwn(assertion, "observed_sample") ? toJsonValue(assertion.observed_sample) : void 0,
8199
+ observed_length: numberValue2(assertion.observed_length),
8200
+ observed_key_count: numberValue2(assertion.observed_key_count),
8201
+ observed_omitted_count: numberValue2(assertion.observed_omitted_count),
8202
+ observed_type: stringValue3(assertion.observed_type) || "missing",
8203
+ expected_exists: booleanValue2(assertion.expected_exists),
8204
+ equals: hasOwn(assertion, "equals") ? toJsonValue(assertion.equals) : void 0,
8205
+ not_equals: hasOwn(assertion, "not_equals") ? toJsonValue(assertion.not_equals) : void 0,
8206
+ contains: hasOwn(assertion, "contains") ? toJsonValue(assertion.contains) : void 0,
8207
+ type: stringValue3(assertion.type),
8208
+ errors: Array.isArray(assertion.errors) ? assertion.errors.map(String) : void 0
8209
+ }));
11644
8210
  }
11645
- function networkMockResponseHitsByLabel(events) {
11646
- const responseHits = {};
11647
- for (const event of events) {
11648
- if (!event || event.ok === false) continue;
11649
- const label = typeof event.label === "string" ? event.label : "";
11650
- const responseLabel2 = typeof event.response_label === "string" ? event.response_label : "";
11651
- const hasSequenceResponse = typeof event.response_index === "number" || event.sequence_cycle === true || Boolean(responseLabel2 && responseLabel2 !== label);
11652
- if (!label || !responseLabel2 || !hasSequenceResponse) continue;
11653
- responseHits[label] || (responseHits[label] = {});
11654
- responseHits[label][responseLabel2] = (responseHits[label][responseLabel2] || 0) + 1;
8211
+ function linkStatusResultOk(result, check) {
8212
+ const status = numberValue2(result.status);
8213
+ if (!httpStatusIsAllowed(status, check)) return false;
8214
+ if (stringValue3(result.error)) return false;
8215
+ if (result.ok === false) return false;
8216
+ if (!linkStatusContentTypeOk(result, check)) return false;
8217
+ if (check.require_nonzero_bytes) {
8218
+ const observedBytes = linkStatusObservedBytes(result);
8219
+ if (observedBytes === void 0 || observedBytes <= 0) return false;
8220
+ }
8221
+ if (check.min_bytes !== void 0) {
8222
+ const observedBytes = linkStatusObservedBytes(result);
8223
+ if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
11655
8224
  }
11656
- return responseHits;
8225
+ if (httpStatusBodyContainsFailures(result, check).length) return false;
8226
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
8227
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
8228
+ if (httpStatusBodyJsonAssertionFailures(result, check).length) return false;
8229
+ return true;
11657
8230
  }
11658
- function requiredNetworkMockHitCount(mock) {
11659
- if (mock.required === false) return 0;
11660
- if (mock.required_hit_count !== void 0) return mock.required_hit_count;
11661
- if (Array.isArray(mock.responses) && mock.responses.length) return mock.responses.length;
11662
- return 1;
8231
+ function responseHeader(response, name) {
8232
+ const getter = response.headers?.get;
8233
+ if (typeof getter !== "function") return null;
8234
+ return getter.call(response.headers, name);
11663
8235
  }
11664
- function profileStatusFromEvidence(profile, evidence, checks) {
11665
- if (!evidence) return "proof_insufficient";
11666
- const viewports = evidence.viewports || [];
11667
- const expectedViewportCount = profile.target.viewports?.length || 0;
11668
- if (!viewports.length || !checks.length) return "proof_insufficient";
11669
- if (viewports.some((viewport) => viewport.navigation_error)) return "environment_blocked";
11670
- if (expectedViewportCount && viewports.length < expectedViewportCount) return "proof_insufficient";
11671
- if (checks.some((check) => check.status === "proof_insufficient")) return "proof_insufficient";
11672
- if (checks.some((check) => check.status === "needs_human_review")) return "needs_human_review";
11673
- if (checks.some((check) => check.status === "failed")) return "product_regression";
11674
- return "passed";
8236
+ function responseContentLength(response) {
8237
+ const value = responseHeader(response, "content-length");
8238
+ return value && /^\d+$/.test(value) ? Number(value) : null;
11675
8239
  }
11676
- var RIDDLE_PROOF_HOSTED_PROFILE_ARTIFACTS = [
11677
- "screenshot",
11678
- "console",
11679
- "dom_summary",
11680
- "proof_json"
11681
- ];
11682
- function preflightRiddleProofProfileRunnerArtifacts(profile, runner) {
11683
- const requested = uniqueNonEmptyStrings(profile.artifacts || []);
11684
- if (runner !== "riddle") {
8240
+ async function responseBodyText(response) {
8241
+ if (typeof response.arrayBuffer === "function") {
8242
+ const buffer = await response.arrayBuffer();
11685
8243
  return {
11686
- ok: true,
11687
- runner,
11688
- requested,
11689
- local_only: [],
11690
- hosted_artifacts: [...RIDDLE_PROOF_HOSTED_PROFILE_ARTIFACTS]
8244
+ text: new TextDecoder().decode(buffer),
8245
+ bytes: buffer.byteLength
11691
8246
  };
11692
8247
  }
11693
- const localOnly = requested.filter(profileArtifactIsHostedLocalOnly);
11694
- const subject = localOnly.join(", ");
11695
- const message = localOnly.length ? `${subject} ${localOnly.length === 1 ? "is" : "are"} local-runner output; hosted runs produce proof_json, console, dom_summary, and screenshots. Remove ${localOnly.length === 1 ? "it" : "them"} from profile.artifacts for --runner riddle.` : void 0;
11696
- return {
11697
- ok: localOnly.length === 0,
11698
- runner,
11699
- requested,
11700
- local_only: localOnly,
11701
- hosted_artifacts: [...RIDDLE_PROOF_HOSTED_PROFILE_ARTIFACTS],
11702
- message
11703
- };
11704
- }
11705
- function assessRiddleProofProfileArtifactCompleteness(profile, result, artifacts) {
11706
- const required = profileRequiredArtifactKeys(profile, result);
11707
- const missing = required.filter((key) => !profileArtifactKeyPresent(key, artifacts));
11708
- return {
11709
- ok: missing.length === 0,
11710
- required,
11711
- observed: artifacts.map((artifact) => profileArtifactObservedLabel(artifact)).filter(Boolean),
11712
- missing
11713
- };
11714
- }
11715
- function applyRiddleProofProfileArtifactCompleteness(profile, result, artifacts) {
11716
- if (artifacts === void 0) return result;
11717
- const completeness = assessRiddleProofProfileArtifactCompleteness(profile, result, artifacts);
11718
- if (result.status !== "passed" || completeness.ok) {
8248
+ if (typeof response.text === "function") {
8249
+ const text = await response.text();
11719
8250
  return {
11720
- ...result,
11721
- artifacts: {
11722
- ...result.artifacts,
11723
- riddle_artifacts: artifacts
11724
- }
8251
+ text,
8252
+ bytes: new TextEncoder().encode(text).byteLength
11725
8253
  };
11726
8254
  }
11727
- const missing = completeness.missing.join(", ");
11728
- const message = `Missing required profile artifact(s): ${missing}`;
11729
- const warnings = [...result.warnings || [], message];
11730
- return {
11731
- ...result,
11732
- status: "proof_insufficient",
11733
- artifacts: {
11734
- ...result.artifacts,
11735
- riddle_artifacts: artifacts
11736
- },
11737
- summary: `${profile.name} did not produce required profile artifact(s): ${missing}.`,
11738
- warnings,
11739
- error: message
11740
- };
8255
+ return { text: "", bytes: null };
11741
8256
  }
11742
- function profileRequiredArtifactKeys(profile, result) {
11743
- const keys = [];
11744
- for (const artifact of profile.artifacts || []) {
11745
- const role = normalizeProfileArtifactRole(artifact);
11746
- if (!role) continue;
11747
- if (role === "screenshot") {
11748
- const screenshots = result.artifacts.screenshots || [];
11749
- if (screenshots.length) {
11750
- keys.push(...screenshots.map((label) => `screenshot:${label}`));
11751
- } else {
11752
- keys.push("screenshot");
11753
- }
11754
- continue;
8257
+ function httpStatusRequestBody(check) {
8258
+ const headers = isRecord(check.headers) ? Object.fromEntries(Object.entries(check.headers).map(([key, value]) => [key, String(value)]).filter(([key]) => key.trim())) : {};
8259
+ if (check.body_json !== void 0) {
8260
+ if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
8261
+ headers["content-type"] = "application/json";
11755
8262
  }
11756
- keys.push(role);
11757
- }
11758
- return uniqueNonEmptyStrings(keys);
11759
- }
11760
- function normalizeProfileArtifactRole(input) {
11761
- const normalized = input.trim().toLowerCase().replace(/[\s-]+/g, "_");
11762
- if (!normalized) return "";
11763
- if (normalized === "screenshots") return "screenshot";
11764
- if (normalized === "console" || normalized === "console_json" || normalized === "console.json") return "console.json";
11765
- if (normalized === "dom_summary" || normalized === "dom_summary_json" || normalized === "dom-summary.json") return "dom-summary.json";
11766
- if (normalized === "proof" || normalized === "proof_json" || normalized === "proof.json") return "proof.json";
11767
- return normalizeRiddleProfileArtifactName(input.trim()).toLowerCase();
11768
- }
11769
- function profileArtifactIsHostedLocalOnly(input) {
11770
- const raw = input.trim().toLowerCase();
11771
- const normalized = raw.replace(/[\s-]+/g, "_");
11772
- const role = normalizeProfileArtifactRole(input);
11773
- return [
11774
- raw,
11775
- normalized,
11776
- role
11777
- ].some((value) => value === "artifact_manifest" || value === "artifact_manifest.json" || value === "artifact-manifest" || value === "artifact-manifest.json");
11778
- }
11779
- function profileArtifactKeyPresent(key, artifacts) {
11780
- if (key.startsWith("screenshot:")) {
11781
- const label = key.slice("screenshot:".length);
11782
- return artifacts.some((artifact) => profileArtifactRefIsScreenshot(artifact) && profileArtifactRefText(artifact).includes(label.toLowerCase()));
11783
- }
11784
- if (key === "screenshot") {
11785
- return artifacts.some((artifact) => profileArtifactRefIsScreenshot(artifact));
11786
- }
11787
- return artifacts.some((artifact) => {
11788
- const name = normalizeRiddleProfileArtifactName(artifact.name || artifactNameFromPath(artifact.url || artifact.path) || "").toLowerCase();
11789
- return name === key || profileArtifactRefText(artifact).includes(key);
11790
- });
11791
- }
11792
- function profileArtifactRefIsScreenshot(artifact) {
11793
- const kind = (artifact.kind || "").toLowerCase();
11794
- const contentType = (artifact.content_type || "").toLowerCase();
11795
- const text = profileArtifactRefText(artifact);
11796
- return kind === "screenshot" || contentType.startsWith("image/") || /\.(png|jpe?g|webp)(?:$|[?#])/i.test(text) || /\.(png|jpe?g|webp)$/i.test(text);
11797
- }
11798
- function profileArtifactRefText(artifact) {
11799
- return [
11800
- artifact.name,
11801
- artifact.url,
11802
- artifact.path,
11803
- artifact.kind,
11804
- artifact.content_type
11805
- ].filter(Boolean).join(" ").toLowerCase();
11806
- }
11807
- function profileArtifactObservedLabel(artifact) {
11808
- return artifact.name || artifact.url || artifact.path || artifact.kind || "";
11809
- }
11810
- function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
11811
- const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
11812
- const warnings = collectRiddleProofProfileWarnings(profile);
11813
- const checks = evidence ? [
11814
- assessNetworkMocksFromEvidence(profile, evidence),
11815
- assessSetupActionsFromEvidence(profile, evidence),
11816
- ...profile.checks.map((check) => assessCheckFromEvidence(check, evidence))
11817
- ].filter((check) => Boolean(check)) : [];
11818
- const status = profileStatusFromEvidence(profile, evidence, checks);
11819
- const firstViewport = evidence?.viewports?.[0];
11820
- const screenshots = profileScreenshotLabels(evidence?.viewports);
11821
- const canonicalScreenshots = profileFinalScreenshotLabels(evidence?.viewports);
11822
- const setupScreenshots = profileAllSetupScreenshotLabels(evidence?.viewports);
11823
- const result = {
11824
- version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
11825
- profile_name: profile.name,
11826
- runner: options.runner || "riddle",
11827
- status,
11828
- baseline_policy: profile.baseline_policy,
11829
- route: routeForViewport(firstViewport, evidence?.target_url),
11830
- artifacts: {
11831
- screenshots,
11832
- canonical_screenshots: canonicalScreenshots,
11833
- setup_screenshots: setupScreenshots,
11834
- console: "console.json",
11835
- proof_json: "proof.json",
11836
- dom_summary: "dom-summary.json",
11837
- riddle_artifacts: options.artifacts
11838
- },
11839
- checks,
11840
- summary: summarizeRiddleProofProfileResult({ profile_name: profile.name, status, checks, viewports: evidence?.viewports || [] }),
11841
- captured_at: capturedAt,
11842
- metadata: profile.metadata,
11843
- warnings: warnings.length ? warnings : void 0,
11844
- evidence,
11845
- riddle: options.riddle
11846
- };
11847
- return applyRiddleProofProfileArtifactCompleteness(profile, result, options.artifacts);
11848
- }
11849
- function summarizeRiddleProofProfileResult(input) {
11850
- const passedChecks = input.checks.filter((check) => check.status === "passed").length;
11851
- const failedChecks = input.checks.filter((check) => check.status === "failed").length;
11852
- const viewportNames = input.viewports.map((viewport) => viewport.name).join(", ");
11853
- if (input.status === "passed") {
11854
- return `${input.profile_name} passed ${passedChecks} check(s) across ${input.viewports.length} viewport(s)${viewportNames ? ` (${viewportNames})` : ""}.`;
11855
- }
11856
- if (input.status === "product_regression") {
11857
- return `${input.profile_name} failed ${failedChecks} product invariant(s) across ${input.viewports.length} viewport(s).`;
11858
- }
11859
- if (input.status === "environment_blocked") {
11860
- return `${input.profile_name} could not collect reliable evidence because navigation or the browser environment was blocked.`;
11861
- }
11862
- if (input.status === "proof_insufficient") {
11863
- return `${input.profile_name} did not produce enough evidence for a profile judgment.`;
11864
- }
11865
- if (input.status === "configuration_error") {
11866
- return `${input.profile_name} has a profile configuration error.`;
8263
+ return { headers, body: JSON.stringify(check.body_json) };
11867
8264
  }
11868
- return `${input.profile_name} collected artifacts but needs human review.`;
11869
- }
11870
- function profileStatusExitCode(profile, status) {
11871
- if (status === "passed") return 0;
11872
- return profile.failure_policy[status] === "neutral" ? 0 : 1;
11873
- }
11874
- function createRiddleProofProfileConfigurationError(profileOrName, error, runner = "riddle", options = {}) {
11875
- const profile = typeof profileOrName === "string" ? void 0 : profileOrName;
11876
- const name = profile ? profile.name : String(profileOrName);
11877
- const message = error instanceof Error ? error.message : String(error);
11878
- let requested = "";
11879
- if (profile) {
11880
- try {
11881
- requested = resolveRiddleProofProfileTargetUrl(profile);
11882
- } catch {
11883
- requested = "";
11884
- }
8265
+ if (typeof check.body === "string") {
8266
+ return { headers, body: check.body };
11885
8267
  }
11886
- return {
11887
- version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
11888
- profile_name: name,
11889
- runner,
11890
- status: "configuration_error",
11891
- baseline_policy: profile?.baseline_policy || "invariant_only",
11892
- route: { requested, observed: "", matched: false, error: message },
11893
- artifacts: { screenshots: [], proof_json: "proof.json" },
11894
- checks: [],
11895
- summary: `${name} has a profile configuration error.`,
11896
- captured_at: (/* @__PURE__ */ new Date()).toISOString(),
11897
- metadata: profile?.metadata,
11898
- warnings: options.warnings?.length ? options.warnings : void 0,
11899
- configuration_blocker: options.configurationBlocker,
11900
- error: message
11901
- };
8268
+ return { headers };
11902
8269
  }
11903
- function createRiddleProofProfileEnvironmentBlockedResult(input) {
11904
- const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "Riddle runner did not complete successfully.";
11905
- const environmentBlocker = input.environmentBlocker || extractRiddleRunnerBlocker(message);
11906
- const warnings = collectRiddleProofProfileEnvironmentBlockedWarnings(input.profile, environmentBlocker);
11907
- return {
11908
- version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
11909
- profile_name: input.profile.name,
11910
- runner: input.runner || "riddle",
11911
- status: "environment_blocked",
11912
- baseline_policy: input.profile.baseline_policy,
11913
- route: { requested: resolveRiddleProofProfileTargetUrl(input.profile), observed: "", matched: false, error: message },
11914
- artifacts: { screenshots: [], proof_json: "proof.json", riddle_artifacts: input.artifacts },
11915
- checks: [],
11916
- summary: summarizeEnvironmentBlockedRunner(input.profile.name, environmentBlocker),
11917
- captured_at: (/* @__PURE__ */ new Date()).toISOString(),
11918
- metadata: input.profile.metadata,
11919
- warnings: warnings.length ? warnings : void 0,
11920
- riddle: input.riddle,
11921
- environment_blocker: environmentBlocker,
11922
- error: message
8270
+ async function preflightHttpStatusCheck(check, index, targetUrl, fetchImpl) {
8271
+ const url = httpStatusRequestUrl(check, targetUrl);
8272
+ const method = httpStatusMethod(check);
8273
+ const request = httpStatusRequestBody(check);
8274
+ const init = {
8275
+ method,
8276
+ redirect: "follow",
8277
+ cache: "no-store",
8278
+ headers: request.headers
11923
8279
  };
11924
- }
11925
- function extractRiddleRunnerBlocker(message) {
11926
- const apiError = message.match(/^Riddle API\s+(\S+)\s+failed HTTP\s+(\d+):\s+(\{[\s\S]*\})$/);
11927
- if (!apiError) return void 0;
11928
- const details = {
11929
- source: "riddle_api",
11930
- endpoint: apiError[1],
11931
- http_status: Number(apiError[2])
8280
+ if (request.body !== void 0 && method !== "GET" && method !== "HEAD") {
8281
+ init.body = request.body;
8282
+ }
8283
+ const result = {
8284
+ status: null,
8285
+ content_type: null,
8286
+ content_length: null,
8287
+ bytes: null
11932
8288
  };
11933
- let payload;
8289
+ let error = null;
8290
+ let statusText = "";
11934
8291
  try {
11935
- const parsed = JSON.parse(apiError[3]);
11936
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
11937
- payload = parsed;
8292
+ const response = await fetchImpl(url, init);
8293
+ result.status = typeof response.status === "number" && Number.isFinite(response.status) ? response.status : null;
8294
+ statusText = typeof response.statusText === "string" ? response.statusText : "";
8295
+ result.content_type = responseHeader(response, "content-type");
8296
+ result.content_length = responseContentLength(response);
8297
+ const shouldReadBody = check.require_nonzero_bytes === true || typeof check.min_bytes === "number" || Boolean(check.body_contains?.length) || Boolean(check.body_not_contains?.length) || Boolean(check.body_not_patterns?.length) || Boolean(check.body_json_assertions?.length);
8298
+ if (shouldReadBody && method !== "HEAD") {
8299
+ const body = await responseBodyText(response);
8300
+ result.bytes = body.bytes;
8301
+ if (check.body_contains?.length) {
8302
+ result.body_contains = Object.fromEntries(check.body_contains.filter(Boolean).map((snippet) => [snippet, body.text.includes(snippet)]));
8303
+ }
8304
+ if (check.body_not_contains?.length) {
8305
+ result.body_not_contains = Object.fromEntries(check.body_not_contains.filter(Boolean).map((snippet) => [snippet, body.text.includes(snippet)]));
8306
+ }
8307
+ if (check.body_not_patterns?.length) {
8308
+ result.body_not_patterns = Object.fromEntries(check.body_not_patterns.filter(Boolean).map((pattern) => [pattern, new RegExp(pattern).test(body.text)]));
8309
+ }
8310
+ if (check.body_json_assertions?.length) {
8311
+ result.body_json_assertions = evaluateHttpStatusBodyJsonAssertions(body.text, check.body_json_assertions);
8312
+ }
11938
8313
  }
11939
- } catch {
11940
- payload = void 0;
8314
+ } catch (caught) {
8315
+ error = String(caught instanceof Error ? caught.message : caught).slice(0, 500);
8316
+ result.error = error;
11941
8317
  }
11942
- const copyScalar = (key) => {
11943
- const value = payload?.[key];
11944
- if (typeof value === "string" || typeof value === "boolean") details[key] = value;
11945
- if (typeof value === "number" && Number.isFinite(value)) details[key] = value;
8318
+ const bodyContainsMissing = httpStatusBodyContainsFailures(result, check);
8319
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(result, check);
8320
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(result, check);
8321
+ const bodyJsonAssertionsFailed = httpStatusBodyJsonAssertionFailures(result, check);
8322
+ const ok = !error && linkStatusResultOk(result, check);
8323
+ return {
8324
+ index,
8325
+ label: checkLabel(check) || `checks[${index}]`,
8326
+ url,
8327
+ method,
8328
+ ok,
8329
+ status: numberValue2(result.status) ?? null,
8330
+ status_text: statusText,
8331
+ error,
8332
+ content_type: stringValue3(result.content_type) ?? null,
8333
+ content_length: numberValue2(result.content_length) ?? null,
8334
+ bytes: numberValue2(result.bytes) ?? null,
8335
+ body_contains: isRecord(result.body_contains) ? Object.fromEntries(Object.entries(result.body_contains).map(([key, value]) => [key, value === true])) : null,
8336
+ body_contains_missing: bodyContainsMissing,
8337
+ body_not_contains: isRecord(result.body_not_contains) ? Object.fromEntries(Object.entries(result.body_not_contains).map(([key, value]) => [key, value === true])) : null,
8338
+ body_not_contains_found: bodyNotContainsFound,
8339
+ body_not_patterns: isRecord(result.body_not_patterns) ? Object.fromEntries(Object.entries(result.body_not_patterns).map(([key, value]) => [key, value === true])) : null,
8340
+ body_not_patterns_found: bodyNotPatternsFound,
8341
+ body_json_assertions: Array.isArray(result.body_json_assertions) ? result.body_json_assertions : null,
8342
+ body_json_assertions_failed: bodyJsonAssertionsFailed
11946
8343
  };
11947
- for (const key of ["error", "required_seconds", "available_seconds", "deficit_seconds", "minimum_purchase_dollars"]) {
11948
- copyScalar(key);
11949
- }
11950
- const warnings = payload?.warnings;
11951
- if (Array.isArray(warnings)) {
11952
- const warningStrings = warnings.filter((warning) => typeof warning === "string");
11953
- if (warningStrings.length) details.warnings = warningStrings;
11954
- }
11955
- const httpStatus = typeof details.http_status === "number" ? details.http_status : void 0;
11956
- const errorText = typeof details.error === "string" ? details.error.toLowerCase() : "";
11957
- if (httpStatus === 402 && errorText.includes("balance")) {
11958
- details.reason = "insufficient_balance";
11959
- }
11960
- return details;
11961
8344
  }
11962
- function summarizeEnvironmentBlockedRunner(profileName, blocker) {
11963
- if (blocker?.reason === "insufficient_balance") {
11964
- const required = typeof blocker.required_seconds === "number" ? blocker.required_seconds : void 0;
11965
- const available = typeof blocker.available_seconds === "number" ? blocker.available_seconds : void 0;
11966
- const deficit = typeof blocker.deficit_seconds === "number" ? blocker.deficit_seconds : void 0;
11967
- const parts = [
11968
- required === void 0 ? "" : `required ${required}s`,
11969
- available === void 0 ? "" : `available ${available}s`,
11970
- deficit === void 0 ? "" : `deficit ${deficit}s`
11971
- ].filter(Boolean);
11972
- return `${profileName} could not start because Riddle balance was insufficient${parts.length ? ` (${parts.join(", ")})` : ""}.`;
11973
- }
11974
- if (blocker?.source === "riddle_api") {
11975
- const endpoint = typeof blocker.endpoint === "string" ? blocker.endpoint : "Riddle API";
11976
- const status = typeof blocker.http_status === "number" ? blocker.http_status : void 0;
11977
- return `${profileName} could not start because ${endpoint} returned${status === void 0 ? "" : ` HTTP ${status}`}.`;
8345
+ async function preflightRiddleProofProfileHttpStatusChecks(profile, options = {}) {
8346
+ const targetUrl = options.target_url || (0, import_profile.resolveRiddleProofProfileTargetUrl)(profile);
8347
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
8348
+ if (typeof fetchImpl !== "function") {
8349
+ throw new Error("profile http_status preflight requires fetch support or options.fetchImpl.");
11978
8350
  }
11979
- return `${profileName} could not collect reliable evidence because the runner was blocked.`;
11980
- }
11981
- function createRiddleProofProfileInsufficientResult(input) {
11982
- const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "No proof.json profile result artifact was found.";
11983
- const warnings = collectRiddleProofProfileWarnings(input.profile);
8351
+ const httpStatusChecks = profile.checks.map((check, index) => ({ check, index })).filter((item) => item.check.type === "http_status");
8352
+ const checks = await Promise.all(httpStatusChecks.map((item) => preflightHttpStatusCheck(item.check, item.index, targetUrl, fetchImpl)));
8353
+ const failed = checks.filter((check) => !check.ok).length;
11984
8354
  return {
11985
- version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
11986
- profile_name: input.profile.name,
11987
- runner: input.runner || "riddle",
11988
- status: "proof_insufficient",
11989
- baseline_policy: input.profile.baseline_policy,
11990
- route: { requested: resolveRiddleProofProfileTargetUrl(input.profile), observed: "", matched: false, error: message },
11991
- artifacts: { screenshots: [], proof_json: "proof.json", riddle_artifacts: input.artifacts },
11992
- checks: [],
11993
- summary: `${input.profile.name} did not produce enough evidence for a profile judgment.`,
11994
- captured_at: (/* @__PURE__ */ new Date()).toISOString(),
11995
- metadata: input.profile.metadata,
11996
- warnings: warnings.length ? warnings : void 0,
11997
- riddle: input.riddle,
11998
- error: message
8355
+ version: "riddle-proof.profile-http-status-preflight.v1",
8356
+ ok: failed === 0,
8357
+ profile_name: profile.name,
8358
+ target_url: targetUrl,
8359
+ checked: checks.length,
8360
+ failed,
8361
+ checks,
8362
+ summary: failed === 0 ? `${profile.name} http_status preflight passed ${checks.length} check(s).` : `${profile.name} http_status preflight failed ${failed} of ${checks.length} check(s).`
11999
8363
  };
12000
8364
  }
8365
+ function previewMountPrefix(pathname) {
8366
+ const value = pathname || "/";
8367
+ const apiPreview = value.match(/^(\/s\/[^/]+)(?:\/|$)/);
8368
+ if (apiPreview) return apiPreview[1];
8369
+ const internalPreview = value.match(/^(\/preview\/[^/]+\/[^/]+)(?:\/|$)/);
8370
+ return internalPreview ? internalPreview[1] : "";
8371
+ }
8372
+ function joinMountedRoutePath(mountPrefix, routePath) {
8373
+ const route = routePath.startsWith("/") ? routePath : `/${routePath}`;
8374
+ if (!mountPrefix) return route;
8375
+ if (route === mountPrefix || route.startsWith(`${mountPrefix}/`)) return route;
8376
+ return `${mountPrefix}${route}`;
8377
+ }
12001
8378
  function runtimeScriptAssessmentSource(includeOrderedTrace = false) {
12002
8379
  const orderedTraceSource = includeOrderedTrace ? String.raw`
12003
- const assessRiddleProofOrderedTrace = ${assessRiddleProofOrderedTrace.toString()};
12004
- const assessRiddleProofOrderedTraceSetupResults = ${assessRiddleProofOrderedTraceSetupResults.toString()};` : "";
8380
+ const assessRiddleProofOrderedTrace = ${import_profile.assessRiddleProofOrderedTrace.toString()};
8381
+ const assessRiddleProofOrderedTraceSetupResults = ${import_profile.assessRiddleProofOrderedTraceSetupResults.toString()};` : "";
12005
8382
  return String.raw`
12006
8383
  ${orderedTraceSource}
12007
8384
  function normalizeRoutePath(path) {
@@ -14097,9 +10474,9 @@ function assessProfile(profile, evidence) {
14097
10474
  `;
14098
10475
  }
14099
10476
  function buildRiddleProofProfileScript(profile) {
14100
- const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
14101
- const slug = slugifyRiddleProofProfileName(profile.name);
14102
- const profileWarnings = collectRiddleProofProfileWarnings(profile);
10477
+ const targetUrl = (0, import_profile.resolveRiddleProofProfileTargetUrl)(profile);
10478
+ const slug = (0, import_profile.slugifyRiddleProofProfileName)(profile.name);
10479
+ const profileWarnings = (0, import_profile.collectRiddleProofProfileWarnings)(profile);
14103
10480
  const serializableProfile = JSON.stringify(profile);
14104
10481
  const serializableProfileWarnings = JSON.stringify(profileWarnings);
14105
10482
  const serializableTargetUrl = JSON.stringify(targetUrl);
@@ -17751,97 +14128,6 @@ for (const viewport of profile.target.viewports || []) {
17751
14128
  return result;
17752
14129
  `.trim();
17753
14130
  }
17754
- function collectRiddleProfileArtifactRefs(input) {
17755
- const refs = [];
17756
- const indexes = /* @__PURE__ */ new Map();
17757
- const priorities = /* @__PURE__ */ new Map();
17758
- function add(item, source) {
17759
- if (!isRecord(item)) return;
17760
- const url = stringValue3(item.url);
17761
- const path6 = stringValue3(item.path);
17762
- const rawName = stringValue3(item.name) || stringValue3(item.filename) || artifactNameFromPath(url || path6) || "";
17763
- const name = normalizeRiddleProfileArtifactName(rawName);
17764
- if (!name && !url && !path6) return;
17765
- const key = profileArtifactDedupeKey(name, url || path6 || "");
17766
- const priority = profileArtifactPriority(rawName, name);
17767
- const ref = {
17768
- name,
17769
- url,
17770
- path: path6,
17771
- kind: stringValue3(item.kind) || stringValue3(item.type),
17772
- content_type: stringValue3(item.content_type) || stringValue3(item.contentType),
17773
- source
17774
- };
17775
- const existingIndex = indexes.get(key);
17776
- if (existingIndex !== void 0) {
17777
- if (priority > (priorities.get(key) || 0)) {
17778
- refs[existingIndex] = ref;
17779
- priorities.set(key, priority);
17780
- }
17781
- return;
17782
- }
17783
- indexes.set(key, refs.length);
17784
- priorities.set(key, priority);
17785
- refs.push(ref);
17786
- }
17787
- function visit(value, source) {
17788
- if (Array.isArray(value)) {
17789
- for (const item of value) add(item, source);
17790
- return;
17791
- }
17792
- if (!isRecord(value)) return;
17793
- for (const key of ["artifacts", "outputs", "screenshots", "files"]) {
17794
- if (Array.isArray(value[key])) visit(value[key], key);
17795
- }
17796
- }
17797
- visit(input, "artifacts");
17798
- return refs;
17799
- }
17800
- function normalizeRiddleProfileArtifactName(name) {
17801
- return name.replace(/\.json\.json$/i, ".json");
17802
- }
17803
- function profileArtifactDedupeKey(name, location) {
17804
- if (PROFILE_SINGLETON_ARTIFACT_NAMES.has(name.toLowerCase())) return name.toLowerCase();
17805
- return `${name}:${location}`;
17806
- }
17807
- function profileArtifactPriority(rawName, normalizedName) {
17808
- const lower = normalizedName.toLowerCase();
17809
- if (!PROFILE_SINGLETON_ARTIFACT_NAMES.has(lower)) return 1;
17810
- return /\.json\.json$/i.test(rawName) ? 2 : 1;
17811
- }
17812
- function artifactNameFromPath(value) {
17813
- if (!value) return "";
17814
- try {
17815
- return new URL(value).pathname.split("/").filter(Boolean).pop() || "";
17816
- } catch {
17817
- return value.split(/[\\/]/).filter(Boolean).pop() || "";
17818
- }
17819
- }
17820
- var PROFILE_SINGLETON_ARTIFACT_NAMES = /* @__PURE__ */ new Set([
17821
- "proof.json",
17822
- "console.json",
17823
- "dom-summary.json"
17824
- ]);
17825
- function extractRiddleProofProfileResult(input) {
17826
- if (!isRecord(input)) return void 0;
17827
- if (input.version === RIDDLE_PROOF_PROFILE_RESULT_VERSION) return input;
17828
- const candidates = [
17829
- input.result,
17830
- input.return_value,
17831
- input.value,
17832
- input.profile_result,
17833
- isRecord(input["proof.json"]) ? input["proof.json"] : void 0,
17834
- isRecord(input._proof_json) ? input._proof_json : void 0
17835
- ];
17836
- for (const candidate of candidates) {
17837
- const result = extractRiddleProofProfileResult(candidate);
17838
- if (result) return result;
17839
- }
17840
- if (isRecord(input._artifact_json)) {
17841
- return extractRiddleProofProfileResult(input._artifact_json["proof.json"]);
17842
- }
17843
- return void 0;
17844
- }
17845
14131
 
17846
14132
  // src/receipts.ts
17847
14133
  var receipts_exports = {};
@@ -18396,7 +14682,7 @@ function parseRiddleProofChangeReceipt(value) {
18396
14682
  throw new Error(`Unsupported Change receipt version ${String(value.version || "missing")}.`);
18397
14683
  }
18398
14684
  const receipt = value;
18399
- if (!RIDDLE_PROOF_PROFILE_STATUSES.includes(receipt.status)) {
14685
+ if (!import_profile2.RIDDLE_PROOF_PROFILE_STATUSES.includes(receipt.status)) {
18400
14686
  throw new Error(`Unsupported Change receipt status ${String(receipt.status || "missing")}.`);
18401
14687
  }
18402
14688
  (0, import_receipts.parseRiddleProofObservationReceipt)(receipt.before);
@@ -19389,7 +15675,7 @@ function suggestRiddleProofProfileChecks(input) {
19389
15675
  setup_actions: setupActions.length ? setupActions : void 0
19390
15676
  };
19391
15677
  const profile = {
19392
- version: RIDDLE_PROOF_PROFILE_VERSION,
15678
+ version: import_profile2.RIDDLE_PROOF_PROFILE_VERSION,
19393
15679
  name: suggestionProfileName(input, routeOrDefault, changedFiles),
19394
15680
  target,
19395
15681
  checks,
@@ -20041,7 +16327,7 @@ async function runRegressionPackHostedRiddle(pack, options) {
20041
16327
  };
20042
16328
  try {
20043
16329
  const profile = profileWithSelectedViewportNamesForCli(
20044
- normalizeRiddleProofProfile(rawProfile, { url: baseUrl }),
16330
+ (0, import_profile2.normalizeRiddleProofProfile)(rawProfile, { url: baseUrl }),
20045
16331
  options
20046
16332
  );
20047
16333
  result = await runProfileForCli(profile, caseOptions);
@@ -20624,7 +16910,7 @@ function readProfileRecordForCli(options) {
20624
16910
  return readJsonValue(optionString(options, "profile"), "--profile");
20625
16911
  }
20626
16912
  function normalizeProfileRecordForCli(rawProfile, options) {
20627
- return normalizeRiddleProofProfile(rawProfile, {
16913
+ return (0, import_profile2.normalizeRiddleProofProfile)(rawProfile, {
20628
16914
  url: optionString(options, "url") ?? optionString(options, "baseUrl"),
20629
16915
  route: optionString(options, "route"),
20630
16916
  viewports: parseProfileViewports(optionString(options, "viewports") || optionString(options, "viewport"))
@@ -20684,7 +16970,7 @@ function profilePlainCheckSummary(result) {
20684
16970
  if (!total) return route ? `No browser checks were recorded for ${markdownInlineCode(route, 120)}.` : "No browser checks were recorded.";
20685
16971
  return `${countText}${viewportText}${routeText}; ${outcomes || "no completed checks"}.`;
20686
16972
  }
20687
- function profileArtifactRefIsScreenshot2(artifact) {
16973
+ function profileArtifactRefIsScreenshot(artifact) {
20688
16974
  const kind = cliString(artifact.kind)?.toLowerCase() || "";
20689
16975
  const contentType = cliString(artifact.content_type)?.toLowerCase() || "";
20690
16976
  const text = [artifact.name, artifact.url, artifact.path].map((value) => cliString(value)).filter(Boolean).join(" ");
@@ -20697,7 +16983,7 @@ function profileArtifactDisplay(artifact) {
20697
16983
  }
20698
16984
  function profilePlainArtifactSummary(result) {
20699
16985
  const hostedArtifacts = result.artifacts.riddle_artifacts || [];
20700
- const hostedScreenshot = hostedArtifacts.find(profileArtifactRefIsScreenshot2);
16986
+ const hostedScreenshot = hostedArtifacts.find(profileArtifactRefIsScreenshot);
20701
16987
  if (hostedScreenshot) return `Hosted screenshot ${profileArtifactDisplay(hostedScreenshot)}`;
20702
16988
  const hostedProof = hostedArtifacts.find((artifact) => {
20703
16989
  const name = (artifact.name || artifact.url || artifact.path || "").toLowerCase();
@@ -23123,7 +19409,7 @@ function profileNetworkMockSummaryMarkdown(result) {
23123
19409
  const responseHits = cliRecord(responseHitsByLabel[label]);
23124
19410
  const responseLabels = responseHits ? Object.keys(responseHits).sort() : [];
23125
19411
  if (responseHits && responseLabels.length) {
23126
- const responseParts = responseLabels.slice(0, 8).map((responseLabel2) => `${responseLabel2} ${cliRecordNumber(responseHits, responseLabel2) ?? 0}`);
19412
+ const responseParts = responseLabels.slice(0, 8).map((responseLabel) => `${responseLabel} ${cliRecordNumber(responseHits, responseLabel) ?? 0}`);
23127
19413
  const omitted = responseLabels.length > 8 ? `; ${responseLabels.length - 8} additional response label(s) omitted` : "";
23128
19414
  lines.push(`- ${label} responses: ${responseParts.join("; ")}${omitted}`);
23129
19415
  }
@@ -23502,9 +19788,9 @@ async function readArtifactJson(artifact) {
23502
19788
  }
23503
19789
  async function profileResultFromRiddleArtifacts(profile, artifacts, fallbackInputs) {
23504
19790
  for (const input of fallbackInputs) {
23505
- const result = extractRiddleProofProfileResult(input);
19791
+ const result = (0, import_profile2.extractRiddleProofProfileResult)(input);
23506
19792
  if (result) {
23507
- return applyRiddleProofProfileArtifactCompleteness(
19793
+ return (0, import_profile2.applyRiddleProofProfileArtifactCompleteness)(
23508
19794
  profile,
23509
19795
  withProfileMetadata(profile, result),
23510
19796
  artifacts
@@ -23518,9 +19804,9 @@ async function profileResultFromRiddleArtifacts(profile, artifacts, fallbackInpu
23518
19804
  });
23519
19805
  for (const artifact of proofArtifacts) {
23520
19806
  const parsed = await readArtifactJson(artifact);
23521
- const result = extractRiddleProofProfileResult(parsed);
19807
+ const result = (0, import_profile2.extractRiddleProofProfileResult)(parsed);
23522
19808
  if (result) {
23523
- return applyRiddleProofProfileArtifactCompleteness(
19809
+ return (0, import_profile2.applyRiddleProofProfileArtifactCompleteness)(
23524
19810
  profile,
23525
19811
  withProfileMetadata(profile, result),
23526
19812
  artifacts
@@ -23531,7 +19817,7 @@ async function profileResultFromRiddleArtifacts(profile, artifacts, fallbackInpu
23531
19817
  for (const artifact of evidenceArtifacts) {
23532
19818
  const parsed = await readArtifactJson(artifact);
23533
19819
  if (parsed?.version === "riddle-proof.profile-evidence.v1") {
23534
- return assessRiddleProofProfileEvidence(profile, parsed, { artifacts });
19820
+ return (0, import_profile2.assessRiddleProofProfileEvidence)(profile, parsed, { artifacts });
23535
19821
  }
23536
19822
  }
23537
19823
  return void 0;
@@ -23592,7 +19878,7 @@ async function recoverProfileResultFromRiddleArtifacts(profile, input) {
23592
19878
  } catch {
23593
19879
  artifactPayload = {};
23594
19880
  }
23595
- const artifacts = collectRiddleProfileArtifactRefs(artifactPayload);
19881
+ const artifacts = (0, import_profile2.collectRiddleProfileArtifactRefs)(artifactPayload);
23596
19882
  if (artifacts.length) {
23597
19883
  const artifactStatus = riddleArtifactsPayloadStatus(artifactPayload);
23598
19884
  const terminal = artifactStatus ? (0, import_client.isTerminalRiddleJobStatus)(artifactStatus) : true;
@@ -23613,7 +19899,7 @@ async function recoverProfileResultFromRiddleArtifacts(profile, input) {
23613
19899
  });
23614
19900
  }
23615
19901
  if (terminal) {
23616
- return createRiddleProofProfileInsufficientResult({
19902
+ return (0, import_profile2.createRiddleProofProfileInsufficientResult)({
23617
19903
  profile,
23618
19904
  runner: input.runner,
23619
19905
  error: `Riddle job ${input.jobId} timed out in status ${input.poll.status || "unknown"}, but artifacts were recovered without a proof result.`,
@@ -23918,9 +20204,9 @@ function aggregateSplitViewportEvidence(profile, childRuns) {
23918
20204
  const pageErrors = evidences.flatMap((evidence2) => evidence2.page_errors || []);
23919
20205
  const networkMocks = evidences.flatMap((evidence2) => evidence2.network_mocks || []);
23920
20206
  const evidence = {
23921
- version: RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
20207
+ version: import_profile2.RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
23922
20208
  profile_name: profile.name,
23923
- target_url: resolveRiddleProofProfileTargetUrl(profile),
20209
+ target_url: (0, import_profile2.resolveRiddleProofProfileTargetUrl)(profile),
23924
20210
  baseline_policy: profile.baseline_policy,
23925
20211
  captured_at: latestCapturedAt(evidences),
23926
20212
  viewports,
@@ -24026,7 +20312,7 @@ async function preflightRiddleProfileBalanceForCli(profile, options, input) {
24026
20312
  try {
24027
20313
  balance = await input.client.getBalance();
24028
20314
  } catch (error) {
24029
- return createRiddleProofProfileEnvironmentBlockedResult({
20315
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({
24030
20316
  profile,
24031
20317
  runner: input.runner,
24032
20318
  error,
@@ -24048,7 +20334,7 @@ async function preflightRiddleProfileBalanceForCli(profile, options, input) {
24048
20334
  const reservedSeconds = cliFiniteNumber(balance.reserved_seconds);
24049
20335
  const totalSeconds = cliFiniteNumber(balance.total_seconds);
24050
20336
  const holdsCount = cliFiniteNumber(balance.holds_count);
24051
- return createRiddleProofProfileEnvironmentBlockedResult({
20337
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({
24052
20338
  profile,
24053
20339
  runner: input.runner,
24054
20340
  error: `Riddle balance preflight failed: ${availableSeconds}s available for ${jobCount} intended hosted job(s), minimum ${requiredSeconds}s required.`,
@@ -24073,7 +20359,7 @@ async function preflightRiddleProfileBalanceForCli(profile, options, input) {
24073
20359
  }
24074
20360
  async function runSingleRiddleProfileForCli(profile, options, input) {
24075
20361
  const { client, runner } = input;
24076
- const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
20362
+ const targetUrl = (0, import_profile2.resolveRiddleProofProfileTargetUrl)(profile);
24077
20363
  let created;
24078
20364
  let poll;
24079
20365
  let jobId = "";
@@ -24086,7 +20372,7 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
24086
20372
  url: targetUrl,
24087
20373
  script: buildRiddleProofProfileScript(profile),
24088
20374
  viewport: profile.target.viewports[0],
24089
- timeoutSec: resolveRiddleProofProfileTimeoutSec(
20375
+ timeoutSec: (0, import_profile2.resolveRiddleProofProfileTimeoutSec)(
24090
20376
  profile,
24091
20377
  optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0
24092
20378
  ),
@@ -24094,20 +20380,20 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
24094
20380
  sync: options.sync === true ? true : void 0
24095
20381
  });
24096
20382
  } catch (error) {
24097
- return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error });
20383
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({ profile, runner, error });
24098
20384
  }
24099
20385
  jobId = typeof created.job_id === "string" ? created.job_id : typeof created.id === "string" ? created.id : "";
24100
20386
  if (!jobId) {
24101
- const directResult = extractRiddleProofProfileResult(created);
24102
- const createdArtifacts = collectRiddleProfileArtifactRefs(created);
20387
+ const directResult = (0, import_profile2.extractRiddleProofProfileResult)(created);
20388
+ const createdArtifacts = (0, import_profile2.collectRiddleProfileArtifactRefs)(created);
24103
20389
  return directResult ? withRiddleMetadata(
24104
- applyRiddleProofProfileArtifactCompleteness(
20390
+ (0, import_profile2.applyRiddleProofProfileArtifactCompleteness)(
24105
20391
  profile,
24106
20392
  withProfileMetadata(profile, directResult),
24107
20393
  createdArtifacts
24108
20394
  ),
24109
20395
  { artifacts: createdArtifacts }
24110
- ) : createRiddleProofProfileInsufficientResult({ profile, runner, error: "Riddle run response was missing job_id.", artifacts: createdArtifacts });
20396
+ ) : (0, import_profile2.createRiddleProofProfileInsufficientResult)({ profile, runner, error: "Riddle run response was missing job_id.", artifacts: createdArtifacts });
24111
20397
  }
24112
20398
  writeRiddleJobReceipt(input.outputDir, {
24113
20399
  profile,
@@ -24139,9 +20425,9 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
24139
20425
  break;
24140
20426
  }
24141
20427
  if (!poll) {
24142
- return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error: "Riddle job polling did not produce a result." });
20428
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({ profile, runner, error: "Riddle job polling did not produce a result." });
24143
20429
  }
24144
- const artifacts = collectRiddleProfileArtifactRefs(poll.artifacts);
20430
+ const artifacts = (0, import_profile2.collectRiddleProfileArtifactRefs)(poll.artifacts);
24145
20431
  const retryCount = staleJobIds.length || void 0;
24146
20432
  if (!poll.ok || !poll.terminal) {
24147
20433
  const recoveredResult = await recoverProfileResultFromRiddleArtifacts(profile, {
@@ -24155,7 +20441,7 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
24155
20441
  if (recoveredResult) {
24156
20442
  return withRiddleMetadata(recoveredResult, { retryCount, staleJobIds });
24157
20443
  }
24158
- return createRiddleProofProfileEnvironmentBlockedResult({
20444
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({
24159
20445
  profile,
24160
20446
  runner,
24161
20447
  error: `Riddle job ${jobId} ended with status ${poll.status || "unknown"}.`,
@@ -24169,7 +20455,7 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
24169
20455
  }
24170
20456
  const artifactResult = await profileResultFromRiddleArtifacts(profile, artifacts, [poll.job, poll.artifacts, created]);
24171
20457
  if (!artifactResult) {
24172
- return createRiddleProofProfileInsufficientResult({
20458
+ return (0, import_profile2.createRiddleProofProfileInsufficientResult)({
24173
20459
  profile,
24174
20460
  runner,
24175
20461
  riddle: riddleMetadataFromPoll(jobId, poll),
@@ -24200,7 +20486,7 @@ async function runSplitViewportProfileForCli(profile, options, input) {
24200
20486
  const artifacts = childRuns.flatMap(splitViewportArtifactRefs);
24201
20487
  const blocked = childRuns.filter(({ result: result2 }) => !result2.evidence || result2.status === "environment_blocked" || result2.status === "configuration_error");
24202
20488
  if (blocked.length) {
24203
- return createRiddleProofProfileEnvironmentBlockedResult({
20489
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({
24204
20490
  profile,
24205
20491
  runner: input.runner,
24206
20492
  error: splitViewportBlockedMessage(childRuns),
@@ -24209,7 +20495,7 @@ async function runSplitViewportProfileForCli(profile, options, input) {
24209
20495
  });
24210
20496
  }
24211
20497
  const evidence = aggregateSplitViewportEvidence(profile, childRuns);
24212
- const result = assessRiddleProofProfileEvidence(profile, evidence, {
20498
+ const result = (0, import_profile2.assessRiddleProofProfileEvidence)(profile, evidence, {
24213
20499
  runner: input.runner,
24214
20500
  riddle: splitViewportRiddleMetadata(childRuns),
24215
20501
  artifacts
@@ -24234,7 +20520,7 @@ async function aggregateProfileResultsForCli(profile, options) {
24234
20520
  const artifacts = childRuns.flatMap(splitViewportArtifactRefs);
24235
20521
  const blocked = childRuns.filter(({ result: result2 }) => !result2.evidence || result2.status === "environment_blocked" || result2.status === "configuration_error");
24236
20522
  if (blocked.length) {
24237
- return createRiddleProofProfileEnvironmentBlockedResult({
20523
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({
24238
20524
  profile,
24239
20525
  runner: "riddle",
24240
20526
  error: splitViewportBlockedMessage(childRuns),
@@ -24243,7 +20529,7 @@ async function aggregateProfileResultsForCli(profile, options) {
24243
20529
  });
24244
20530
  }
24245
20531
  const evidence = aggregateSplitViewportEvidence(profile, childRuns);
24246
- const result = assessRiddleProofProfileEvidence(profile, evidence, {
20532
+ const result = (0, import_profile2.assessRiddleProofProfileEvidence)(profile, evidence, {
24247
20533
  runner: "riddle",
24248
20534
  riddle: splitViewportRiddleMetadata(childRuns, "named-viewport-aggregate"),
24249
20535
  artifacts
@@ -24262,14 +20548,14 @@ async function recoverProfileForCli(profile, options) {
24262
20548
  try {
24263
20549
  artifactPayload = await client.getJobArtifacts(jobId);
24264
20550
  } catch (error) {
24265
- return createRiddleProofProfileEnvironmentBlockedResult({
20551
+ return (0, import_profile2.createRiddleProofProfileEnvironmentBlockedResult)({
24266
20552
  profile,
24267
20553
  runner,
24268
20554
  error,
24269
20555
  riddle: { job_id: jobId, terminal: false }
24270
20556
  });
24271
20557
  }
24272
- const artifacts = collectRiddleProfileArtifactRefs(artifactPayload);
20558
+ const artifacts = (0, import_profile2.collectRiddleProfileArtifactRefs)(artifactPayload);
24273
20559
  const artifactStatus = riddleArtifactsPayloadStatus(artifactPayload);
24274
20560
  const terminal = artifactStatus ? (0, import_client.isTerminalRiddleJobStatus)(artifactStatus) : artifacts.length > 0;
24275
20561
  const recovered = await profileResultFromRiddleArtifacts(profile, artifacts, [artifactPayload]);
@@ -24282,7 +20568,7 @@ async function recoverProfileForCli(profile, options) {
24282
20568
  artifactRecovery: true
24283
20569
  });
24284
20570
  }
24285
- return createRiddleProofProfileInsufficientResult({
20571
+ return (0, import_profile2.createRiddleProofProfileInsufficientResult)({
24286
20572
  profile,
24287
20573
  runner,
24288
20574
  error: artifacts.length ? `Riddle job ${jobId} artifacts were recovered without a proof result.` : `Riddle job ${jobId} had no recoverable artifacts.`,
@@ -24300,10 +20586,10 @@ async function runProfileForCli(profile, options) {
24300
20586
  if (runner !== "riddle") {
24301
20587
  throw new Error(`Unsupported --runner ${runner}. The current CLI supports --runner riddle.`);
24302
20588
  }
24303
- const artifactPreflight = preflightRiddleProofProfileRunnerArtifacts(profile, runner);
20589
+ const artifactPreflight = (0, import_profile2.preflightRiddleProofProfileRunnerArtifacts)(profile, runner);
24304
20590
  if (!artifactPreflight.ok) {
24305
20591
  const message = artifactPreflight.message || "Profile artifacts are not supported by the selected runner.";
24306
- return createRiddleProofProfileConfigurationError(profile, message, runner, {
20592
+ return (0, import_profile2.createRiddleProofProfileConfigurationError)(profile, message, runner, {
24307
20593
  warnings: [message],
24308
20594
  configurationBlocker: {
24309
20595
  source: "profile_artifacts",
@@ -24527,7 +20813,7 @@ async function main() {
24527
20813
  `);
24528
20814
  }
24529
20815
  writeRunProfileResult(result, options);
24530
- process.exitCode = profileStatusExitCode(profile, result.status);
20816
+ process.exitCode = (0, import_profile2.profileStatusExitCode)(profile, result.status);
24531
20817
  return;
24532
20818
  }
24533
20819
  if (command === "run-change-proof") {
@@ -24644,7 +20930,7 @@ async function main() {
24644
20930
  if (!candidates.length && !required?.length) {
24645
20931
  throw new Error("--candidates-json or --required-json must provide at least one snippet.");
24646
20932
  }
24647
- const result = deriveRiddleProofArtifactBodyAssertions({
20933
+ const result = (0, import_profile2.deriveRiddleProofArtifactBodyAssertions)({
24648
20934
  artifact_text: artifactText,
24649
20935
  candidates,
24650
20936
  required