@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/{chunk-G3UY3SHZ.js → chunk-OMTWNL4B.js} +287 -14
- package/dist/cli.cjs +319 -15
- package/dist/cli.js +33 -2
- package/dist/index.cjs +287 -14
- package/dist/index.js +1 -1
- package/dist/profile.cjs +287 -14
- package/dist/profile.d.cts +7 -1
- package/dist/profile.d.ts +7 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
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
|
|
7676
|
-
const
|
|
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 =
|
|
7682
|
-
const expectedContentType =
|
|
7683
|
-
const allowedContentTypes =
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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 (!
|
|
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
|
|
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
|
|
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 =
|
|
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 (!
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
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 });
|