@riddledc/riddle-proof 0.7.97 → 0.7.99
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/{chunk-G3UY3SHZ.js → chunk-W5PBTTMJ.js} +332 -14
- package/dist/cli.cjs +361 -14
- package/dist/cli.js +30 -1
- package/dist/index.cjs +332 -14
- package/dist/index.js +1 -1
- package/dist/profile.cjs +332 -14
- package/dist/profile.d.cts +8 -1
- package/dist/profile.d.ts +8 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8758,6 +8758,7 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
8758
8758
|
"frame_no_horizontal_overflow",
|
|
8759
8759
|
"text_visible",
|
|
8760
8760
|
"text_absent",
|
|
8761
|
+
"http_status",
|
|
8761
8762
|
"link_status",
|
|
8762
8763
|
"artifact_link_status",
|
|
8763
8764
|
"route_inventory",
|
|
@@ -9507,19 +9508,34 @@ function normalizeCheck(input, index) {
|
|
|
9507
9508
|
if (type === "route_inventory" && !expectedRoutes?.length) {
|
|
9508
9509
|
throw new Error(`checks[${index}] route_inventory requires expected_routes.`);
|
|
9509
9510
|
}
|
|
9511
|
+
const isHttpStatusCheck = type === "http_status";
|
|
9510
9512
|
const isLinkStatusCheck = type === "link_status" || type === "artifact_link_status";
|
|
9511
|
-
const
|
|
9512
|
-
const
|
|
9513
|
+
const isStatusCheck = isHttpStatusCheck || isLinkStatusCheck;
|
|
9514
|
+
const requestUrl = stringFromOwn(input, "url", "endpoint_url", "endpointUrl", "endpoint", "request_url", "requestUrl", "path");
|
|
9515
|
+
if (isHttpStatusCheck && !requestUrl) {
|
|
9516
|
+
throw new Error(`checks[${index}] http_status requires url.`);
|
|
9517
|
+
}
|
|
9518
|
+
const method = stringFromOwn(input, "method", "http_method", "httpMethod")?.toUpperCase();
|
|
9519
|
+
if (isHttpStatusCheck && method && !/^[A-Z]+$/.test(method)) {
|
|
9520
|
+
throw new Error(`checks[${index}] http_status method must contain only letters.`);
|
|
9521
|
+
}
|
|
9522
|
+
const hasBodyJson = hasOwn(input, "body_json") || hasOwn(input, "bodyJson") || hasOwn(input, "json");
|
|
9523
|
+
const expectedStatus = isStatusCheck ? normalizeHttpStatus(input.expected_status ?? input.expectedStatus ?? input.status, `checks[${index}] expected_status`) : void 0;
|
|
9524
|
+
const allowedStatuses = isStatusCheck ? normalizeHttpStatuses(
|
|
9513
9525
|
input.allowed_statuses ?? input.allowedStatuses ?? input.expected_statuses ?? input.expectedStatuses,
|
|
9514
9526
|
`checks[${index}] allowed_statuses`
|
|
9515
9527
|
) : void 0;
|
|
9516
9528
|
const maxLinks = isLinkStatusCheck ? normalizePositiveInteger(input.max_links ?? input.maxLinks ?? input.limit, `checks[${index}] max_links`, 500) : void 0;
|
|
9517
|
-
const minBytes =
|
|
9518
|
-
const expectedContentType =
|
|
9519
|
-
const allowedContentTypes =
|
|
9529
|
+
const minBytes = isStatusCheck ? normalizePositiveInteger(input.min_bytes ?? input.minBytes ?? input.min_response_bytes ?? input.minResponseBytes, `checks[${index}] min_bytes`) : void 0;
|
|
9530
|
+
const expectedContentType = isStatusCheck ? stringValue5(input.expected_content_type) || stringValue5(input.expectedContentType) || stringValue5(input.content_type) || stringValue5(input.contentType) : void 0;
|
|
9531
|
+
const allowedContentTypes = isStatusCheck ? normalizeStringList(
|
|
9520
9532
|
input.allowed_content_types ?? input.allowedContentTypes ?? input.expected_content_types ?? input.expectedContentTypes,
|
|
9521
9533
|
`checks[${index}] allowed_content_types`
|
|
9522
9534
|
) ?? (expectedContentType ? [expectedContentType] : void 0) : void 0;
|
|
9535
|
+
const bodyContains = isHttpStatusCheck ? normalizeStringList(
|
|
9536
|
+
input.body_contains ?? input.bodyContains ?? input.expected_body_contains ?? input.expectedBodyContains ?? input.response_body_contains ?? input.responseBodyContains ?? input.body_includes ?? input.bodyIncludes,
|
|
9537
|
+
`checks[${index}] body_contains`
|
|
9538
|
+
) : void 0;
|
|
9523
9539
|
if (isLinkStatusCheck) {
|
|
9524
9540
|
if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
|
|
9525
9541
|
throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
|
|
@@ -9537,6 +9553,12 @@ function normalizeCheck(input, index) {
|
|
|
9537
9553
|
expected_url: expectedUrl,
|
|
9538
9554
|
expected_routes: expectedRoutes,
|
|
9539
9555
|
selector: stringValue5(input.selector),
|
|
9556
|
+
url: isHttpStatusCheck ? requestUrl : void 0,
|
|
9557
|
+
method: isHttpStatusCheck ? method || "GET" : void 0,
|
|
9558
|
+
headers: isHttpStatusCheck ? stringRecord(input.headers) : void 0,
|
|
9559
|
+
body: isHttpStatusCheck ? stringValue5(input.body) : void 0,
|
|
9560
|
+
body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
|
|
9561
|
+
body_contains: bodyContains,
|
|
9540
9562
|
expected_texts: expectedTexts,
|
|
9541
9563
|
link_selector: stringValue5(input.link_selector) || stringValue5(input.linkSelector),
|
|
9542
9564
|
source_selector: stringValue5(input.source_selector) || stringValue5(input.sourceSelector),
|
|
@@ -9560,7 +9582,7 @@ function normalizeCheck(input, index) {
|
|
|
9560
9582
|
max_links: maxLinks,
|
|
9561
9583
|
same_origin_only: isLinkStatusCheck ? input.same_origin_only === true || input.sameOriginOnly === true : void 0,
|
|
9562
9584
|
dedupe: isLinkStatusCheck ? input.dedupe === false ? false : true : void 0,
|
|
9563
|
-
require_nonzero_bytes:
|
|
9585
|
+
require_nonzero_bytes: isStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
|
|
9564
9586
|
min_bytes: minBytes,
|
|
9565
9587
|
allowed_content_types: allowedContentTypes,
|
|
9566
9588
|
allow_get_fallback: isLinkStatusCheck ? input.allow_get_fallback === false || input.allowGetFallback === false ? false : true : void 0,
|
|
@@ -9659,14 +9681,32 @@ function selectorKey(check) {
|
|
|
9659
9681
|
function linkStatusSelector(check) {
|
|
9660
9682
|
return check.selector || check.link_selector || "a[href]";
|
|
9661
9683
|
}
|
|
9662
|
-
function
|
|
9684
|
+
function httpStatusRequestUrl(check, baseUrl) {
|
|
9685
|
+
const rawUrl = check.url || "";
|
|
9686
|
+
if (!rawUrl) return "";
|
|
9687
|
+
try {
|
|
9688
|
+
return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
|
|
9689
|
+
} catch {
|
|
9690
|
+
return rawUrl;
|
|
9691
|
+
}
|
|
9692
|
+
}
|
|
9693
|
+
function httpStatusMethod(check) {
|
|
9694
|
+
return (check.method || "GET").toUpperCase();
|
|
9695
|
+
}
|
|
9696
|
+
function httpStatusKey(check, baseUrl) {
|
|
9697
|
+
return `${httpStatusMethod(check)} ${httpStatusRequestUrl(check, baseUrl)}`;
|
|
9698
|
+
}
|
|
9699
|
+
function httpStatusAllowedStatuses(check) {
|
|
9663
9700
|
if (check.allowed_statuses?.length) return check.allowed_statuses;
|
|
9664
9701
|
if (check.expected_status !== void 0) return [check.expected_status];
|
|
9665
9702
|
return void 0;
|
|
9666
9703
|
}
|
|
9667
|
-
function
|
|
9704
|
+
function linkStatusAllowedStatuses(check) {
|
|
9705
|
+
return httpStatusAllowedStatuses(check);
|
|
9706
|
+
}
|
|
9707
|
+
function httpStatusIsAllowed(status, check) {
|
|
9668
9708
|
if (status === void 0) return false;
|
|
9669
|
-
const allowed =
|
|
9709
|
+
const allowed = httpStatusAllowedStatuses(check);
|
|
9670
9710
|
return allowed?.length ? allowed.includes(status) : status >= 200 && status < 400;
|
|
9671
9711
|
}
|
|
9672
9712
|
function linkStatusObservedBytes(result) {
|
|
@@ -9690,9 +9730,15 @@ function linkStatusContentTypeOk(result, check) {
|
|
|
9690
9730
|
return actual === normalized;
|
|
9691
9731
|
});
|
|
9692
9732
|
}
|
|
9733
|
+
function httpStatusBodyContainsFailures(result, check) {
|
|
9734
|
+
const expected = check.body_contains?.filter(Boolean) ?? [];
|
|
9735
|
+
if (!expected.length) return [];
|
|
9736
|
+
const observed = isRecord2(result.body_contains) ? result.body_contains : {};
|
|
9737
|
+
return expected.filter((text) => observed[text] !== true);
|
|
9738
|
+
}
|
|
9693
9739
|
function linkStatusResultOk(result, check) {
|
|
9694
9740
|
const status = numberValue3(result.status);
|
|
9695
|
-
if (!
|
|
9741
|
+
if (!httpStatusIsAllowed(status, check)) return false;
|
|
9696
9742
|
if (stringValue5(result.error)) return false;
|
|
9697
9743
|
if (result.ok === false) return false;
|
|
9698
9744
|
if (!linkStatusContentTypeOk(result, check)) return false;
|
|
@@ -9704,8 +9750,64 @@ function linkStatusResultOk(result, check) {
|
|
|
9704
9750
|
const observedBytes = linkStatusObservedBytes(result);
|
|
9705
9751
|
if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
|
|
9706
9752
|
}
|
|
9753
|
+
if (httpStatusBodyContainsFailures(result, check).length) return false;
|
|
9707
9754
|
return true;
|
|
9708
9755
|
}
|
|
9756
|
+
function httpStatusEvidenceForCheck(viewport, check) {
|
|
9757
|
+
const evidence = viewport.http_statuses?.[httpStatusKey(check, viewport.url)];
|
|
9758
|
+
return isRecord2(evidence) ? evidence : void 0;
|
|
9759
|
+
}
|
|
9760
|
+
function summarizeHttpStatusEvidence(viewport, check) {
|
|
9761
|
+
const key = httpStatusKey(check, viewport.url);
|
|
9762
|
+
const statusEvidence = httpStatusEvidenceForCheck(viewport, check);
|
|
9763
|
+
if (!statusEvidence) {
|
|
9764
|
+
return {
|
|
9765
|
+
viewport: viewport.name,
|
|
9766
|
+
key,
|
|
9767
|
+
url: httpStatusRequestUrl(check, viewport.url),
|
|
9768
|
+
method: httpStatusMethod(check),
|
|
9769
|
+
ok: false,
|
|
9770
|
+
status: null,
|
|
9771
|
+
failures: [{ code: "http_status_evidence_missing" }]
|
|
9772
|
+
};
|
|
9773
|
+
}
|
|
9774
|
+
const failures = [];
|
|
9775
|
+
const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
|
|
9776
|
+
if (!linkStatusResultOk(statusEvidence, check)) {
|
|
9777
|
+
failures.push({
|
|
9778
|
+
code: "http_status_failed",
|
|
9779
|
+
url: stringValue5(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
|
|
9780
|
+
status: numberValue3(statusEvidence.status) ?? null,
|
|
9781
|
+
method: stringValue5(statusEvidence.method) ?? httpStatusMethod(check),
|
|
9782
|
+
error: stringValue5(statusEvidence.error) ?? null,
|
|
9783
|
+
content_type: stringValue5(statusEvidence.content_type) ?? null,
|
|
9784
|
+
bytes: linkStatusObservedBytes(statusEvidence) ?? null,
|
|
9785
|
+
allowed_statuses: httpStatusAllowedStatuses(check) ?? ["2xx", "3xx"],
|
|
9786
|
+
min_bytes: check.min_bytes ?? null,
|
|
9787
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
9788
|
+
body_contains: check.body_contains ?? null,
|
|
9789
|
+
body_contains_missing: bodyContainsMissing,
|
|
9790
|
+
body_sample: stringValue5(statusEvidence.body_sample) ?? null
|
|
9791
|
+
});
|
|
9792
|
+
}
|
|
9793
|
+
return {
|
|
9794
|
+
viewport: viewport.name,
|
|
9795
|
+
key,
|
|
9796
|
+
url: stringValue5(statusEvidence.url) ?? httpStatusRequestUrl(check, viewport.url),
|
|
9797
|
+
method: stringValue5(statusEvidence.method) ?? httpStatusMethod(check),
|
|
9798
|
+
status: numberValue3(statusEvidence.status) ?? null,
|
|
9799
|
+
status_text: stringValue5(statusEvidence.status_text) ?? null,
|
|
9800
|
+
ok: failures.length === 0,
|
|
9801
|
+
error: stringValue5(statusEvidence.error) ?? null,
|
|
9802
|
+
content_type: stringValue5(statusEvidence.content_type) ?? null,
|
|
9803
|
+
content_length: numberValue3(statusEvidence.content_length) ?? null,
|
|
9804
|
+
bytes: linkStatusObservedBytes(statusEvidence) ?? null,
|
|
9805
|
+
body_contains: isRecord2(statusEvidence.body_contains) ? toJsonValue(statusEvidence.body_contains) : null,
|
|
9806
|
+
body_contains_missing: bodyContainsMissing,
|
|
9807
|
+
body_sample: stringValue5(statusEvidence.body_sample) ?? null,
|
|
9808
|
+
failures
|
|
9809
|
+
};
|
|
9810
|
+
}
|
|
9709
9811
|
function linkStatusEvidenceForCheck(viewport, check) {
|
|
9710
9812
|
const evidence = viewport.link_statuses?.[linkStatusSelector(check)];
|
|
9711
9813
|
return isRecord2(evidence) ? evidence : void 0;
|
|
@@ -10296,6 +10398,29 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
10296
10398
|
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
10297
10399
|
};
|
|
10298
10400
|
}
|
|
10401
|
+
if (check.type === "http_status") {
|
|
10402
|
+
const url = httpStatusRequestUrl(check, evidence.target_url);
|
|
10403
|
+
const method = httpStatusMethod(check);
|
|
10404
|
+
const summaries = viewports.map((viewport) => summarizeHttpStatusEvidence(viewport, check));
|
|
10405
|
+
const failed = summaries.filter((summary) => Array.isArray(summary.failures) && summary.failures.length > 0);
|
|
10406
|
+
return {
|
|
10407
|
+
type: check.type,
|
|
10408
|
+
label: checkLabel(check),
|
|
10409
|
+
status: failed.length ? "failed" : "passed",
|
|
10410
|
+
evidence: {
|
|
10411
|
+
url,
|
|
10412
|
+
method,
|
|
10413
|
+
allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
10414
|
+
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
10415
|
+
min_bytes: check.min_bytes ?? null,
|
|
10416
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
10417
|
+
body_contains: check.body_contains ?? [],
|
|
10418
|
+
viewports: summaries.map((summary) => toJsonValue(summary)),
|
|
10419
|
+
failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue5(summary.viewport) ?? null, failure })) : [])
|
|
10420
|
+
},
|
|
10421
|
+
message: failed.length ? `HTTP status failed in ${failed.length} viewport(s).` : void 0
|
|
10422
|
+
};
|
|
10423
|
+
}
|
|
10299
10424
|
if (check.type === "link_status" || check.type === "artifact_link_status") {
|
|
10300
10425
|
const selector = linkStatusSelector(check);
|
|
10301
10426
|
const summaries = viewports.map((viewport) => summarizeLinkStatusEvidence(viewport, check));
|
|
@@ -10905,16 +11030,37 @@ function textOrderMatch(texts, expectedTexts) {
|
|
|
10905
11030
|
function linkStatusSelector(check) {
|
|
10906
11031
|
return check.selector || check.link_selector || "a[href]";
|
|
10907
11032
|
}
|
|
10908
|
-
function
|
|
11033
|
+
function httpStatusRequestUrl(check, baseUrl) {
|
|
11034
|
+
const rawUrl = check.url || "";
|
|
11035
|
+
if (!rawUrl) return "";
|
|
11036
|
+
try {
|
|
11037
|
+
return baseUrl ? new URL(rawUrl, baseUrl).href : new URL(rawUrl).href;
|
|
11038
|
+
} catch {
|
|
11039
|
+
return rawUrl;
|
|
11040
|
+
}
|
|
11041
|
+
}
|
|
11042
|
+
function httpStatusMethod(check) {
|
|
11043
|
+
return String(check.method || "GET").toUpperCase();
|
|
11044
|
+
}
|
|
11045
|
+
function httpStatusKey(check, baseUrl) {
|
|
11046
|
+
return httpStatusMethod(check) + " " + httpStatusRequestUrl(check, baseUrl);
|
|
11047
|
+
}
|
|
11048
|
+
function httpStatusAllowedStatuses(check) {
|
|
10909
11049
|
if (Array.isArray(check.allowed_statuses) && check.allowed_statuses.length) return check.allowed_statuses;
|
|
10910
11050
|
if (typeof check.expected_status === "number" && Number.isFinite(check.expected_status)) return [check.expected_status];
|
|
10911
11051
|
return undefined;
|
|
10912
11052
|
}
|
|
10913
|
-
function
|
|
11053
|
+
function linkStatusAllowedStatuses(check) {
|
|
11054
|
+
return httpStatusAllowedStatuses(check);
|
|
11055
|
+
}
|
|
11056
|
+
function httpStatusIsAllowed(status, check) {
|
|
10914
11057
|
if (typeof status !== "number" || !Number.isFinite(status)) return false;
|
|
10915
|
-
const allowed =
|
|
11058
|
+
const allowed = httpStatusAllowedStatuses(check);
|
|
10916
11059
|
return Array.isArray(allowed) && allowed.length ? allowed.includes(status) : status >= 200 && status < 400;
|
|
10917
11060
|
}
|
|
11061
|
+
function linkStatusIsAllowed(status, check) {
|
|
11062
|
+
return httpStatusIsAllowed(status, check);
|
|
11063
|
+
}
|
|
10918
11064
|
function linkStatusObservedBytes(result) {
|
|
10919
11065
|
const bytes = typeof result.bytes === "number" && Number.isFinite(result.bytes) ? result.bytes : undefined;
|
|
10920
11066
|
const contentLength = typeof result.content_length === "number" && Number.isFinite(result.content_length) ? result.content_length : undefined;
|
|
@@ -10937,9 +11083,17 @@ function linkStatusContentTypeOk(result, check) {
|
|
|
10937
11083
|
return actual === normalized;
|
|
10938
11084
|
});
|
|
10939
11085
|
}
|
|
11086
|
+
function httpStatusBodyContainsFailures(result, check) {
|
|
11087
|
+
const expected = Array.isArray(check.body_contains) ? check.body_contains.filter(Boolean) : [];
|
|
11088
|
+
if (!expected.length) return [];
|
|
11089
|
+
const observed = result && typeof result.body_contains === "object" && !Array.isArray(result.body_contains)
|
|
11090
|
+
? result.body_contains
|
|
11091
|
+
: {};
|
|
11092
|
+
return expected.filter((text) => observed[text] !== true);
|
|
11093
|
+
}
|
|
10940
11094
|
function linkStatusResultOk(result, check) {
|
|
10941
11095
|
if (!result || typeof result !== "object" || Array.isArray(result)) return false;
|
|
10942
|
-
if (!
|
|
11096
|
+
if (!httpStatusIsAllowed(result.status, check)) return false;
|
|
10943
11097
|
if (typeof result.error === "string" && result.error.trim()) return false;
|
|
10944
11098
|
if (result.ok === false) return false;
|
|
10945
11099
|
if (!linkStatusContentTypeOk(result, check)) return false;
|
|
@@ -10951,8 +11105,62 @@ function linkStatusResultOk(result, check) {
|
|
|
10951
11105
|
const observedBytes = linkStatusObservedBytes(result);
|
|
10952
11106
|
if (observedBytes === undefined || observedBytes < check.min_bytes) return false;
|
|
10953
11107
|
}
|
|
11108
|
+
if (httpStatusBodyContainsFailures(result, check).length) return false;
|
|
10954
11109
|
return true;
|
|
10955
11110
|
}
|
|
11111
|
+
function summarizeHttpStatusEvidence(viewport, check) {
|
|
11112
|
+
const key = httpStatusKey(check, viewport && viewport.url);
|
|
11113
|
+
const statusEvidence = viewport && viewport.http_statuses && viewport.http_statuses[key];
|
|
11114
|
+
if (!statusEvidence || typeof statusEvidence !== "object" || Array.isArray(statusEvidence)) {
|
|
11115
|
+
return {
|
|
11116
|
+
viewport: viewport && viewport.name,
|
|
11117
|
+
key,
|
|
11118
|
+
url: httpStatusRequestUrl(check, viewport && viewport.url),
|
|
11119
|
+
method: httpStatusMethod(check),
|
|
11120
|
+
ok: false,
|
|
11121
|
+
status: null,
|
|
11122
|
+
failures: [{ code: "http_status_evidence_missing" }],
|
|
11123
|
+
};
|
|
11124
|
+
}
|
|
11125
|
+
const failures = [];
|
|
11126
|
+
const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
|
|
11127
|
+
if (!linkStatusResultOk(statusEvidence, check)) {
|
|
11128
|
+
failures.push({
|
|
11129
|
+
code: "http_status_failed",
|
|
11130
|
+
url: typeof statusEvidence.url === "string" ? statusEvidence.url : httpStatusRequestUrl(check, viewport && viewport.url),
|
|
11131
|
+
status: typeof statusEvidence.status === "number" && Number.isFinite(statusEvidence.status) ? statusEvidence.status : null,
|
|
11132
|
+
method: typeof statusEvidence.method === "string" ? statusEvidence.method : httpStatusMethod(check),
|
|
11133
|
+
error: typeof statusEvidence.error === "string" ? statusEvidence.error : null,
|
|
11134
|
+
content_type: typeof statusEvidence.content_type === "string" ? statusEvidence.content_type : null,
|
|
11135
|
+
bytes: linkStatusObservedBytes(statusEvidence) ?? null,
|
|
11136
|
+
allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
11137
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
11138
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
11139
|
+
body_contains: Array.isArray(check.body_contains) ? check.body_contains : null,
|
|
11140
|
+
body_contains_missing: bodyContainsMissing,
|
|
11141
|
+
body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
|
|
11142
|
+
});
|
|
11143
|
+
}
|
|
11144
|
+
return {
|
|
11145
|
+
viewport: viewport && viewport.name,
|
|
11146
|
+
key,
|
|
11147
|
+
url: typeof statusEvidence.url === "string" ? statusEvidence.url : httpStatusRequestUrl(check, viewport && viewport.url),
|
|
11148
|
+
method: typeof statusEvidence.method === "string" ? statusEvidence.method : httpStatusMethod(check),
|
|
11149
|
+
status: typeof statusEvidence.status === "number" && Number.isFinite(statusEvidence.status) ? statusEvidence.status : null,
|
|
11150
|
+
status_text: typeof statusEvidence.status_text === "string" ? statusEvidence.status_text : null,
|
|
11151
|
+
ok: failures.length === 0,
|
|
11152
|
+
error: typeof statusEvidence.error === "string" ? statusEvidence.error : null,
|
|
11153
|
+
content_type: typeof statusEvidence.content_type === "string" ? statusEvidence.content_type : null,
|
|
11154
|
+
content_length: typeof statusEvidence.content_length === "number" && Number.isFinite(statusEvidence.content_length) ? statusEvidence.content_length : null,
|
|
11155
|
+
bytes: linkStatusObservedBytes(statusEvidence) ?? null,
|
|
11156
|
+
body_contains: statusEvidence.body_contains && typeof statusEvidence.body_contains === "object" && !Array.isArray(statusEvidence.body_contains)
|
|
11157
|
+
? statusEvidence.body_contains
|
|
11158
|
+
: null,
|
|
11159
|
+
body_contains_missing: bodyContainsMissing,
|
|
11160
|
+
body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
|
|
11161
|
+
failures,
|
|
11162
|
+
};
|
|
11163
|
+
}
|
|
10956
11164
|
function summarizeLinkStatusEvidence(viewport, check) {
|
|
10957
11165
|
const selector = linkStatusSelector(check);
|
|
10958
11166
|
const linkEvidence = viewport && viewport.link_statuses && viewport.link_statuses[selector];
|
|
@@ -11708,6 +11916,31 @@ function assessProfile(profile, evidence) {
|
|
|
11708
11916
|
});
|
|
11709
11917
|
continue;
|
|
11710
11918
|
}
|
|
11919
|
+
if (check.type === "http_status") {
|
|
11920
|
+
const url = httpStatusRequestUrl(check, evidence.target_url);
|
|
11921
|
+
const method = httpStatusMethod(check);
|
|
11922
|
+
const summaries = checkViewports.map((viewport) => summarizeHttpStatusEvidence(viewport, check));
|
|
11923
|
+
const failed = summaries.filter((summary) => Array.isArray(summary.failures) && summary.failures.length > 0);
|
|
11924
|
+
checks.push({
|
|
11925
|
+
type: check.type,
|
|
11926
|
+
label: check.label || check.type,
|
|
11927
|
+
status: failed.length ? "failed" : "passed",
|
|
11928
|
+
evidence: {
|
|
11929
|
+
url,
|
|
11930
|
+
method,
|
|
11931
|
+
allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
11932
|
+
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
11933
|
+
min_bytes: check.min_bytes ?? null,
|
|
11934
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
11935
|
+
viewports: summaries,
|
|
11936
|
+
failures: failed.flatMap((summary) => Array.isArray(summary.failures)
|
|
11937
|
+
? summary.failures.map((failure) => ({ viewport: summary.viewport || null, failure }))
|
|
11938
|
+
: []),
|
|
11939
|
+
},
|
|
11940
|
+
message: failed.length ? "HTTP status failed in " + failed.length + " viewport(s)." : undefined,
|
|
11941
|
+
});
|
|
11942
|
+
continue;
|
|
11943
|
+
}
|
|
11711
11944
|
if (check.type === "link_status" || check.type === "artifact_link_status") {
|
|
11712
11945
|
const selector = linkStatusSelector(check);
|
|
11713
11946
|
const summaries = checkViewports.map((viewport) => summarizeLinkStatusEvidence(viewport, check));
|
|
@@ -12905,6 +13138,72 @@ function linkProbeResponseFields(response, method) {
|
|
|
12905
13138
|
content_length: contentLength,
|
|
12906
13139
|
};
|
|
12907
13140
|
}
|
|
13141
|
+
async function collectHttpStatus(check) {
|
|
13142
|
+
const url = httpStatusRequestUrl(check, page.url() || targetUrl);
|
|
13143
|
+
const method = httpStatusMethod(check);
|
|
13144
|
+
const headers = check.headers && typeof check.headers === "object" && !Array.isArray(check.headers)
|
|
13145
|
+
? Object.fromEntries(Object.entries(check.headers).map(([key, value]) => [key, String(value)]).filter(([key]) => key.trim()))
|
|
13146
|
+
: {};
|
|
13147
|
+
let body;
|
|
13148
|
+
if (check.body_json !== undefined) {
|
|
13149
|
+
body = JSON.stringify(check.body_json);
|
|
13150
|
+
if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) headers["content-type"] = "application/json";
|
|
13151
|
+
} else if (typeof check.body === "string") {
|
|
13152
|
+
body = check.body;
|
|
13153
|
+
}
|
|
13154
|
+
const bodyContains = Array.isArray(check.body_contains) ? check.body_contains.filter(Boolean) : [];
|
|
13155
|
+
const options = {
|
|
13156
|
+
method,
|
|
13157
|
+
redirect: "follow",
|
|
13158
|
+
cache: "no-store",
|
|
13159
|
+
headers,
|
|
13160
|
+
};
|
|
13161
|
+
if (body !== undefined && method !== "GET" && method !== "HEAD") options.body = body;
|
|
13162
|
+
const result = {
|
|
13163
|
+
version: "riddle-proof.http-status.v1",
|
|
13164
|
+
url,
|
|
13165
|
+
method,
|
|
13166
|
+
status: null,
|
|
13167
|
+
ok: false,
|
|
13168
|
+
error: null,
|
|
13169
|
+
request_body_bytes: typeof body === "string" ? body.length : 0,
|
|
13170
|
+
allowed_statuses: httpStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
13171
|
+
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
13172
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
13173
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
13174
|
+
};
|
|
13175
|
+
try {
|
|
13176
|
+
const response = await fetch(url, options);
|
|
13177
|
+
Object.assign(result, linkProbeResponseFields(response, method));
|
|
13178
|
+
result.url = url;
|
|
13179
|
+
result.status_text = response.statusText || "";
|
|
13180
|
+
const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0;
|
|
13181
|
+
if (shouldReadBody) {
|
|
13182
|
+
try {
|
|
13183
|
+
const buffer = await response.arrayBuffer();
|
|
13184
|
+
result.bytes = buffer.byteLength;
|
|
13185
|
+
if (bodyContains.length) {
|
|
13186
|
+
const text = new TextDecoder().decode(buffer);
|
|
13187
|
+
result.body_sample = text.slice(0, 1000);
|
|
13188
|
+
result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
|
|
13189
|
+
}
|
|
13190
|
+
} catch (error) {
|
|
13191
|
+
result.error = String(error && error.message ? error.message : error).slice(0, 500);
|
|
13192
|
+
}
|
|
13193
|
+
}
|
|
13194
|
+
result.ok = linkProbeAllowed(result.status, check)
|
|
13195
|
+
&& linkProbeContentTypeAllowed(result, check)
|
|
13196
|
+
&& (check.require_nonzero_bytes !== true || ((linkProbeObservedBytes(result) || 0) > 0))
|
|
13197
|
+
&& (!(typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || ((linkProbeObservedBytes(result) || 0) >= check.min_bytes))
|
|
13198
|
+
&& (!bodyContains.length || bodyContains.every((expected) => result.body_contains && result.body_contains[expected] === true))
|
|
13199
|
+
&& !result.error;
|
|
13200
|
+
return result;
|
|
13201
|
+
} catch (error) {
|
|
13202
|
+
result.error = String(error && error.message ? error.message : error).slice(0, 500);
|
|
13203
|
+
result.ok = false;
|
|
13204
|
+
return result;
|
|
13205
|
+
}
|
|
13206
|
+
}
|
|
12908
13207
|
async function probeLinkStatus(candidate, check) {
|
|
12909
13208
|
const requireNonzeroBytes = check.require_nonzero_bytes === true;
|
|
12910
13209
|
const minBytes = typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? Math.max(1, Math.floor(check.min_bytes)) : null;
|
|
@@ -13597,6 +13896,7 @@ async function captureViewport(viewport) {
|
|
|
13597
13896
|
const frames = {};
|
|
13598
13897
|
const text_sequences = {};
|
|
13599
13898
|
const text_matches = {};
|
|
13899
|
+
const http_statuses = {};
|
|
13600
13900
|
const link_statuses = {};
|
|
13601
13901
|
for (const check of profile.checks || []) {
|
|
13602
13902
|
if (!profileCheckAppliesToViewport(check, viewport)) continue;
|
|
@@ -13623,6 +13923,10 @@ async function captureViewport(viewport) {
|
|
|
13623
13923
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
13624
13924
|
frames[check.selector] = frames[check.selector] || await frameEvidence(check.selector);
|
|
13625
13925
|
}
|
|
13926
|
+
if (check.type === "http_status") {
|
|
13927
|
+
const key = httpStatusKey(check, page.url() || targetUrl);
|
|
13928
|
+
http_statuses[key] = http_statuses[key] || await collectHttpStatus(check);
|
|
13929
|
+
}
|
|
13626
13930
|
if (check.type === "link_status" || check.type === "artifact_link_status") {
|
|
13627
13931
|
const selector = linkStatusSelector(check);
|
|
13628
13932
|
link_statuses[selector] = link_statuses[selector] || await collectLinkStatus(check);
|
|
@@ -13690,6 +13994,7 @@ async function captureViewport(viewport) {
|
|
|
13690
13994
|
frames,
|
|
13691
13995
|
text_sequences,
|
|
13692
13996
|
text_matches,
|
|
13997
|
+
http_statuses,
|
|
13693
13998
|
link_statuses,
|
|
13694
13999
|
route_inventory: routeInventory,
|
|
13695
14000
|
setup_action_results: setupActionResults,
|
|
@@ -13739,6 +14044,19 @@ function buildProfileEvidence(currentViewports) {
|
|
|
13739
14044
|
),
|
|
13740
14045
|
})),
|
|
13741
14046
|
})),
|
|
14047
|
+
http_status: currentViewports
|
|
14048
|
+
.filter((viewport) => viewport.http_statuses && Object.keys(viewport.http_statuses).length)
|
|
14049
|
+
.map((viewport) => ({
|
|
14050
|
+
viewport: viewport.name,
|
|
14051
|
+
requests: Object.entries(viewport.http_statuses || {}).map(([key, statusSet]) => ({
|
|
14052
|
+
key,
|
|
14053
|
+
url: statusSet && statusSet.url,
|
|
14054
|
+
method: statusSet && statusSet.method,
|
|
14055
|
+
status: statusSet && statusSet.status,
|
|
14056
|
+
ok: statusSet && statusSet.ok === true,
|
|
14057
|
+
error: statusSet && statusSet.error,
|
|
14058
|
+
})),
|
|
14059
|
+
})),
|
|
13742
14060
|
link_status: currentViewports
|
|
13743
14061
|
.filter((viewport) => viewport.link_statuses && Object.keys(viewport.link_statuses).length)
|
|
13744
14062
|
.map((viewport) => ({
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
resolveRiddleProofProfileTimeoutSec,
|
|
59
59
|
slugifyRiddleProofProfileName,
|
|
60
60
|
summarizeRiddleProofProfileResult
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-W5PBTTMJ.js";
|
|
62
62
|
import {
|
|
63
63
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
64
64
|
DEFAULT_RIDDLE_API_KEY_FILE,
|