@riddledc/riddle-proof 0.7.96 → 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/cli.cjs CHANGED
@@ -6922,6 +6922,7 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
6922
6922
  "frame_no_horizontal_overflow",
6923
6923
  "text_visible",
6924
6924
  "text_absent",
6925
+ "http_status",
6925
6926
  "link_status",
6926
6927
  "artifact_link_status",
6927
6928
  "route_inventory",
@@ -7671,16 +7672,27 @@ function normalizeCheck(input, index) {
7671
7672
  if (type === "route_inventory" && !expectedRoutes?.length) {
7672
7673
  throw new Error(`checks[${index}] route_inventory requires expected_routes.`);
7673
7674
  }
7675
+ const isHttpStatusCheck = type === "http_status";
7674
7676
  const isLinkStatusCheck = type === "link_status" || type === "artifact_link_status";
7675
- const expectedStatus = isLinkStatusCheck ? normalizeHttpStatus(input.expected_status ?? input.expectedStatus ?? input.status, `checks[${index}] expected_status`) : void 0;
7676
- const allowedStatuses = isLinkStatusCheck ? normalizeHttpStatuses(
7677
+ const isStatusCheck = isHttpStatusCheck || isLinkStatusCheck;
7678
+ const requestUrl = stringFromOwn(input, "url", "endpoint_url", "endpointUrl", "endpoint", "request_url", "requestUrl", "path");
7679
+ if (isHttpStatusCheck && !requestUrl) {
7680
+ throw new Error(`checks[${index}] http_status requires url.`);
7681
+ }
7682
+ const method = stringFromOwn(input, "method", "http_method", "httpMethod")?.toUpperCase();
7683
+ if (isHttpStatusCheck && method && !/^[A-Z]+$/.test(method)) {
7684
+ throw new Error(`checks[${index}] http_status method must contain only letters.`);
7685
+ }
7686
+ const hasBodyJson = hasOwn(input, "body_json") || hasOwn(input, "bodyJson") || hasOwn(input, "json");
7687
+ const expectedStatus = isStatusCheck ? normalizeHttpStatus(input.expected_status ?? input.expectedStatus ?? input.status, `checks[${index}] expected_status`) : void 0;
7688
+ const allowedStatuses = isStatusCheck ? normalizeHttpStatuses(
7677
7689
  input.allowed_statuses ?? input.allowedStatuses ?? input.expected_statuses ?? input.expectedStatuses,
7678
7690
  `checks[${index}] allowed_statuses`
7679
7691
  ) : void 0;
7680
7692
  const maxLinks = isLinkStatusCheck ? normalizePositiveInteger(input.max_links ?? input.maxLinks ?? input.limit, `checks[${index}] max_links`, 500) : void 0;
7681
- const minBytes = isLinkStatusCheck ? normalizePositiveInteger(input.min_bytes ?? input.minBytes ?? input.min_response_bytes ?? input.minResponseBytes, `checks[${index}] min_bytes`) : void 0;
7682
- const expectedContentType = isLinkStatusCheck ? stringValue2(input.expected_content_type) || stringValue2(input.expectedContentType) || stringValue2(input.content_type) || stringValue2(input.contentType) : void 0;
7683
- const allowedContentTypes = isLinkStatusCheck ? normalizeStringList(
7693
+ const minBytes = isStatusCheck ? normalizePositiveInteger(input.min_bytes ?? input.minBytes ?? input.min_response_bytes ?? input.minResponseBytes, `checks[${index}] min_bytes`) : void 0;
7694
+ const expectedContentType = isStatusCheck ? stringValue2(input.expected_content_type) || stringValue2(input.expectedContentType) || stringValue2(input.content_type) || stringValue2(input.contentType) : void 0;
7695
+ const allowedContentTypes = isStatusCheck ? normalizeStringList(
7684
7696
  input.allowed_content_types ?? input.allowedContentTypes ?? input.expected_content_types ?? input.expectedContentTypes,
7685
7697
  `checks[${index}] allowed_content_types`
7686
7698
  ) ?? (expectedContentType ? [expectedContentType] : void 0) : void 0;
@@ -7701,6 +7713,11 @@ function normalizeCheck(input, index) {
7701
7713
  expected_url: expectedUrl,
7702
7714
  expected_routes: expectedRoutes,
7703
7715
  selector: stringValue2(input.selector),
7716
+ url: isHttpStatusCheck ? requestUrl : void 0,
7717
+ method: isHttpStatusCheck ? method || "GET" : void 0,
7718
+ headers: isHttpStatusCheck ? stringRecord(input.headers) : void 0,
7719
+ body: isHttpStatusCheck ? stringValue2(input.body) : void 0,
7720
+ body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
7704
7721
  expected_texts: expectedTexts,
7705
7722
  link_selector: stringValue2(input.link_selector) || stringValue2(input.linkSelector),
7706
7723
  source_selector: stringValue2(input.source_selector) || stringValue2(input.sourceSelector),
@@ -7724,7 +7741,7 @@ function normalizeCheck(input, index) {
7724
7741
  max_links: maxLinks,
7725
7742
  same_origin_only: isLinkStatusCheck ? input.same_origin_only === true || input.sameOriginOnly === true : void 0,
7726
7743
  dedupe: isLinkStatusCheck ? input.dedupe === false ? false : true : void 0,
7727
- require_nonzero_bytes: isLinkStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
7744
+ require_nonzero_bytes: isStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
7728
7745
  min_bytes: minBytes,
7729
7746
  allowed_content_types: allowedContentTypes,
7730
7747
  allow_get_fallback: isLinkStatusCheck ? input.allow_get_fallback === false || input.allowGetFallback === false ? false : true : void 0,
@@ -7823,14 +7840,32 @@ function selectorKey(check) {
7823
7840
  function linkStatusSelector(check) {
7824
7841
  return check.selector || check.link_selector || "a[href]";
7825
7842
  }
7826
- function linkStatusAllowedStatuses(check) {
7843
+ function httpStatusRequestUrl(check, baseUrl) {
7844
+ const rawUrl = check.url || "";
7845
+ if (!rawUrl) return "";
7846
+ try {
7847
+ return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
7848
+ } catch {
7849
+ return rawUrl;
7850
+ }
7851
+ }
7852
+ function httpStatusMethod(check) {
7853
+ return (check.method || "GET").toUpperCase();
7854
+ }
7855
+ function httpStatusKey(check, baseUrl) {
7856
+ return `${httpStatusMethod(check)} ${httpStatusRequestUrl(check, baseUrl)}`;
7857
+ }
7858
+ function httpStatusAllowedStatuses(check) {
7827
7859
  if (check.allowed_statuses?.length) return check.allowed_statuses;
7828
7860
  if (check.expected_status !== void 0) return [check.expected_status];
7829
7861
  return void 0;
7830
7862
  }
7831
- function linkStatusIsAllowed(status, check) {
7863
+ function linkStatusAllowedStatuses(check) {
7864
+ return httpStatusAllowedStatuses(check);
7865
+ }
7866
+ function httpStatusIsAllowed(status, check) {
7832
7867
  if (status === void 0) return false;
7833
- const allowed = linkStatusAllowedStatuses(check);
7868
+ const allowed = httpStatusAllowedStatuses(check);
7834
7869
  return allowed?.length ? allowed.includes(status) : status >= 200 && status < 400;
7835
7870
  }
7836
7871
  function linkStatusObservedBytes(result) {
@@ -7856,7 +7891,7 @@ function linkStatusContentTypeOk(result, check) {
7856
7891
  }
7857
7892
  function linkStatusResultOk(result, check) {
7858
7893
  const status = numberValue(result.status);
7859
- if (!linkStatusIsAllowed(status, check)) return false;
7894
+ if (!httpStatusIsAllowed(status, check)) return false;
7860
7895
  if (stringValue2(result.error)) return false;
7861
7896
  if (result.ok === false) return false;
7862
7897
  if (!linkStatusContentTypeOk(result, check)) return false;
@@ -7870,6 +7905,54 @@ function linkStatusResultOk(result, check) {
7870
7905
  }
7871
7906
  return true;
7872
7907
  }
7908
+ function httpStatusEvidenceForCheck(viewport, check) {
7909
+ const evidence = viewport.http_statuses?.[httpStatusKey(check, viewport.url)];
7910
+ return isRecord(evidence) ? evidence : void 0;
7911
+ }
7912
+ function summarizeHttpStatusEvidence(viewport, check) {
7913
+ const key = httpStatusKey(check, viewport.url);
7914
+ const statusEvidence = httpStatusEvidenceForCheck(viewport, check);
7915
+ if (!statusEvidence) {
7916
+ return {
7917
+ viewport: viewport.name,
7918
+ key,
7919
+ url: httpStatusRequestUrl(check, viewport.url),
7920
+ method: httpStatusMethod(check),
7921
+ ok: false,
7922
+ status: null,
7923
+ failures: [{ code: "http_status_evidence_missing" }]
7924
+ };
7925
+ }
7926
+ const failures = [];
7927
+ if (!linkStatusResultOk(statusEvidence, check)) {
7928
+ failures.push({
7929
+ code: "http_status_failed",
7930
+ url: stringValue2(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
7931
+ status: numberValue(statusEvidence.status) ?? null,
7932
+ method: stringValue2(statusEvidence.method) ?? httpStatusMethod(check),
7933
+ error: stringValue2(statusEvidence.error) ?? null,
7934
+ content_type: stringValue2(statusEvidence.content_type) ?? null,
7935
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
7936
+ allowed_statuses: httpStatusAllowedStatuses(check) ?? ["2xx", "3xx"],
7937
+ min_bytes: check.min_bytes ?? null,
7938
+ allowed_content_types: check.allowed_content_types ?? null
7939
+ });
7940
+ }
7941
+ return {
7942
+ viewport: viewport.name,
7943
+ key,
7944
+ url: stringValue2(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
7945
+ method: stringValue2(statusEvidence.method) ?? httpStatusMethod(check),
7946
+ status: numberValue(statusEvidence.status) ?? null,
7947
+ status_text: stringValue2(statusEvidence.status_text) ?? null,
7948
+ ok: failures.length === 0,
7949
+ error: stringValue2(statusEvidence.error) ?? null,
7950
+ content_type: stringValue2(statusEvidence.content_type) ?? null,
7951
+ content_length: numberValue(statusEvidence.content_length) ?? null,
7952
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
7953
+ failures
7954
+ };
7955
+ }
7873
7956
  function linkStatusEvidenceForCheck(viewport, check) {
7874
7957
  const evidence = viewport.link_statuses?.[linkStatusSelector(check)];
7875
7958
  return isRecord(evidence) ? evidence : void 0;
@@ -8460,6 +8543,28 @@ function assessCheckFromEvidence(check, evidence) {
8460
8543
  message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
8461
8544
  };
8462
8545
  }
8546
+ if (check.type === "http_status") {
8547
+ const url = httpStatusRequestUrl(check, evidence.target_url);
8548
+ const method = httpStatusMethod(check);
8549
+ const summaries = viewports.map((viewport) => summarizeHttpStatusEvidence(viewport, check));
8550
+ const failed = summaries.filter((summary) => Array.isArray(summary.failures) && summary.failures.length > 0);
8551
+ return {
8552
+ type: check.type,
8553
+ label: checkLabel(check),
8554
+ status: failed.length ? "failed" : "passed",
8555
+ evidence: {
8556
+ url,
8557
+ method,
8558
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
8559
+ require_nonzero_bytes: check.require_nonzero_bytes === true,
8560
+ min_bytes: check.min_bytes ?? null,
8561
+ allowed_content_types: check.allowed_content_types ?? null,
8562
+ viewports: summaries.map((summary) => toJsonValue(summary)),
8563
+ failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue2(summary.viewport) ?? null, failure })) : [])
8564
+ },
8565
+ message: failed.length ? `HTTP status failed in ${failed.length} viewport(s).` : void 0
8566
+ };
8567
+ }
8463
8568
  if (check.type === "link_status" || check.type === "artifact_link_status") {
8464
8569
  const selector = linkStatusSelector(check);
8465
8570
  const summaries = viewports.map((viewport) => summarizeLinkStatusEvidence(viewport, check));
@@ -9053,16 +9158,37 @@ function textOrderMatch(texts, expectedTexts) {
9053
9158
  function linkStatusSelector(check) {
9054
9159
  return check.selector || check.link_selector || "a[href]";
9055
9160
  }
9056
- function linkStatusAllowedStatuses(check) {
9161
+ function httpStatusRequestUrl(check, baseUrl) {
9162
+ const rawUrl = check.url || "";
9163
+ if (!rawUrl) return "";
9164
+ try {
9165
+ return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
9166
+ } catch {
9167
+ return rawUrl;
9168
+ }
9169
+ }
9170
+ function httpStatusMethod(check) {
9171
+ return String(check.method || "GET").toUpperCase();
9172
+ }
9173
+ function httpStatusKey(check, baseUrl) {
9174
+ return httpStatusMethod(check) + " " + httpStatusRequestUrl(check, baseUrl);
9175
+ }
9176
+ function httpStatusAllowedStatuses(check) {
9057
9177
  if (Array.isArray(check.allowed_statuses) && check.allowed_statuses.length) return check.allowed_statuses;
9058
9178
  if (typeof check.expected_status === "number" && Number.isFinite(check.expected_status)) return [check.expected_status];
9059
9179
  return undefined;
9060
9180
  }
9061
- function linkStatusIsAllowed(status, check) {
9181
+ function linkStatusAllowedStatuses(check) {
9182
+ return httpStatusAllowedStatuses(check);
9183
+ }
9184
+ function httpStatusIsAllowed(status, check) {
9062
9185
  if (typeof status !== "number" || !Number.isFinite(status)) return false;
9063
- const allowed = linkStatusAllowedStatuses(check);
9186
+ const allowed = httpStatusAllowedStatuses(check);
9064
9187
  return Array.isArray(allowed) && allowed.length ? allowed.includes(status) : status >= 200 && status < 400;
9065
9188
  }
9189
+ function linkStatusIsAllowed(status, check) {
9190
+ return httpStatusIsAllowed(status, check);
9191
+ }
9066
9192
  function linkStatusObservedBytes(result) {
9067
9193
  const bytes = typeof result.bytes === "number" && Number.isFinite(result.bytes) ? result.bytes : undefined;
9068
9194
  const contentLength = typeof result.content_length === "number" && Number.isFinite(result.content_length) ? result.content_length : undefined;
@@ -9087,7 +9213,7 @@ function linkStatusContentTypeOk(result, check) {
9087
9213
  }
9088
9214
  function linkStatusResultOk(result, check) {
9089
9215
  if (!result || typeof result !== "object" || Array.isArray(result)) return false;
9090
- if (!linkStatusIsAllowed(result.status, check)) return false;
9216
+ if (!httpStatusIsAllowed(result.status, check)) return false;
9091
9217
  if (typeof result.error === "string" && result.error.trim()) return false;
9092
9218
  if (result.ok === false) return false;
9093
9219
  if (!linkStatusContentTypeOk(result, check)) return false;
@@ -9101,6 +9227,50 @@ function linkStatusResultOk(result, check) {
9101
9227
  }
9102
9228
  return true;
9103
9229
  }
9230
+ function summarizeHttpStatusEvidence(viewport, check) {
9231
+ const key = httpStatusKey(check, viewport && viewport.url);
9232
+ const statusEvidence = viewport && viewport.http_statuses && viewport.http_statuses[key];
9233
+ if (!statusEvidence || typeof statusEvidence !== "object" || Array.isArray(statusEvidence)) {
9234
+ return {
9235
+ viewport: viewport && viewport.name,
9236
+ key,
9237
+ url: httpStatusRequestUrl(check, viewport && viewport.url),
9238
+ method: httpStatusMethod(check),
9239
+ ok: false,
9240
+ status: null,
9241
+ failures: [{ code: "http_status_evidence_missing" }],
9242
+ };
9243
+ }
9244
+ const failures = [];
9245
+ if (!linkStatusResultOk(statusEvidence, check)) {
9246
+ failures.push({
9247
+ code: "http_status_failed",
9248
+ url: typeof statusEvidence.url === "string" ? statusEvidence.url : httpStatusRequestUrl(check, viewport && viewport.url),
9249
+ status: typeof statusEvidence.status === "number" && Number.isFinite(statusEvidence.status) ? statusEvidence.status : null,
9250
+ method: typeof statusEvidence.method === "string" ? statusEvidence.method : httpStatusMethod(check),
9251
+ error: typeof statusEvidence.error === "string" ? statusEvidence.error : null,
9252
+ content_type: typeof statusEvidence.content_type === "string" ? statusEvidence.content_type : null,
9253
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
9254
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
9255
+ min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
9256
+ allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
9257
+ });
9258
+ }
9259
+ return {
9260
+ viewport: viewport && viewport.name,
9261
+ key,
9262
+ url: typeof statusEvidence.url === "string" ? statusEvidence.url : httpStatusRequestUrl(check, viewport && viewport.url),
9263
+ method: typeof statusEvidence.method === "string" ? statusEvidence.method : httpStatusMethod(check),
9264
+ status: typeof statusEvidence.status === "number" && Number.isFinite(statusEvidence.status) ? statusEvidence.status : null,
9265
+ status_text: typeof statusEvidence.status_text === "string" ? statusEvidence.status_text : null,
9266
+ ok: failures.length === 0,
9267
+ error: typeof statusEvidence.error === "string" ? statusEvidence.error : null,
9268
+ content_type: typeof statusEvidence.content_type === "string" ? statusEvidence.content_type : null,
9269
+ content_length: typeof statusEvidence.content_length === "number" && Number.isFinite(statusEvidence.content_length) ? statusEvidence.content_length : null,
9270
+ bytes: linkStatusObservedBytes(statusEvidence) ?? null,
9271
+ failures,
9272
+ };
9273
+ }
9104
9274
  function summarizeLinkStatusEvidence(viewport, check) {
9105
9275
  const selector = linkStatusSelector(check);
9106
9276
  const linkEvidence = viewport && viewport.link_statuses && viewport.link_statuses[selector];
@@ -9856,6 +10026,31 @@ function assessProfile(profile, evidence) {
9856
10026
  });
9857
10027
  continue;
9858
10028
  }
10029
+ if (check.type === "http_status") {
10030
+ const url = httpStatusRequestUrl(check, evidence.target_url);
10031
+ const method = httpStatusMethod(check);
10032
+ const summaries = checkViewports.map((viewport) => summarizeHttpStatusEvidence(viewport, check));
10033
+ const failed = summaries.filter((summary) => Array.isArray(summary.failures) && summary.failures.length > 0);
10034
+ checks.push({
10035
+ type: check.type,
10036
+ label: check.label || check.type,
10037
+ status: failed.length ? "failed" : "passed",
10038
+ evidence: {
10039
+ url,
10040
+ method,
10041
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
10042
+ require_nonzero_bytes: check.require_nonzero_bytes === true,
10043
+ min_bytes: check.min_bytes ?? null,
10044
+ allowed_content_types: check.allowed_content_types ?? null,
10045
+ viewports: summaries,
10046
+ failures: failed.flatMap((summary) => Array.isArray(summary.failures)
10047
+ ? summary.failures.map((failure) => ({ viewport: summary.viewport || null, failure }))
10048
+ : []),
10049
+ },
10050
+ message: failed.length ? "HTTP status failed in " + failed.length + " viewport(s)." : undefined,
10051
+ });
10052
+ continue;
10053
+ }
9859
10054
  if (check.type === "link_status" || check.type === "artifact_link_status") {
9860
10055
  const selector = linkStatusSelector(check);
9861
10056
  const summaries = checkViewports.map((viewport) => summarizeLinkStatusEvidence(viewport, check));
@@ -11053,6 +11248,65 @@ function linkProbeResponseFields(response, method) {
11053
11248
  content_length: contentLength,
11054
11249
  };
11055
11250
  }
11251
+ async function collectHttpStatus(check) {
11252
+ const url = httpStatusRequestUrl(check, page.url() || targetUrl);
11253
+ const method = httpStatusMethod(check);
11254
+ const headers = check.headers && typeof check.headers === "object" && !Array.isArray(check.headers)
11255
+ ? Object.fromEntries(Object.entries(check.headers).map(([key, value]) => [key, String(value)]).filter(([key]) => key.trim()))
11256
+ : {};
11257
+ let body;
11258
+ if (check.body_json !== undefined) {
11259
+ body = JSON.stringify(check.body_json);
11260
+ if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) headers["content-type"] = "application/json";
11261
+ } else if (typeof check.body === "string") {
11262
+ body = check.body;
11263
+ }
11264
+ const options = {
11265
+ method,
11266
+ redirect: "follow",
11267
+ cache: "no-store",
11268
+ headers,
11269
+ };
11270
+ if (body !== undefined && method !== "GET" && method !== "HEAD") options.body = body;
11271
+ const result = {
11272
+ version: "riddle-proof.http-status.v1",
11273
+ url,
11274
+ method,
11275
+ status: null,
11276
+ ok: false,
11277
+ error: null,
11278
+ request_body_bytes: typeof body === "string" ? body.length : 0,
11279
+ allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
11280
+ require_nonzero_bytes: check.require_nonzero_bytes === true,
11281
+ min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
11282
+ allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
11283
+ };
11284
+ try {
11285
+ const response = await fetch(url, options);
11286
+ Object.assign(result, linkProbeResponseFields(response, method));
11287
+ result.url = url;
11288
+ result.status_text = response.statusText || "";
11289
+ const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes));
11290
+ if (shouldReadBody) {
11291
+ try {
11292
+ const buffer = await response.arrayBuffer();
11293
+ result.bytes = buffer.byteLength;
11294
+ } catch (error) {
11295
+ result.error = String(error && error.message ? error.message : error).slice(0, 500);
11296
+ }
11297
+ }
11298
+ result.ok = linkProbeAllowed(result.status, check)
11299
+ && linkProbeContentTypeAllowed(result, check)
11300
+ && (check.require_nonzero_bytes !== true || ((linkProbeObservedBytes(result) || 0) > 0))
11301
+ && (!(typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || ((linkProbeObservedBytes(result) || 0) >= check.min_bytes))
11302
+ && !result.error;
11303
+ return result;
11304
+ } catch (error) {
11305
+ result.error = String(error && error.message ? error.message : error).slice(0, 500);
11306
+ result.ok = false;
11307
+ return result;
11308
+ }
11309
+ }
11056
11310
  async function probeLinkStatus(candidate, check) {
11057
11311
  const requireNonzeroBytes = check.require_nonzero_bytes === true;
11058
11312
  const minBytes = typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? Math.max(1, Math.floor(check.min_bytes)) : null;
@@ -11745,6 +11999,7 @@ async function captureViewport(viewport) {
11745
11999
  const frames = {};
11746
12000
  const text_sequences = {};
11747
12001
  const text_matches = {};
12002
+ const http_statuses = {};
11748
12003
  const link_statuses = {};
11749
12004
  for (const check of profile.checks || []) {
11750
12005
  if (!profileCheckAppliesToViewport(check, viewport)) continue;
@@ -11771,6 +12026,10 @@ async function captureViewport(viewport) {
11771
12026
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
11772
12027
  frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
11773
12028
  }
12029
+ if (check.type === "http_status") {
12030
+ const key = httpStatusKey(check, page.url() || targetUrl);
12031
+ http_statuses[key] = http_statuses[key] || await collectHttpStatus(check);
12032
+ }
11774
12033
  if (check.type === "link_status" || check.type === "artifact_link_status") {
11775
12034
  const selector = linkStatusSelector(check);
11776
12035
  link_statuses[selector] = link_statuses[selector] || await collectLinkStatus(check);
@@ -11838,6 +12097,7 @@ async function captureViewport(viewport) {
11838
12097
  frames,
11839
12098
  text_sequences,
11840
12099
  text_matches,
12100
+ http_statuses,
11841
12101
  link_statuses,
11842
12102
  route_inventory: routeInventory,
11843
12103
  setup_action_results: setupActionResults,
@@ -11887,6 +12147,19 @@ function buildProfileEvidence(currentViewports) {
11887
12147
  ),
11888
12148
  })),
11889
12149
  })),
12150
+ http_status: currentViewports
12151
+ .filter((viewport) => viewport.http_statuses && Object.keys(viewport.http_statuses).length)
12152
+ .map((viewport) => ({
12153
+ viewport: viewport.name,
12154
+ requests: Object.entries(viewport.http_statuses || {}).map(([key, statusSet]) => ({
12155
+ key,
12156
+ url: statusSet && statusSet.url,
12157
+ method: statusSet && statusSet.method,
12158
+ status: statusSet && statusSet.status,
12159
+ ok: statusSet && statusSet.ok === true,
12160
+ error: statusSet && statusSet.error,
12161
+ })),
12162
+ })),
11890
12163
  link_status: currentViewports
11891
12164
  .filter((viewport) => viewport.link_statuses && Object.keys(viewport.link_statuses).length)
11892
12165
  .map((viewport) => ({
@@ -12344,6 +12617,10 @@ function profileResultMarkdown(result) {
12344
12617
  if (linkStatusSummaryLines.length) {
12345
12618
  lines.push("", "## Link Status", "", ...linkStatusSummaryLines);
12346
12619
  }
12620
+ const httpStatusSummaryLines = profileHttpStatusSummaryMarkdown(result);
12621
+ if (httpStatusSummaryLines.length) {
12622
+ lines.push("", "## HTTP Status", "", ...httpStatusSummaryLines);
12623
+ }
12347
12624
  const environmentBlockerLines = profileEnvironmentBlockerMarkdown(result);
12348
12625
  if (environmentBlockerLines.length) {
12349
12626
  lines.push("", "## Environment Blocker", "", ...environmentBlockerLines);
@@ -12389,7 +12666,9 @@ function profileCheckMarkdownTarget(check) {
12389
12666
  }
12390
12667
  if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
12391
12668
  const textTarget = profileCheckTextTarget(evidence);
12392
- if (selector && textTarget) return `${markdownInlineCode(selector)} contains ${textTarget}`;
12669
+ const verb = check.type === "selector_text_absent" ? "does not contain" : "contains";
12670
+ if (selector && textTarget) return `${markdownInlineCode(selector)} ${verb} ${textTarget}`;
12671
+ if (textTarget && check.type === "selector_text_absent") return `${verb} ${textTarget}`;
12393
12672
  return selector ? markdownInlineCode(selector) : textTarget;
12394
12673
  }
12395
12674
  if (check.type === "selector_text_order") {
@@ -12418,6 +12697,13 @@ function profileCheckMarkdownTarget(check) {
12418
12697
  if (selector && maxOverflow !== void 0) return `${markdownInlineCode(selector)} <= ${maxOverflow}px`;
12419
12698
  return selector ? markdownInlineCode(selector) : maxOverflow !== void 0 ? `<= ${maxOverflow}px` : void 0;
12420
12699
  }
12700
+ if (check.type === "http_status") {
12701
+ const url = cliString(evidence.url);
12702
+ const method = cliString(evidence.method);
12703
+ const statuses = Array.isArray(evidence.allowed_statuses) ? evidence.allowed_statuses.map((status) => cliString(status) || String(status)).filter(Boolean) : [];
12704
+ const target = [method, url ? markdownInlineCode(url) : ""].filter(Boolean).join(" ");
12705
+ return statuses.length ? `${target} -> ${statuses.join("/")}` : target || void 0;
12706
+ }
12421
12707
  if (check.type === "link_status" || check.type === "artifact_link_status") {
12422
12708
  const expectedCount = cliFiniteNumber(evidence.expected_count);
12423
12709
  const minCount = cliFiniteNumber(evidence.min_count);
@@ -12645,6 +12931,24 @@ function profileLinkStatusSummaryMarkdown(result) {
12645
12931
  }
12646
12932
  return lines;
12647
12933
  }
12934
+ function profileHttpStatusSummaryMarkdown(result) {
12935
+ const httpStatusChecks = result.checks.filter((check) => check.type === "http_status");
12936
+ const lines = [];
12937
+ for (const check of httpStatusChecks) {
12938
+ const evidence = cliRecord(check.evidence);
12939
+ if (!evidence) continue;
12940
+ const label = check.label || check.type;
12941
+ const url = cliString(evidence.url);
12942
+ const method = cliString(evidence.method) || "GET";
12943
+ const viewports = Array.isArray(evidence.viewports) ? evidence.viewports.map(cliRecord).filter((viewport) => Boolean(viewport)) : [];
12944
+ const statuses = viewports.map((viewport) => cliFiniteNumber(viewport.status)).map((status) => status === void 0 ? "error" : String(status));
12945
+ const failedTotal = Array.isArray(evidence.failures) ? evidence.failures.length : 0;
12946
+ lines.push(
12947
+ `- ${label}: ${method}${url ? ` ${markdownInlineCode(url)}` : ""}, statuses ${statuses.length ? statuses.join("/") : "unknown"}, failures ${failedTotal}`
12948
+ );
12949
+ }
12950
+ return lines;
12951
+ }
12648
12952
  function writeProfileOutput(outputDir, result) {
12649
12953
  if (!outputDir) return;
12650
12954
  (0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-G3UY3SHZ.js";
13
+ } from "./chunk-OMTWNL4B.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
@@ -346,6 +346,10 @@ function profileResultMarkdown(result) {
346
346
  if (linkStatusSummaryLines.length) {
347
347
  lines.push("", "## Link Status", "", ...linkStatusSummaryLines);
348
348
  }
349
+ const httpStatusSummaryLines = profileHttpStatusSummaryMarkdown(result);
350
+ if (httpStatusSummaryLines.length) {
351
+ lines.push("", "## HTTP Status", "", ...httpStatusSummaryLines);
352
+ }
349
353
  const environmentBlockerLines = profileEnvironmentBlockerMarkdown(result);
350
354
  if (environmentBlockerLines.length) {
351
355
  lines.push("", "## Environment Blocker", "", ...environmentBlockerLines);
@@ -391,7 +395,9 @@ function profileCheckMarkdownTarget(check) {
391
395
  }
392
396
  if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
393
397
  const textTarget = profileCheckTextTarget(evidence);
394
- if (selector && textTarget) return `${markdownInlineCode(selector)} contains ${textTarget}`;
398
+ const verb = check.type === "selector_text_absent" ? "does not contain" : "contains";
399
+ if (selector && textTarget) return `${markdownInlineCode(selector)} ${verb} ${textTarget}`;
400
+ if (textTarget && check.type === "selector_text_absent") return `${verb} ${textTarget}`;
395
401
  return selector ? markdownInlineCode(selector) : textTarget;
396
402
  }
397
403
  if (check.type === "selector_text_order") {
@@ -420,6 +426,13 @@ function profileCheckMarkdownTarget(check) {
420
426
  if (selector && maxOverflow !== void 0) return `${markdownInlineCode(selector)} <= ${maxOverflow}px`;
421
427
  return selector ? markdownInlineCode(selector) : maxOverflow !== void 0 ? `<= ${maxOverflow}px` : void 0;
422
428
  }
429
+ if (check.type === "http_status") {
430
+ const url = cliString(evidence.url);
431
+ const method = cliString(evidence.method);
432
+ const statuses = Array.isArray(evidence.allowed_statuses) ? evidence.allowed_statuses.map((status) => cliString(status) || String(status)).filter(Boolean) : [];
433
+ const target = [method, url ? markdownInlineCode(url) : ""].filter(Boolean).join(" ");
434
+ return statuses.length ? `${target} -> ${statuses.join("/")}` : target || void 0;
435
+ }
423
436
  if (check.type === "link_status" || check.type === "artifact_link_status") {
424
437
  const expectedCount = cliFiniteNumber(evidence.expected_count);
425
438
  const minCount = cliFiniteNumber(evidence.min_count);
@@ -647,6 +660,24 @@ function profileLinkStatusSummaryMarkdown(result) {
647
660
  }
648
661
  return lines;
649
662
  }
663
+ function profileHttpStatusSummaryMarkdown(result) {
664
+ const httpStatusChecks = result.checks.filter((check) => check.type === "http_status");
665
+ const lines = [];
666
+ for (const check of httpStatusChecks) {
667
+ const evidence = cliRecord(check.evidence);
668
+ if (!evidence) continue;
669
+ const label = check.label || check.type;
670
+ const url = cliString(evidence.url);
671
+ const method = cliString(evidence.method) || "GET";
672
+ const viewports = Array.isArray(evidence.viewports) ? evidence.viewports.map(cliRecord).filter((viewport) => Boolean(viewport)) : [];
673
+ const statuses = viewports.map((viewport) => cliFiniteNumber(viewport.status)).map((status) => status === void 0 ? "error" : String(status));
674
+ const failedTotal = Array.isArray(evidence.failures) ? evidence.failures.length : 0;
675
+ lines.push(
676
+ `- ${label}: ${method}${url ? ` ${markdownInlineCode(url)}` : ""}, statuses ${statuses.length ? statuses.join("/") : "unknown"}, failures ${failedTotal}`
677
+ );
678
+ }
679
+ return lines;
680
+ }
650
681
  function writeProfileOutput(outputDir, result) {
651
682
  if (!outputDir) return;
652
683
  mkdirSync(outputDir, { recursive: true });