@riddledc/riddle-proof 0.7.93 → 0.7.95
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/README.md +25 -3
- package/dist/{chunk-QLBOFUON.js → chunk-5OCJKEHE.js} +188 -25
- package/dist/cli.cjs +203 -29
- package/dist/cli.js +16 -5
- package/dist/index.cjs +188 -25
- package/dist/index.js +1 -1
- package/dist/profile.cjs +188 -25
- package/dist/profile.d.cts +3 -1
- package/dist/profile.d.ts +3 -1
- package/dist/profile.js +1 -1
- package/dist/proof-run-engine.d.cts +3 -3
- package/dist/proof-run-engine.d.ts +3 -3
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -6913,6 +6913,8 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
6913
6913
|
"selector_count_equals",
|
|
6914
6914
|
"selector_count_equal",
|
|
6915
6915
|
"selector_count_eq",
|
|
6916
|
+
"selector_text_visible",
|
|
6917
|
+
"selector_text_absent",
|
|
6916
6918
|
"selector_text_order",
|
|
6917
6919
|
"frame_text_visible",
|
|
6918
6920
|
"frame_url_equals",
|
|
@@ -7626,7 +7628,7 @@ function normalizeCheck(input, index) {
|
|
|
7626
7628
|
if (!isSupportedCheckType(type)) {
|
|
7627
7629
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
7628
7630
|
}
|
|
7629
|
-
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && !stringValue2(input.selector)) {
|
|
7631
|
+
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue2(input.selector)) {
|
|
7630
7632
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
7631
7633
|
}
|
|
7632
7634
|
if ((type === "frame_text_visible" || type === "frame_url_equals" || type === "frame_url_matches" || type === "frame_no_horizontal_overflow") && !stringValue2(input.selector)) {
|
|
@@ -7642,7 +7644,7 @@ function normalizeCheck(input, index) {
|
|
|
7642
7644
|
if (type === "frame_url_matches" && !stringValue2(input.pattern)) {
|
|
7643
7645
|
throw new Error(`checks[${index}] frame_url_matches requires pattern.`);
|
|
7644
7646
|
}
|
|
7645
|
-
if ((type === "text_visible" || type === "text_absent") && !stringValue2(input.text) && !stringValue2(input.pattern)) {
|
|
7647
|
+
if ((type === "text_visible" || type === "text_absent" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue2(input.text) && !stringValue2(input.pattern)) {
|
|
7646
7648
|
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
7647
7649
|
}
|
|
7648
7650
|
if ((type === "url_search_param_equals" || type === "url_search_param_absent") && !stringValue2(input.param) && !stringValue2(input.search_param) && !stringValue2(input.searchParam) && !stringValue2(input.key)) {
|
|
@@ -7676,6 +7678,12 @@ function normalizeCheck(input, index) {
|
|
|
7676
7678
|
`checks[${index}] allowed_statuses`
|
|
7677
7679
|
) : void 0;
|
|
7678
7680
|
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(
|
|
7684
|
+
input.allowed_content_types ?? input.allowedContentTypes ?? input.expected_content_types ?? input.expectedContentTypes,
|
|
7685
|
+
`checks[${index}] allowed_content_types`
|
|
7686
|
+
) ?? (expectedContentType ? [expectedContentType] : void 0) : void 0;
|
|
7679
7687
|
if (isLinkStatusCheck) {
|
|
7680
7688
|
if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
|
|
7681
7689
|
throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
|
|
@@ -7717,6 +7725,8 @@ function normalizeCheck(input, index) {
|
|
|
7717
7725
|
same_origin_only: isLinkStatusCheck ? input.same_origin_only === true || input.sameOriginOnly === true : void 0,
|
|
7718
7726
|
dedupe: isLinkStatusCheck ? input.dedupe === false ? false : true : void 0,
|
|
7719
7727
|
require_nonzero_bytes: isLinkStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
|
|
7728
|
+
min_bytes: minBytes,
|
|
7729
|
+
allowed_content_types: allowedContentTypes,
|
|
7720
7730
|
allow_get_fallback: isLinkStatusCheck ? input.allow_get_fallback === false || input.allowGetFallback === false ? false : true : void 0,
|
|
7721
7731
|
max_overflow_px: numberValue(input.max_overflow_px),
|
|
7722
7732
|
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
@@ -7823,15 +7833,40 @@ function linkStatusIsAllowed(status, check) {
|
|
|
7823
7833
|
const allowed = linkStatusAllowedStatuses(check);
|
|
7824
7834
|
return allowed?.length ? allowed.includes(status) : status >= 200 && status < 400;
|
|
7825
7835
|
}
|
|
7836
|
+
function linkStatusObservedBytes(result) {
|
|
7837
|
+
const bytes = numberValue(result.bytes);
|
|
7838
|
+
const contentLength = numberValue(result.content_length);
|
|
7839
|
+
return Math.max(bytes ?? 0, contentLength ?? 0) || void 0;
|
|
7840
|
+
}
|
|
7841
|
+
function normalizeLinkStatusContentType(value) {
|
|
7842
|
+
const normalized = value?.split(";")[0]?.trim().toLowerCase();
|
|
7843
|
+
return normalized || void 0;
|
|
7844
|
+
}
|
|
7845
|
+
function linkStatusContentTypeOk(result, check) {
|
|
7846
|
+
const expected = check.allowed_content_types;
|
|
7847
|
+
if (!expected?.length) return true;
|
|
7848
|
+
const actual = normalizeLinkStatusContentType(stringValue2(result.content_type));
|
|
7849
|
+
if (!actual) return false;
|
|
7850
|
+
return expected.some((contentType) => {
|
|
7851
|
+
const normalized = normalizeLinkStatusContentType(contentType);
|
|
7852
|
+
if (!normalized) return false;
|
|
7853
|
+
if (normalized.endsWith("/*")) return actual.startsWith(normalized.slice(0, -1));
|
|
7854
|
+
return actual === normalized;
|
|
7855
|
+
});
|
|
7856
|
+
}
|
|
7826
7857
|
function linkStatusResultOk(result, check) {
|
|
7827
7858
|
const status = numberValue(result.status);
|
|
7828
7859
|
if (!linkStatusIsAllowed(status, check)) return false;
|
|
7829
7860
|
if (stringValue2(result.error)) return false;
|
|
7830
7861
|
if (result.ok === false) return false;
|
|
7862
|
+
if (!linkStatusContentTypeOk(result, check)) return false;
|
|
7831
7863
|
if (check.require_nonzero_bytes) {
|
|
7832
|
-
const
|
|
7833
|
-
|
|
7834
|
-
|
|
7864
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
7865
|
+
if (observedBytes === void 0 || observedBytes <= 0) return false;
|
|
7866
|
+
}
|
|
7867
|
+
if (check.min_bytes !== void 0) {
|
|
7868
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
7869
|
+
if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
|
|
7835
7870
|
}
|
|
7836
7871
|
return true;
|
|
7837
7872
|
}
|
|
@@ -7860,7 +7895,10 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
7860
7895
|
status: numberValue(result.status) ?? null,
|
|
7861
7896
|
method: stringValue2(result.method) ?? null,
|
|
7862
7897
|
error: stringValue2(result.error) ?? null,
|
|
7863
|
-
|
|
7898
|
+
content_type: stringValue2(result.content_type) ?? null,
|
|
7899
|
+
bytes: linkStatusObservedBytes(result) ?? null,
|
|
7900
|
+
min_bytes: check.min_bytes ?? null,
|
|
7901
|
+
allowed_content_types: check.allowed_content_types ?? null
|
|
7864
7902
|
}));
|
|
7865
7903
|
if (stringValue2(linkEvidence.error)) {
|
|
7866
7904
|
failures.push({ code: "link_status_capture_failed", error: stringValue2(linkEvidence.error) ?? "" });
|
|
@@ -7897,6 +7935,8 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
7897
7935
|
failed_count: failures.length,
|
|
7898
7936
|
truncated: linkEvidence.truncated === true,
|
|
7899
7937
|
max_links: numberValue(linkEvidence.max_links) ?? check.max_links ?? 100,
|
|
7938
|
+
min_bytes: check.min_bytes ?? null,
|
|
7939
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
7900
7940
|
status_counts: statusCounts,
|
|
7901
7941
|
failures: failures.slice(0, 20)
|
|
7902
7942
|
};
|
|
@@ -7908,9 +7948,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
7908
7948
|
const key = selectorKey(check);
|
|
7909
7949
|
const sequence = viewport.text_sequences?.[key];
|
|
7910
7950
|
if (isRecord(sequence)) {
|
|
7951
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
7952
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
7911
7953
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
7912
7954
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
7913
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
7955
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
7914
7956
|
return candidates.map((text) => String(text).replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
7915
7957
|
}
|
|
7916
7958
|
return [];
|
|
@@ -8244,6 +8286,35 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
8244
8286
|
message: failed.length ? `Selector ${key} count did not equal ${expectedCount} in ${failed.length} viewport(s).` : void 0
|
|
8245
8287
|
};
|
|
8246
8288
|
}
|
|
8289
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
8290
|
+
const key = selectorKey(check);
|
|
8291
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
8292
|
+
const results = viewports.map((viewport) => {
|
|
8293
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
8294
|
+
const matches = texts.filter((text) => matchText(text, check));
|
|
8295
|
+
return {
|
|
8296
|
+
viewport: viewport.name,
|
|
8297
|
+
selector_count: viewport.selectors?.[key]?.count || 0,
|
|
8298
|
+
visible_count: viewport.selectors?.[key]?.visible_count || 0,
|
|
8299
|
+
matched_count: matches.length,
|
|
8300
|
+
matched: matches.length > 0,
|
|
8301
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240))
|
|
8302
|
+
};
|
|
8303
|
+
});
|
|
8304
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
8305
|
+
return {
|
|
8306
|
+
type: check.type,
|
|
8307
|
+
label: checkLabel(check),
|
|
8308
|
+
status: failed ? "failed" : "passed",
|
|
8309
|
+
evidence: {
|
|
8310
|
+
selector: key,
|
|
8311
|
+
text: check.text || null,
|
|
8312
|
+
pattern: check.pattern || null,
|
|
8313
|
+
viewports: results.map((result) => toJsonValue(result))
|
|
8314
|
+
},
|
|
8315
|
+
message: failed ? `Selector ${key} text assertion failed in ${failed} viewport(s).` : void 0
|
|
8316
|
+
};
|
|
8317
|
+
}
|
|
8247
8318
|
if (check.type === "selector_text_order") {
|
|
8248
8319
|
const key = selectorKey(check);
|
|
8249
8320
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -8254,7 +8325,7 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
8254
8325
|
viewport: viewport.name,
|
|
8255
8326
|
matched: match.matched,
|
|
8256
8327
|
matched_positions: match.positions,
|
|
8257
|
-
visible_texts: texts.slice(0, 20)
|
|
8328
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240))
|
|
8258
8329
|
};
|
|
8259
8330
|
});
|
|
8260
8331
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -8394,6 +8465,8 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
8394
8465
|
min_count: check.min_count ?? null,
|
|
8395
8466
|
allowed_statuses: linkStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
8396
8467
|
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
8468
|
+
min_bytes: check.min_bytes ?? null,
|
|
8469
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
8397
8470
|
viewports: summaries.map((summary) => toJsonValue(summary)),
|
|
8398
8471
|
failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue2(summary.viewport) ?? null, failure })) : [])
|
|
8399
8472
|
},
|
|
@@ -8948,9 +9021,11 @@ function textSequenceForCheck(viewport, check) {
|
|
|
8948
9021
|
const key = check.selector || "";
|
|
8949
9022
|
const sequence = viewport.text_sequences && viewport.text_sequences[key];
|
|
8950
9023
|
if (sequence && typeof sequence === "object") {
|
|
9024
|
+
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
9025
|
+
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
8951
9026
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
8952
9027
|
const texts = Array.isArray(sequence.texts) ? sequence.texts : [];
|
|
8953
|
-
const candidates = visibleTexts.length ? visibleTexts : texts;
|
|
9028
|
+
const candidates = visibleMatchTexts.length ? visibleMatchTexts : matchTexts.length ? matchTexts : visibleTexts.length ? visibleTexts : texts;
|
|
8954
9029
|
return candidates.map((text) => String(text || "").replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
8955
9030
|
}
|
|
8956
9031
|
return [];
|
|
@@ -8979,15 +9054,41 @@ function linkStatusIsAllowed(status, check) {
|
|
|
8979
9054
|
const allowed = linkStatusAllowedStatuses(check);
|
|
8980
9055
|
return Array.isArray(allowed) && allowed.length ? allowed.includes(status) : status >= 200 && status < 400;
|
|
8981
9056
|
}
|
|
9057
|
+
function linkStatusObservedBytes(result) {
|
|
9058
|
+
const bytes = typeof result.bytes === "number" && Number.isFinite(result.bytes) ? result.bytes : undefined;
|
|
9059
|
+
const contentLength = typeof result.content_length === "number" && Number.isFinite(result.content_length) ? result.content_length : undefined;
|
|
9060
|
+
const observed = Math.max(bytes || 0, contentLength || 0);
|
|
9061
|
+
return observed > 0 ? observed : undefined;
|
|
9062
|
+
}
|
|
9063
|
+
function normalizeLinkStatusContentType(value) {
|
|
9064
|
+
if (typeof value !== "string") return undefined;
|
|
9065
|
+
const normalized = (value.split(";")[0] || "").trim().toLowerCase();
|
|
9066
|
+
return normalized || undefined;
|
|
9067
|
+
}
|
|
9068
|
+
function linkStatusContentTypeOk(result, check) {
|
|
9069
|
+
if (!Array.isArray(check.allowed_content_types) || !check.allowed_content_types.length) return true;
|
|
9070
|
+
const actual = normalizeLinkStatusContentType(result.content_type);
|
|
9071
|
+
if (!actual) return false;
|
|
9072
|
+
return check.allowed_content_types.some((contentType) => {
|
|
9073
|
+
const normalized = normalizeLinkStatusContentType(contentType);
|
|
9074
|
+
if (!normalized) return false;
|
|
9075
|
+
if (normalized.endsWith("/*")) return actual.startsWith(normalized.slice(0, -1));
|
|
9076
|
+
return actual === normalized;
|
|
9077
|
+
});
|
|
9078
|
+
}
|
|
8982
9079
|
function linkStatusResultOk(result, check) {
|
|
8983
9080
|
if (!result || typeof result !== "object" || Array.isArray(result)) return false;
|
|
8984
9081
|
if (!linkStatusIsAllowed(result.status, check)) return false;
|
|
8985
9082
|
if (typeof result.error === "string" && result.error.trim()) return false;
|
|
8986
9083
|
if (result.ok === false) return false;
|
|
9084
|
+
if (!linkStatusContentTypeOk(result, check)) return false;
|
|
8987
9085
|
if (check.require_nonzero_bytes === true) {
|
|
8988
|
-
const
|
|
8989
|
-
|
|
8990
|
-
|
|
9086
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
9087
|
+
if (observedBytes === undefined || observedBytes <= 0) return false;
|
|
9088
|
+
}
|
|
9089
|
+
if (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) {
|
|
9090
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
9091
|
+
if (observedBytes === undefined || observedBytes < check.min_bytes) return false;
|
|
8991
9092
|
}
|
|
8992
9093
|
return true;
|
|
8993
9094
|
}
|
|
@@ -9015,11 +9116,10 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
9015
9116
|
status: typeof result.status === "number" && Number.isFinite(result.status) ? result.status : null,
|
|
9016
9117
|
method: typeof result.method === "string" ? result.method : null,
|
|
9017
9118
|
error: typeof result.error === "string" ? result.error : null,
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
: null,
|
|
9119
|
+
content_type: typeof result.content_type === "string" ? result.content_type : null,
|
|
9120
|
+
bytes: linkStatusObservedBytes(result) ?? null,
|
|
9121
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
9122
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
9023
9123
|
}));
|
|
9024
9124
|
if (typeof linkEvidence.error === "string" && linkEvidence.error.trim()) {
|
|
9025
9125
|
failures.push({ code: "link_status_capture_failed", error: linkEvidence.error });
|
|
@@ -9054,6 +9154,8 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
9054
9154
|
failed_count: failures.length,
|
|
9055
9155
|
truncated: linkEvidence.truncated === true,
|
|
9056
9156
|
max_links: typeof linkEvidence.max_links === "number" ? linkEvidence.max_links : check.max_links || 100,
|
|
9157
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
9158
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
9057
9159
|
status_counts: linkEvidence.status_counts && typeof linkEvidence.status_counts === "object" && !Array.isArray(linkEvidence.status_counts) ? linkEvidence.status_counts : {},
|
|
9058
9160
|
failures: failures.slice(0, 20),
|
|
9059
9161
|
};
|
|
@@ -9588,6 +9690,31 @@ function assessProfile(profile, evidence) {
|
|
|
9588
9690
|
});
|
|
9589
9691
|
continue;
|
|
9590
9692
|
}
|
|
9693
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
9694
|
+
const selector = check.selector || "";
|
|
9695
|
+
const expectedVisible = check.type === "selector_text_visible";
|
|
9696
|
+
const results = checkViewports.map((viewport) => {
|
|
9697
|
+
const texts = textSequenceForCheck(viewport, check);
|
|
9698
|
+
const matches = texts.filter((text) => textMatches(text, check));
|
|
9699
|
+
return {
|
|
9700
|
+
viewport: viewport.name,
|
|
9701
|
+
selector_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0,
|
|
9702
|
+
visible_count: viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0,
|
|
9703
|
+
matched_count: matches.length,
|
|
9704
|
+
matched: matches.length > 0,
|
|
9705
|
+
samples: matches.slice(0, 3).map((text) => text.slice(0, 240)),
|
|
9706
|
+
};
|
|
9707
|
+
});
|
|
9708
|
+
const failed = results.filter((result) => result.matched !== expectedVisible).length;
|
|
9709
|
+
checks.push({
|
|
9710
|
+
type: check.type,
|
|
9711
|
+
label: check.label || check.type,
|
|
9712
|
+
status: failed ? "failed" : "passed",
|
|
9713
|
+
evidence: { selector, text: check.text || null, pattern: check.pattern || null, viewports: results },
|
|
9714
|
+
message: failed ? "Selector " + selector + " text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
9715
|
+
});
|
|
9716
|
+
continue;
|
|
9717
|
+
}
|
|
9591
9718
|
if (check.type === "selector_text_order") {
|
|
9592
9719
|
const selector = check.selector || "";
|
|
9593
9720
|
const expectedTexts = check.expected_texts || [];
|
|
@@ -9598,7 +9725,7 @@ function assessProfile(profile, evidence) {
|
|
|
9598
9725
|
viewport: viewport.name,
|
|
9599
9726
|
matched: match.matched,
|
|
9600
9727
|
matched_positions: match.positions,
|
|
9601
|
-
visible_texts: texts.slice(0, 20),
|
|
9728
|
+
visible_texts: texts.slice(0, 20).map((text) => text.slice(0, 240)),
|
|
9602
9729
|
};
|
|
9603
9730
|
});
|
|
9604
9731
|
const failed = results.filter((result) => !result.matched).length;
|
|
@@ -9725,6 +9852,8 @@ function assessProfile(profile, evidence) {
|
|
|
9725
9852
|
min_count: check.min_count ?? null,
|
|
9726
9853
|
allowed_statuses: linkStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
9727
9854
|
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
9855
|
+
min_bytes: check.min_bytes ?? null,
|
|
9856
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
9728
9857
|
viewports: summaries,
|
|
9729
9858
|
failures: failed.flatMap((summary) => Array.isArray(summary.failures)
|
|
9730
9859
|
? summary.failures.map((failure) => ({ viewport: summary.viewport || null, failure }))
|
|
@@ -10802,6 +10931,7 @@ async function selectorStats(selector) {
|
|
|
10802
10931
|
async function selectorTextSequence(selector) {
|
|
10803
10932
|
return page.locator(selector).evaluateAll((elements) => {
|
|
10804
10933
|
const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
10934
|
+
const matchText = (value) => String(value || "").replace(/\s+/g, " ").trim().slice(0, 8000);
|
|
10805
10935
|
const isVisible = (element) => {
|
|
10806
10936
|
const style = window.getComputedStyle(element);
|
|
10807
10937
|
const rect = element.getBoundingClientRect();
|
|
@@ -10810,6 +10940,7 @@ async function selectorTextSequence(selector) {
|
|
|
10810
10940
|
const rows = elements.map((element, index) => ({
|
|
10811
10941
|
index,
|
|
10812
10942
|
text: compact(element.innerText || element.textContent || ""),
|
|
10943
|
+
match_text: matchText(element.innerText || element.textContent || ""),
|
|
10813
10944
|
visible: isVisible(element),
|
|
10814
10945
|
}));
|
|
10815
10946
|
return {
|
|
@@ -10817,8 +10948,10 @@ async function selectorTextSequence(selector) {
|
|
|
10817
10948
|
visible_count: rows.filter((row) => row.visible).length,
|
|
10818
10949
|
texts: rows.map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
10819
10950
|
visible_texts: rows.filter((row) => row.visible).map((row) => row.text).filter(Boolean).slice(0, 40),
|
|
10951
|
+
match_texts: rows.map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
10952
|
+
visible_match_texts: rows.filter((row) => row.visible).map((row) => row.match_text).filter(Boolean).slice(0, 40),
|
|
10820
10953
|
};
|
|
10821
|
-
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
10954
|
+
}).catch((error) => ({ count: 0, visible_count: 0, texts: [], visible_texts: [], match_texts: [], visible_match_texts: [], error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
10822
10955
|
}
|
|
10823
10956
|
function linkProbeMaxLinks(check) {
|
|
10824
10957
|
const value = Number(check.max_links || check.maxLinks || check.limit || 100);
|
|
@@ -10868,6 +11001,28 @@ function linkProbeAllowed(status, check) {
|
|
|
10868
11001
|
: null;
|
|
10869
11002
|
return allowed ? allowed.includes(status) : status >= 200 && status < 400;
|
|
10870
11003
|
}
|
|
11004
|
+
function linkProbeObservedBytes(result) {
|
|
11005
|
+
const bytes = typeof result.bytes === "number" && Number.isFinite(result.bytes) ? result.bytes : undefined;
|
|
11006
|
+
const contentLength = typeof result.content_length === "number" && Number.isFinite(result.content_length) ? result.content_length : undefined;
|
|
11007
|
+
const observed = Math.max(bytes || 0, contentLength || 0);
|
|
11008
|
+
return observed > 0 ? observed : undefined;
|
|
11009
|
+
}
|
|
11010
|
+
function normalizeLinkProbeContentType(value) {
|
|
11011
|
+
if (typeof value !== "string") return undefined;
|
|
11012
|
+
const normalized = (value.split(";")[0] || "").trim().toLowerCase();
|
|
11013
|
+
return normalized || undefined;
|
|
11014
|
+
}
|
|
11015
|
+
function linkProbeContentTypeAllowed(result, check) {
|
|
11016
|
+
if (!Array.isArray(check.allowed_content_types) || !check.allowed_content_types.length) return true;
|
|
11017
|
+
const actual = normalizeLinkProbeContentType(result.content_type);
|
|
11018
|
+
if (!actual) return false;
|
|
11019
|
+
return check.allowed_content_types.some((contentType) => {
|
|
11020
|
+
const normalized = normalizeLinkProbeContentType(contentType);
|
|
11021
|
+
if (!normalized) return false;
|
|
11022
|
+
if (normalized.endsWith("/*")) return actual.startsWith(normalized.slice(0, -1));
|
|
11023
|
+
return actual === normalized;
|
|
11024
|
+
});
|
|
11025
|
+
}
|
|
10871
11026
|
function linkProbeResponseFields(response, method) {
|
|
10872
11027
|
const contentLengthHeader = response.headers && typeof response.headers.get === "function" ? response.headers.get("content-length") : null;
|
|
10873
11028
|
const contentLength = contentLengthHeader && /^\d+$/.test(contentLengthHeader) ? Number(contentLengthHeader) : null;
|
|
@@ -10882,6 +11037,7 @@ function linkProbeResponseFields(response, method) {
|
|
|
10882
11037
|
}
|
|
10883
11038
|
async function probeLinkStatus(candidate, check) {
|
|
10884
11039
|
const requireNonzeroBytes = check.require_nonzero_bytes === true;
|
|
11040
|
+
const minBytes = typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? Math.max(1, Math.floor(check.min_bytes)) : null;
|
|
10885
11041
|
const allowGetFallback = check.allow_get_fallback !== false;
|
|
10886
11042
|
const result = {
|
|
10887
11043
|
url: candidate.url,
|
|
@@ -10899,7 +11055,7 @@ async function probeLinkStatus(candidate, check) {
|
|
|
10899
11055
|
};
|
|
10900
11056
|
const applyResponse = async (response, method, readBytes) => {
|
|
10901
11057
|
Object.assign(result, linkProbeResponseFields(response, method));
|
|
10902
|
-
if (readBytes
|
|
11058
|
+
if (readBytes) {
|
|
10903
11059
|
try {
|
|
10904
11060
|
const buffer = await response.arrayBuffer();
|
|
10905
11061
|
result.bytes = buffer.byteLength;
|
|
@@ -10908,7 +11064,9 @@ async function probeLinkStatus(candidate, check) {
|
|
|
10908
11064
|
}
|
|
10909
11065
|
}
|
|
10910
11066
|
result.ok = linkProbeAllowed(result.status, check)
|
|
10911
|
-
&& (
|
|
11067
|
+
&& linkProbeContentTypeAllowed(result, check)
|
|
11068
|
+
&& (!requireNonzeroBytes || ((linkProbeObservedBytes(result) || 0) > 0))
|
|
11069
|
+
&& (minBytes === null || ((linkProbeObservedBytes(result) || 0) >= minBytes))
|
|
10912
11070
|
&& !result.error;
|
|
10913
11071
|
};
|
|
10914
11072
|
try {
|
|
@@ -10925,9 +11083,9 @@ async function probeLinkStatus(candidate, check) {
|
|
|
10925
11083
|
method: "GET",
|
|
10926
11084
|
redirect: "follow",
|
|
10927
11085
|
cache: "no-store",
|
|
10928
|
-
headers: { Range: "bytes=0-
|
|
11086
|
+
headers: { Range: "bytes=0-" + String((minBytes || 1) - 1) },
|
|
10929
11087
|
});
|
|
10930
|
-
await applyResponse(response, "GET", requireNonzeroBytes);
|
|
11088
|
+
await applyResponse(response, "GET", requireNonzeroBytes || minBytes !== null);
|
|
10931
11089
|
return result;
|
|
10932
11090
|
} catch (error) {
|
|
10933
11091
|
result.error = String(error && error.message ? error.message : error).slice(0, 500);
|
|
@@ -10981,7 +11139,10 @@ async function collectLinkStatus(check) {
|
|
|
10981
11139
|
status: result.status,
|
|
10982
11140
|
method: result.method,
|
|
10983
11141
|
error: result.error,
|
|
10984
|
-
|
|
11142
|
+
content_type: result.content_type,
|
|
11143
|
+
bytes: linkProbeObservedBytes(result) || null,
|
|
11144
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
11145
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
10985
11146
|
}));
|
|
10986
11147
|
const statusCounts = {};
|
|
10987
11148
|
for (const result of results) {
|
|
@@ -10995,6 +11156,8 @@ async function collectLinkStatus(check) {
|
|
|
10995
11156
|
same_origin_only: linkProbeSameOriginOnly(check),
|
|
10996
11157
|
dedupe: linkProbeDedupe(check),
|
|
10997
11158
|
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
11159
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
11160
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
10998
11161
|
allowed_statuses: Array.isArray(check.allowed_statuses) && check.allowed_statuses.length
|
|
10999
11162
|
? check.allowed_statuses
|
|
11000
11163
|
: typeof check.expected_status === "number"
|
|
@@ -11554,7 +11717,7 @@ async function captureViewport(viewport) {
|
|
|
11554
11717
|
) {
|
|
11555
11718
|
selectors[check.selector] = await selectorStats(check.selector);
|
|
11556
11719
|
}
|
|
11557
|
-
if (check.type === "selector_text_order" && check.selector) {
|
|
11720
|
+
if ((check.type === "selector_text_order" || check.type === "selector_text_visible" || check.type === "selector_text_absent") && check.selector) {
|
|
11558
11721
|
selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
|
|
11559
11722
|
text_sequences[check.selector] = await selectorTextSequence(check.selector);
|
|
11560
11723
|
}
|
|
@@ -12181,6 +12344,11 @@ function profileCheckMarkdownTarget(check) {
|
|
|
12181
12344
|
const expectedCount = cliFiniteNumber(evidence.expected_count);
|
|
12182
12345
|
return selector && expectedCount !== void 0 ? `${markdownInlineCode(selector)} = ${expectedCount}` : void 0;
|
|
12183
12346
|
}
|
|
12347
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
12348
|
+
const textTarget = profileCheckTextTarget(evidence);
|
|
12349
|
+
if (selector && textTarget) return `${markdownInlineCode(selector)} contains ${textTarget}`;
|
|
12350
|
+
return selector ? markdownInlineCode(selector) : textTarget;
|
|
12351
|
+
}
|
|
12184
12352
|
if (check.type === "selector_text_order") {
|
|
12185
12353
|
return selector ? `${markdownInlineCode(selector)} text order` : void 0;
|
|
12186
12354
|
}
|
|
@@ -12210,9 +12378,13 @@ function profileCheckMarkdownTarget(check) {
|
|
|
12210
12378
|
if (check.type === "link_status" || check.type === "artifact_link_status") {
|
|
12211
12379
|
const expectedCount = cliFiniteNumber(evidence.expected_count);
|
|
12212
12380
|
const minCount = cliFiniteNumber(evidence.min_count);
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
|
|
12381
|
+
const minBytes = cliFiniteNumber(evidence.min_bytes);
|
|
12382
|
+
const parts = [];
|
|
12383
|
+
if (expectedCount !== void 0) parts.push(`links = ${expectedCount}`);
|
|
12384
|
+
else if (minCount !== void 0) parts.push(`links >= ${minCount}`);
|
|
12385
|
+
if (minBytes !== void 0) parts.push(`bytes >= ${minBytes}`);
|
|
12386
|
+
if (selector && parts.length) return `${markdownInlineCode(selector)} ${parts.join(", ")}`;
|
|
12387
|
+
return selector ? markdownInlineCode(selector) : parts.join(", ") || void 0;
|
|
12216
12388
|
}
|
|
12217
12389
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
12218
12390
|
const maxOverflow = cliFiniteNumber(evidence.max_overflow_px);
|
|
@@ -12420,9 +12592,11 @@ function profileLinkStatusSummaryMarkdown(result) {
|
|
|
12420
12592
|
const okTotal = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.ok_count) || 0), 0);
|
|
12421
12593
|
const failedTotal = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.failed_count) || 0), 0);
|
|
12422
12594
|
const truncatedTotal = viewports.filter((viewport) => viewport.truncated === true).length;
|
|
12595
|
+
const minBytes = cliFiniteNumber(evidence.min_bytes);
|
|
12596
|
+
const allowedContentTypes = Array.isArray(evidence.allowed_content_types) ? evidence.allowed_content_types.map(cliString).filter((value) => Boolean(value)) : [];
|
|
12423
12597
|
const countText = totals.length ? totals.join("/") : "unknown";
|
|
12424
12598
|
lines.push(
|
|
12425
|
-
`- ${label}${selector ? ` ${markdownInlineCode(selector)}` : ""}: links ${countText}, ok ${okTotal}, failures ${failedTotal}${truncatedTotal ? `, truncated viewports ${truncatedTotal}` : ""}`
|
|
12599
|
+
`- ${label}${selector ? ` ${markdownInlineCode(selector)}` : ""}: links ${countText}, ok ${okTotal}, failures ${failedTotal}${truncatedTotal ? `, truncated viewports ${truncatedTotal}` : ""}${minBytes !== void 0 ? `, min bytes ${minBytes}` : ""}${allowedContentTypes.length ? `, content types ${allowedContentTypes.map((value) => markdownInlineCode(value)).join(", ")}` : ""}`
|
|
12426
12600
|
);
|
|
12427
12601
|
}
|
|
12428
12602
|
return lines;
|
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-5OCJKEHE.js";
|
|
14
14
|
import {
|
|
15
15
|
createRiddleApiClient,
|
|
16
16
|
parseRiddleViewport
|
|
@@ -389,6 +389,11 @@ function profileCheckMarkdownTarget(check) {
|
|
|
389
389
|
const expectedCount = cliFiniteNumber(evidence.expected_count);
|
|
390
390
|
return selector && expectedCount !== void 0 ? `${markdownInlineCode(selector)} = ${expectedCount}` : void 0;
|
|
391
391
|
}
|
|
392
|
+
if (check.type === "selector_text_visible" || check.type === "selector_text_absent") {
|
|
393
|
+
const textTarget = profileCheckTextTarget(evidence);
|
|
394
|
+
if (selector && textTarget) return `${markdownInlineCode(selector)} contains ${textTarget}`;
|
|
395
|
+
return selector ? markdownInlineCode(selector) : textTarget;
|
|
396
|
+
}
|
|
392
397
|
if (check.type === "selector_text_order") {
|
|
393
398
|
return selector ? `${markdownInlineCode(selector)} text order` : void 0;
|
|
394
399
|
}
|
|
@@ -418,9 +423,13 @@ function profileCheckMarkdownTarget(check) {
|
|
|
418
423
|
if (check.type === "link_status" || check.type === "artifact_link_status") {
|
|
419
424
|
const expectedCount = cliFiniteNumber(evidence.expected_count);
|
|
420
425
|
const minCount = cliFiniteNumber(evidence.min_count);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
426
|
+
const minBytes = cliFiniteNumber(evidence.min_bytes);
|
|
427
|
+
const parts = [];
|
|
428
|
+
if (expectedCount !== void 0) parts.push(`links = ${expectedCount}`);
|
|
429
|
+
else if (minCount !== void 0) parts.push(`links >= ${minCount}`);
|
|
430
|
+
if (minBytes !== void 0) parts.push(`bytes >= ${minBytes}`);
|
|
431
|
+
if (selector && parts.length) return `${markdownInlineCode(selector)} ${parts.join(", ")}`;
|
|
432
|
+
return selector ? markdownInlineCode(selector) : parts.join(", ") || void 0;
|
|
424
433
|
}
|
|
425
434
|
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
426
435
|
const maxOverflow = cliFiniteNumber(evidence.max_overflow_px);
|
|
@@ -628,9 +637,11 @@ function profileLinkStatusSummaryMarkdown(result) {
|
|
|
628
637
|
const okTotal = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.ok_count) || 0), 0);
|
|
629
638
|
const failedTotal = viewports.reduce((sum, viewport) => sum + (cliFiniteNumber(viewport.failed_count) || 0), 0);
|
|
630
639
|
const truncatedTotal = viewports.filter((viewport) => viewport.truncated === true).length;
|
|
640
|
+
const minBytes = cliFiniteNumber(evidence.min_bytes);
|
|
641
|
+
const allowedContentTypes = Array.isArray(evidence.allowed_content_types) ? evidence.allowed_content_types.map(cliString).filter((value) => Boolean(value)) : [];
|
|
631
642
|
const countText = totals.length ? totals.join("/") : "unknown";
|
|
632
643
|
lines.push(
|
|
633
|
-
`- ${label}${selector ? ` ${markdownInlineCode(selector)}` : ""}: links ${countText}, ok ${okTotal}, failures ${failedTotal}${truncatedTotal ? `, truncated viewports ${truncatedTotal}` : ""}`
|
|
644
|
+
`- ${label}${selector ? ` ${markdownInlineCode(selector)}` : ""}: links ${countText}, ok ${okTotal}, failures ${failedTotal}${truncatedTotal ? `, truncated viewports ${truncatedTotal}` : ""}${minBytes !== void 0 ? `, min bytes ${minBytes}` : ""}${allowedContentTypes.length ? `, content types ${allowedContentTypes.map((value) => markdownInlineCode(value)).join(", ")}` : ""}`
|
|
634
645
|
);
|
|
635
646
|
}
|
|
636
647
|
return lines;
|