@riddledc/riddle-proof 0.7.97 → 0.7.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/profile.cjs CHANGED
@@ -72,6 +72,7 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
72
72
  "frame_no_horizontal_overflow",
73
73
  "text_visible",
74
74
  "text_absent",
75
+ "http_status",
75
76
  "link_status",
76
77
  "artifact_link_status",
77
78
  "route_inventory",
@@ -821,16 +822,27 @@ function normalizeCheck(input, index) {
821
822
  if (type === "route_inventory" && !expectedRoutes?.length) {
822
823
  throw new Error(`checks[${index}] route_inventory requires expected_routes.`);
823
824
  }
825
+ const isHttpStatusCheck = type === "http_status";
824
826
  const isLinkStatusCheck = type === "link_status" || type === "artifact_link_status";
825
- const expectedStatus = isLinkStatusCheck ? normalizeHttpStatus(input.expected_status ?? input.expectedStatus ?? input.status, `checks[${index}] expected_status`) : void 0;
826
- const allowedStatuses = isLinkStatusCheck ? normalizeHttpStatuses(
827
+ const isStatusCheck = isHttpStatusCheck || isLinkStatusCheck;
828
+ const requestUrl = stringFromOwn(input, "url", "endpoint_url", "endpointUrl", "endpoint", "request_url", "requestUrl", "path");
829
+ if (isHttpStatusCheck && !requestUrl) {
830
+ throw new Error(`checks[${index}] http_status requires url.`);
831
+ }
832
+ const method = stringFromOwn(input, "method", "http_method", "httpMethod")?.toUpperCase();
833
+ if (isHttpStatusCheck && method && !/^[A-Z]+$/.test(method)) {
834
+ throw new Error(`checks[${index}] http_status method must contain only letters.`);
835
+ }
836
+ const hasBodyJson = hasOwn(input, "body_json") || hasOwn(input, "bodyJson") || hasOwn(input, "json");
837
+ const expectedStatus = isStatusCheck ? normalizeHttpStatus(input.expected_status ?? input.expectedStatus ?? input.status, `checks[${index}] expected_status`) : void 0;
838
+ const allowedStatuses = isStatusCheck ? normalizeHttpStatuses(
827
839
  input.allowed_statuses ?? input.allowedStatuses ?? input.expected_statuses ?? input.expectedStatuses,
828
840
  `checks[${index}] allowed_statuses`
829
841
  ) : void 0;
830
842
  const maxLinks = isLinkStatusCheck ? normalizePositiveInteger(input.max_links ?? input.maxLinks ?? input.limit, `checks[${index}] max_links`, 500) : void 0;
831
- const minBytes = isLinkStatusCheck ? normalizePositiveInteger(input.min_bytes ?? input.minBytes ?? input.min_response_bytes ?? input.minResponseBytes, `checks[${index}] min_bytes`) : void 0;
832
- const expectedContentType = isLinkStatusCheck ? stringValue(input.expected_content_type) || stringValue(input.expectedContentType) || stringValue(input.content_type) || stringValue(input.contentType) : void 0;
833
- const allowedContentTypes = isLinkStatusCheck ? normalizeStringList(
843
+ const minBytes = isStatusCheck ? normalizePositiveInteger(input.min_bytes ?? input.minBytes ?? input.min_response_bytes ?? input.minResponseBytes, `checks[${index}] min_bytes`) : void 0;
844
+ const expectedContentType = isStatusCheck ? stringValue(input.expected_content_type) || stringValue(input.expectedContentType) || stringValue(input.content_type) || stringValue(input.contentType) : void 0;
845
+ const allowedContentTypes = isStatusCheck ? normalizeStringList(
834
846
  input.allowed_content_types ?? input.allowedContentTypes ?? input.expected_content_types ?? input.expectedContentTypes,
835
847
  `checks[${index}] allowed_content_types`
836
848
  ) ?? (expectedContentType ? [expectedContentType] : void 0) : void 0;
@@ -851,6 +863,11 @@ function normalizeCheck(input, index) {
851
863
  expected_url: expectedUrl,
852
864
  expected_routes: expectedRoutes,
853
865
  selector: stringValue(input.selector),
866
+ url: isHttpStatusCheck ? requestUrl : void 0,
867
+ method: isHttpStatusCheck ? method || "GET" : void 0,
868
+ headers: isHttpStatusCheck ? stringRecord(input.headers) : void 0,
869
+ body: isHttpStatusCheck ? stringValue(input.body) : void 0,
870
+ body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
854
871
  expected_texts: expectedTexts,
855
872
  link_selector: stringValue(input.link_selector) || stringValue(input.linkSelector),
856
873
  source_selector: stringValue(input.source_selector) || stringValue(input.sourceSelector),
@@ -874,7 +891,7 @@ function normalizeCheck(input, index) {
874
891
  max_links: maxLinks,
875
892
  same_origin_only: isLinkStatusCheck ? input.same_origin_only === true || input.sameOriginOnly === true : void 0,
876
893
  dedupe: isLinkStatusCheck ? input.dedupe === false ? false : true : void 0,
877
- require_nonzero_bytes: isLinkStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
894
+ require_nonzero_bytes: isStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
878
895
  min_bytes: minBytes,
879
896
  allowed_content_types: allowedContentTypes,
880
897
  allow_get_fallback: isLinkStatusCheck ? input.allow_get_fallback === false || input.allowGetFallback === false ? false : true : void 0,
@@ -973,14 +990,32 @@ function selectorKey(check) {
973
990
  function linkStatusSelector(check) {
974
991
  return check.selector || check.link_selector || "a[href]";
975
992
  }
976
- function linkStatusAllowedStatuses(check) {
993
+ function httpStatusRequestUrl(check, baseUrl) {
994
+ const rawUrl = check.url || "";
995
+ if (!rawUrl) return "";
996
+ try {
997
+ return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
998
+ } catch {
999
+ return rawUrl;
1000
+ }
1001
+ }
1002
+ function httpStatusMethod(check) {
1003
+ return (check.method || "GET").toUpperCase();
1004
+ }
1005
+ function httpStatusKey(check, baseUrl) {
1006
+ return `${httpStatusMethod(check)} ${httpStatusRequestUrl(check, baseUrl)}`;
1007
+ }
1008
+ function httpStatusAllowedStatuses(check) {
977
1009
  if (check.allowed_statuses?.length) return check.allowed_statuses;
978
1010
  if (check.expected_status !== void 0) return [check.expected_status];
979
1011
  return void 0;
980
1012
  }
981
- function linkStatusIsAllowed(status, check) {
1013
+ function linkStatusAllowedStatuses(check) {
1014
+ return httpStatusAllowedStatuses(check);
1015
+ }
1016
+ function httpStatusIsAllowed(status, check) {
982
1017
  if (status === void 0) return false;
983
- const allowed = linkStatusAllowedStatuses(check);
1018
+ const allowed = httpStatusAllowedStatuses(check);
984
1019
  return allowed?.length ? allowed.includes(status) : status >= 200 && status < 400;
985
1020
  }
986
1021
  function linkStatusObservedBytes(result) {
@@ -1006,7 +1041,7 @@ function linkStatusContentTypeOk(result, check) {
1006
1041
  }
1007
1042
  function linkStatusResultOk(result, check) {
1008
1043
  const status = numberValue(result.status);
1009
- if (!linkStatusIsAllowed(status, check)) return false;
1044
+ if (!httpStatusIsAllowed(status, check)) return false;
1010
1045
  if (stringValue(result.error)) return false;
1011
1046
  if (result.ok === false) return false;
1012
1047
  if (!linkStatusContentTypeOk(result, check)) return false;
@@ -1020,6 +1055,54 @@ function linkStatusResultOk(result, check) {
1020
1055
  }
1021
1056
  return true;
1022
1057
  }
1058
+ function httpStatusEvidenceForCheck(viewport, check) {
1059
+ const evidence = viewport.http_statuses?.[httpStatusKey(check, viewport.url)];
1060
+ return isRecord(evidence) ? evidence : void 0;
1061
+ }
1062
+ function summarizeHttpStatusEvidence(viewport, check) {
1063
+ const key = httpStatusKey(check, viewport.url);
1064
+ const statusEvidence = httpStatusEvidenceForCheck(viewport, check);
1065
+ if (!statusEvidence) {
1066
+ return {
1067
+ viewport: viewport.name,
1068
+ key,
1069
+ url: httpStatusRequestUrl(check, viewport.url),
1070
+ method: httpStatusMethod(check),
1071
+ ok: false,
1072
+ status: null,
1073
+ failures: [{ code: "http_status_evidence_missing" }]
1074
+ };
1075
+ }
1076
+ const failures = [];
1077
+ if (!linkStatusResultOk(statusEvidence, check)) {
1078
+ failures.push({
1079
+ code: "http_status_failed",
1080
+ url: stringValue(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
1081
+ status: numberValue(statusEvidence.status) ?? null,
1082
+ method: stringValue(statusEvidence.method) ?? httpStatusMethod(check),
1083
+ error: stringValue(statusEvidence.error) ?? null,
1084
+ content_type: stringValue(statusEvidence.content_type) ?? null,
1085
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
1086
+ allowed_statuses: httpStatusAllowedStatuses(check) ?? ["2xx", "3xx"],
1087
+ min_bytes: check.min_bytes ?? null,
1088
+ allowed_content_types: check.allowed_content_types ?? null
1089
+ });
1090
+ }
1091
+ return {
1092
+ viewport: viewport.name,
1093
+ key,
1094
+ url: stringValue(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
1095
+ method: stringValue(statusEvidence.method) ?? httpStatusMethod(check),
1096
+ status: numberValue(statusEvidence.status) ?? null,
1097
+ status_text: stringValue(statusEvidence.status_text) ?? null,
1098
+ ok: failures.length === 0,
1099
+ error: stringValue(statusEvidence.error) ?? null,
1100
+ content_type: stringValue(statusEvidence.content_type) ?? null,
1101
+ content_length: numberValue(statusEvidence.content_length) ?? null,
1102
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
1103
+ failures
1104
+ };
1105
+ }
1023
1106
  function linkStatusEvidenceForCheck(viewport, check) {
1024
1107
  const evidence = viewport.link_statuses?.[linkStatusSelector(check)];
1025
1108
  return isRecord(evidence) ? evidence : void 0;
@@ -1610,6 +1693,28 @@ function assessCheckFromEvidence(check, evidence) {
1610
1693
  message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
1611
1694
  };
1612
1695
  }
1696
+ if (check.type === "http_status") {
1697
+ const url = httpStatusRequestUrl(check, evidence.target_url);
1698
+ const method = httpStatusMethod(check);
1699
+ const summaries = viewports.map((viewport) => summarizeHttpStatusEvidence(viewport, check));
1700
+ const failed = summaries.filter((summary) => Array.isArray(summary.failures) && summary.failures.length > 0);
1701
+ return {
1702
+ type: check.type,
1703
+ label: checkLabel(check),
1704
+ status: failed.length ? "failed" : "passed",
1705
+ evidence: {
1706
+ url,
1707
+ method,
1708
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
1709
+ require_nonzero_bytes: check.require_nonzero_bytes === true,
1710
+ min_bytes: check.min_bytes ?? null,
1711
+ allowed_content_types: check.allowed_content_types ?? null,
1712
+ viewports: summaries.map((summary) => toJsonValue(summary)),
1713
+ failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue(summary.viewport) ?? null, failure })) : [])
1714
+ },
1715
+ message: failed.length ? `HTTP status failed in ${failed.length} viewport(s).` : void 0
1716
+ };
1717
+ }
1613
1718
  if (check.type === "link_status" || check.type === "artifact_link_status") {
1614
1719
  const selector = linkStatusSelector(check);
1615
1720
  const summaries = viewports.map((viewport) => summarizeLinkStatusEvidence(viewport, check));
@@ -2219,16 +2324,37 @@ function textOrderMatch(texts, expectedTexts) {
2219
2324
  function linkStatusSelector(check) {
2220
2325
  return check.selector || check.link_selector || "a[href]";
2221
2326
  }
2222
- function linkStatusAllowedStatuses(check) {
2327
+ function httpStatusRequestUrl(check, baseUrl) {
2328
+ const rawUrl = check.url || "";
2329
+ if (!rawUrl) return "";
2330
+ try {
2331
+ return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
2332
+ } catch {
2333
+ return rawUrl;
2334
+ }
2335
+ }
2336
+ function httpStatusMethod(check) {
2337
+ return String(check.method || "GET").toUpperCase();
2338
+ }
2339
+ function httpStatusKey(check, baseUrl) {
2340
+ return httpStatusMethod(check) + " " + httpStatusRequestUrl(check, baseUrl);
2341
+ }
2342
+ function httpStatusAllowedStatuses(check) {
2223
2343
  if (Array.isArray(check.allowed_statuses) && check.allowed_statuses.length) return check.allowed_statuses;
2224
2344
  if (typeof check.expected_status === "number" && Number.isFinite(check.expected_status)) return [check.expected_status];
2225
2345
  return undefined;
2226
2346
  }
2227
- function linkStatusIsAllowed(status, check) {
2347
+ function linkStatusAllowedStatuses(check) {
2348
+ return httpStatusAllowedStatuses(check);
2349
+ }
2350
+ function httpStatusIsAllowed(status, check) {
2228
2351
  if (typeof status !== "number" || !Number.isFinite(status)) return false;
2229
- const allowed = linkStatusAllowedStatuses(check);
2352
+ const allowed = httpStatusAllowedStatuses(check);
2230
2353
  return Array.isArray(allowed) && allowed.length ? allowed.includes(status) : status >= 200 && status < 400;
2231
2354
  }
2355
+ function linkStatusIsAllowed(status, check) {
2356
+ return httpStatusIsAllowed(status, check);
2357
+ }
2232
2358
  function linkStatusObservedBytes(result) {
2233
2359
  const bytes = typeof result.bytes === "number" && Number.isFinite(result.bytes) ? result.bytes : undefined;
2234
2360
  const contentLength = typeof result.content_length === "number" && Number.isFinite(result.content_length) ? result.content_length : undefined;
@@ -2253,7 +2379,7 @@ function linkStatusContentTypeOk(result, check) {
2253
2379
  }
2254
2380
  function linkStatusResultOk(result, check) {
2255
2381
  if (!result || typeof result !== "object" || Array.isArray(result)) return false;
2256
- if (!linkStatusIsAllowed(result.status, check)) return false;
2382
+ if (!httpStatusIsAllowed(result.status, check)) return false;
2257
2383
  if (typeof result.error === "string" && result.error.trim()) return false;
2258
2384
  if (result.ok === false) return false;
2259
2385
  if (!linkStatusContentTypeOk(result, check)) return false;
@@ -2267,6 +2393,50 @@ function linkStatusResultOk(result, check) {
2267
2393
  }
2268
2394
  return true;
2269
2395
  }
2396
+ function summarizeHttpStatusEvidence(viewport, check) {
2397
+ const key = httpStatusKey(check, viewport && viewport.url);
2398
+ const statusEvidence = viewport && viewport.http_statuses && viewport.http_statuses[key];
2399
+ if (!statusEvidence || typeof statusEvidence !== "object" || Array.isArray(statusEvidence)) {
2400
+ return {
2401
+ viewport: viewport && viewport.name,
2402
+ key,
2403
+ url: httpStatusRequestUrl(check, viewport && viewport.url),
2404
+ method: httpStatusMethod(check),
2405
+ ok: false,
2406
+ status: null,
2407
+ failures: [{ code: "http_status_evidence_missing" }],
2408
+ };
2409
+ }
2410
+ const failures = [];
2411
+ if (!linkStatusResultOk(statusEvidence, check)) {
2412
+ failures.push({
2413
+ code: "http_status_failed",
2414
+ url: typeof statusEvidence.url === "string" ? statusEvidence.url : httpStatusRequestUrl(check, viewport && viewport.url),
2415
+ status: typeof statusEvidence.status === "number" && Number.isFinite(statusEvidence.status) ? statusEvidence.status : null,
2416
+ method: typeof statusEvidence.method === "string" ? statusEvidence.method : httpStatusMethod(check),
2417
+ error: typeof statusEvidence.error === "string" ? statusEvidence.error : null,
2418
+ content_type: typeof statusEvidence.content_type === "string" ? statusEvidence.content_type : null,
2419
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
2420
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
2421
+ min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
2422
+ allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
2423
+ });
2424
+ }
2425
+ return {
2426
+ viewport: viewport && viewport.name,
2427
+ key,
2428
+ url: typeof statusEvidence.url === "string" ? statusEvidence.url : httpStatusRequestUrl(check, viewport && viewport.url),
2429
+ method: typeof statusEvidence.method === "string" ? statusEvidence.method : httpStatusMethod(check),
2430
+ status: typeof statusEvidence.status === "number" && Number.isFinite(statusEvidence.status) ? statusEvidence.status : null,
2431
+ status_text: typeof statusEvidence.status_text === "string" ? statusEvidence.status_text : null,
2432
+ ok: failures.length === 0,
2433
+ error: typeof statusEvidence.error === "string" ? statusEvidence.error : null,
2434
+ content_type: typeof statusEvidence.content_type === "string" ? statusEvidence.content_type : null,
2435
+ content_length: typeof statusEvidence.content_length === "number" && Number.isFinite(statusEvidence.content_length) ? statusEvidence.content_length : null,
2436
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
2437
+ failures,
2438
+ };
2439
+ }
2270
2440
  function summarizeLinkStatusEvidence(viewport, check) {
2271
2441
  const selector = linkStatusSelector(check);
2272
2442
  const linkEvidence = viewport && viewport.link_statuses && viewport.link_statuses[selector];
@@ -3022,6 +3192,31 @@ function assessProfile(profile, evidence) {
3022
3192
  });
3023
3193
  continue;
3024
3194
  }
3195
+ if (check.type === "http_status") {
3196
+ const url = httpStatusRequestUrl(check, evidence.target_url);
3197
+ const method = httpStatusMethod(check);
3198
+ const summaries = checkViewports.map((viewport) => summarizeHttpStatusEvidence(viewport, check));
3199
+ const failed = summaries.filter((summary) => Array.isArray(summary.failures) && summary.failures.length > 0);
3200
+ checks.push({
3201
+ type: check.type,
3202
+ label: check.label || check.type,
3203
+ status: failed.length ? "failed" : "passed",
3204
+ evidence: {
3205
+ url,
3206
+ method,
3207
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
3208
+ require_nonzero_bytes: check.require_nonzero_bytes === true,
3209
+ min_bytes: check.min_bytes ?? null,
3210
+ allowed_content_types: check.allowed_content_types ?? null,
3211
+ viewports: summaries,
3212
+ failures: failed.flatMap((summary) => Array.isArray(summary.failures)
3213
+ ? summary.failures.map((failure) => ({ viewport: summary.viewport || null, failure }))
3214
+ : []),
3215
+ },
3216
+ message: failed.length ? "HTTP status failed in " + failed.length + " viewport(s)." : undefined,
3217
+ });
3218
+ continue;
3219
+ }
3025
3220
  if (check.type === "link_status" || check.type === "artifact_link_status") {
3026
3221
  const selector = linkStatusSelector(check);
3027
3222
  const summaries = checkViewports.map((viewport) => summarizeLinkStatusEvidence(viewport, check));
@@ -4219,6 +4414,65 @@ function linkProbeResponseFields(response, method) {
4219
4414
  content_length: contentLength,
4220
4415
  };
4221
4416
  }
4417
+ async function collectHttpStatus(check) {
4418
+ const url = httpStatusRequestUrl(check, page.url() || targetUrl);
4419
+ const method = httpStatusMethod(check);
4420
+ const headers = check.headers && typeof check.headers === "object" && !Array.isArray(check.headers)
4421
+ ? Object.fromEntries(Object.entries(check.headers).map(([key, value]) => [key, String(value)]).filter(([key]) => key.trim()))
4422
+ : {};
4423
+ let body;
4424
+ if (check.body_json !== undefined) {
4425
+ body = JSON.stringify(check.body_json);
4426
+ if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) headers["content-type"] = "application/json";
4427
+ } else if (typeof check.body === "string") {
4428
+ body = check.body;
4429
+ }
4430
+ const options = {
4431
+ method,
4432
+ redirect: "follow",
4433
+ cache: "no-store",
4434
+ headers,
4435
+ };
4436
+ if (body !== undefined && method !== "GET" && method !== "HEAD") options.body = body;
4437
+ const result = {
4438
+ version: "riddle-proof.http-status.v1",
4439
+ url,
4440
+ method,
4441
+ status: null,
4442
+ ok: false,
4443
+ error: null,
4444
+ request_body_bytes: typeof body === "string" ? body.length : 0,
4445
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
4446
+ require_nonzero_bytes: check.require_nonzero_bytes === true,
4447
+ min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
4448
+ allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
4449
+ };
4450
+ try {
4451
+ const response = await fetch(url, options);
4452
+ Object.assign(result, linkProbeResponseFields(response, method));
4453
+ result.url = url;
4454
+ result.status_text = response.statusText || "";
4455
+ const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes));
4456
+ if (shouldReadBody) {
4457
+ try {
4458
+ const buffer = await response.arrayBuffer();
4459
+ result.bytes = buffer.byteLength;
4460
+ } catch (error) {
4461
+ result.error = String(error && error.message ? error.message : error).slice(0, 500);
4462
+ }
4463
+ }
4464
+ result.ok = linkProbeAllowed(result.status, check)
4465
+ && linkProbeContentTypeAllowed(result, check)
4466
+ && (check.require_nonzero_bytes !== true || ((linkProbeObservedBytes(result) || 0) > 0))
4467
+ && (!(typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || ((linkProbeObservedBytes(result) || 0) >= check.min_bytes))
4468
+ && !result.error;
4469
+ return result;
4470
+ } catch (error) {
4471
+ result.error = String(error && error.message ? error.message : error).slice(0, 500);
4472
+ result.ok = false;
4473
+ return result;
4474
+ }
4475
+ }
4222
4476
  async function probeLinkStatus(candidate, check) {
4223
4477
  const requireNonzeroBytes = check.require_nonzero_bytes === true;
4224
4478
  const minBytes = typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? Math.max(1, Math.floor(check.min_bytes)) : null;
@@ -4911,6 +5165,7 @@ async function captureViewport(viewport) {
4911
5165
  const frames = {};
4912
5166
  const text_sequences = {};
4913
5167
  const text_matches = {};
5168
+ const http_statuses = {};
4914
5169
  const link_statuses = {};
4915
5170
  for (const check of profile.checks || []) {
4916
5171
  if (!profileCheckAppliesToViewport(check, viewport)) continue;
@@ -4937,6 +5192,10 @@ async function captureViewport(viewport) {
4937
5192
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
4938
5193
  frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
4939
5194
  }
5195
+ if (check.type === "http_status") {
5196
+ const key = httpStatusKey(check, page.url() || targetUrl);
5197
+ http_statuses[key] = http_statuses[key] || await collectHttpStatus(check);
5198
+ }
4940
5199
  if (check.type === "link_status" || check.type === "artifact_link_status") {
4941
5200
  const selector = linkStatusSelector(check);
4942
5201
  link_statuses[selector] = link_statuses[selector] || await collectLinkStatus(check);
@@ -5004,6 +5263,7 @@ async function captureViewport(viewport) {
5004
5263
  frames,
5005
5264
  text_sequences,
5006
5265
  text_matches,
5266
+ http_statuses,
5007
5267
  link_statuses,
5008
5268
  route_inventory: routeInventory,
5009
5269
  setup_action_results: setupActionResults,
@@ -5053,6 +5313,19 @@ function buildProfileEvidence(currentViewports) {
5053
5313
  ),
5054
5314
  })),
5055
5315
  })),
5316
+ http_status: currentViewports
5317
+ .filter((viewport) => viewport.http_statuses && Object.keys(viewport.http_statuses).length)
5318
+ .map((viewport) => ({
5319
+ viewport: viewport.name,
5320
+ requests: Object.entries(viewport.http_statuses || {}).map(([key, statusSet]) => ({
5321
+ key,
5322
+ url: statusSet && statusSet.url,
5323
+ method: statusSet && statusSet.method,
5324
+ status: statusSet && statusSet.status,
5325
+ ok: statusSet && statusSet.ok === true,
5326
+ error: statusSet && statusSet.error,
5327
+ })),
5328
+ })),
5056
5329
  link_status: currentViewports
5057
5330
  .filter((viewport) => viewport.link_statuses && Object.keys(viewport.link_statuses).length)
5058
5331
  .map((viewport) => ({
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
4
4
  declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
5
5
  declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
6
6
  declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
7
- declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
7
+ declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
8
8
  declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
@@ -114,6 +114,11 @@ interface RiddleProofProfileCheck {
114
114
  expected_url?: string;
115
115
  expected_routes?: RiddleProofProfileRouteInventoryRoute[];
116
116
  selector?: string;
117
+ url?: string;
118
+ method?: string;
119
+ headers?: Record<string, string>;
120
+ body?: string;
121
+ body_json?: JsonValue;
117
122
  expected_texts?: string[];
118
123
  link_selector?: string;
119
124
  source_selector?: string;
@@ -205,6 +210,7 @@ interface RiddleProofProfileViewportEvidence {
205
210
  frames?: Record<string, Record<string, JsonValue>>;
206
211
  text_sequences?: Record<string, Record<string, JsonValue>>;
207
212
  text_matches?: Record<string, boolean>;
213
+ http_statuses?: Record<string, Record<string, JsonValue>>;
208
214
  link_statuses?: Record<string, Record<string, JsonValue>>;
209
215
  route_inventory?: Record<string, JsonValue>;
210
216
  setup_action_results?: Array<Record<string, JsonValue>>;
package/dist/profile.d.ts CHANGED
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
4
4
  declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
5
5
  declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
6
6
  declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
7
- declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
7
+ declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors"];
8
8
  declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
@@ -114,6 +114,11 @@ interface RiddleProofProfileCheck {
114
114
  expected_url?: string;
115
115
  expected_routes?: RiddleProofProfileRouteInventoryRoute[];
116
116
  selector?: string;
117
+ url?: string;
118
+ method?: string;
119
+ headers?: Record<string, string>;
120
+ body?: string;
121
+ body_json?: JsonValue;
117
122
  expected_texts?: string[];
118
123
  link_selector?: string;
119
124
  source_selector?: string;
@@ -205,6 +210,7 @@ interface RiddleProofProfileViewportEvidence {
205
210
  frames?: Record<string, Record<string, JsonValue>>;
206
211
  text_sequences?: Record<string, Record<string, JsonValue>>;
207
212
  text_matches?: Record<string, boolean>;
213
+ http_statuses?: Record<string, Record<string, JsonValue>>;
208
214
  link_statuses?: Record<string, Record<string, JsonValue>>;
209
215
  route_inventory?: Record<string, JsonValue>;
210
216
  setup_action_results?: Array<Record<string, JsonValue>>;
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-G3UY3SHZ.js";
22
+ } from "./chunk-OMTWNL4B.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.97",
3
+ "version": "0.7.98",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",