@riddledc/riddle-proof 0.7.94 → 0.7.96
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 +8 -3
- package/dist/{chunk-FQ4QBY2N.js → chunk-G3UY3SHZ.js} +160 -18
- package/dist/cli.cjs +171 -22
- package/dist/cli.js +12 -5
- package/dist/index.cjs +160 -18
- package/dist/index.js +1 -1
- package/dist/profile.cjs +160 -18
- package/dist/profile.d.cts +2 -0
- package/dist/profile.d.ts +2 -0
- package/dist/profile.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -479,6 +479,8 @@ artifact links should fail the profile:
|
|
|
479
479
|
"expected_count": 88,
|
|
480
480
|
"same_origin_only": true,
|
|
481
481
|
"require_nonzero_bytes": true,
|
|
482
|
+
"min_bytes": 32,
|
|
483
|
+
"allowed_content_types": ["image/*", "application/json"],
|
|
482
484
|
"max_links": 150
|
|
483
485
|
}
|
|
484
486
|
```
|
|
@@ -486,9 +488,12 @@ artifact links should fail the profile:
|
|
|
486
488
|
The check defaults to `a[href]`, deduplicates URLs, probes up to 100 selected
|
|
487
489
|
URLs, and treats HTTP `2xx` / `3xx` responses as healthy. Use
|
|
488
490
|
`allowed_statuses` or `expected_status` when a narrower status contract is
|
|
489
|
-
intentional, `min_count` for lower-bound audits,
|
|
490
|
-
|
|
491
|
-
|
|
491
|
+
intentional, `min_count` for lower-bound audits, `min_bytes` when a one-byte
|
|
492
|
+
range response is too weak, `allowed_content_types` for MIME checks, and
|
|
493
|
+
`max_links` when the selected set is intentionally larger than 100.
|
|
494
|
+
`allowed_content_types` accepts exact types and family wildcards such as
|
|
495
|
+
`image/*`. `artifact_link_status` is an alias with the same behavior for
|
|
496
|
+
profiles that want artifact-specific wording.
|
|
492
497
|
|
|
493
498
|
Use the `route_inventory` check for source-page route coverage audits where a
|
|
494
499
|
navigation surface must expose a known set of routes and each route must load
|
|
@@ -785,6 +785,12 @@ function normalizeCheck(input, index) {
|
|
|
785
785
|
`checks[${index}] allowed_statuses`
|
|
786
786
|
) : void 0;
|
|
787
787
|
const maxLinks = isLinkStatusCheck ? normalizePositiveInteger(input.max_links ?? input.maxLinks ?? input.limit, `checks[${index}] max_links`, 500) : void 0;
|
|
788
|
+
const minBytes = isLinkStatusCheck ? normalizePositiveInteger(input.min_bytes ?? input.minBytes ?? input.min_response_bytes ?? input.minResponseBytes, `checks[${index}] min_bytes`) : void 0;
|
|
789
|
+
const expectedContentType = isLinkStatusCheck ? stringValue(input.expected_content_type) || stringValue(input.expectedContentType) || stringValue(input.content_type) || stringValue(input.contentType) : void 0;
|
|
790
|
+
const allowedContentTypes = isLinkStatusCheck ? normalizeStringList(
|
|
791
|
+
input.allowed_content_types ?? input.allowedContentTypes ?? input.expected_content_types ?? input.expectedContentTypes,
|
|
792
|
+
`checks[${index}] allowed_content_types`
|
|
793
|
+
) ?? (expectedContentType ? [expectedContentType] : void 0) : void 0;
|
|
788
794
|
if (isLinkStatusCheck) {
|
|
789
795
|
if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
|
|
790
796
|
throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
|
|
@@ -826,6 +832,8 @@ function normalizeCheck(input, index) {
|
|
|
826
832
|
same_origin_only: isLinkStatusCheck ? input.same_origin_only === true || input.sameOriginOnly === true : void 0,
|
|
827
833
|
dedupe: isLinkStatusCheck ? input.dedupe === false ? false : true : void 0,
|
|
828
834
|
require_nonzero_bytes: isLinkStatusCheck ? input.require_nonzero_bytes === true || input.requireNonzeroBytes === true : void 0,
|
|
835
|
+
min_bytes: minBytes,
|
|
836
|
+
allowed_content_types: allowedContentTypes,
|
|
829
837
|
allow_get_fallback: isLinkStatusCheck ? input.allow_get_fallback === false || input.allowGetFallback === false ? false : true : void 0,
|
|
830
838
|
max_overflow_px: numberValue(input.max_overflow_px),
|
|
831
839
|
timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
|
|
@@ -932,15 +940,40 @@ function linkStatusIsAllowed(status, check) {
|
|
|
932
940
|
const allowed = linkStatusAllowedStatuses(check);
|
|
933
941
|
return allowed?.length ? allowed.includes(status) : status >= 200 && status < 400;
|
|
934
942
|
}
|
|
943
|
+
function linkStatusObservedBytes(result) {
|
|
944
|
+
const bytes = numberValue(result.bytes);
|
|
945
|
+
const contentLength = numberValue(result.content_length);
|
|
946
|
+
return Math.max(bytes ?? 0, contentLength ?? 0) || void 0;
|
|
947
|
+
}
|
|
948
|
+
function normalizeLinkStatusContentType(value) {
|
|
949
|
+
const normalized = value?.split(";")[0]?.trim().toLowerCase();
|
|
950
|
+
return normalized || void 0;
|
|
951
|
+
}
|
|
952
|
+
function linkStatusContentTypeOk(result, check) {
|
|
953
|
+
const expected = check.allowed_content_types;
|
|
954
|
+
if (!expected?.length) return true;
|
|
955
|
+
const actual = normalizeLinkStatusContentType(stringValue(result.content_type));
|
|
956
|
+
if (!actual) return false;
|
|
957
|
+
return expected.some((contentType) => {
|
|
958
|
+
const normalized = normalizeLinkStatusContentType(contentType);
|
|
959
|
+
if (!normalized) return false;
|
|
960
|
+
if (normalized.endsWith("/*")) return actual.startsWith(normalized.slice(0, -1));
|
|
961
|
+
return actual === normalized;
|
|
962
|
+
});
|
|
963
|
+
}
|
|
935
964
|
function linkStatusResultOk(result, check) {
|
|
936
965
|
const status = numberValue(result.status);
|
|
937
966
|
if (!linkStatusIsAllowed(status, check)) return false;
|
|
938
967
|
if (stringValue(result.error)) return false;
|
|
939
968
|
if (result.ok === false) return false;
|
|
969
|
+
if (!linkStatusContentTypeOk(result, check)) return false;
|
|
940
970
|
if (check.require_nonzero_bytes) {
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
971
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
972
|
+
if (observedBytes === void 0 || observedBytes <= 0) return false;
|
|
973
|
+
}
|
|
974
|
+
if (check.min_bytes !== void 0) {
|
|
975
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
976
|
+
if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
|
|
944
977
|
}
|
|
945
978
|
return true;
|
|
946
979
|
}
|
|
@@ -962,6 +995,10 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
962
995
|
}
|
|
963
996
|
const results = Array.isArray(linkEvidence.results) ? linkEvidence.results.filter(isRecord) : [];
|
|
964
997
|
const totalCount = numberValue(linkEvidence.total_count) ?? results.length;
|
|
998
|
+
const resultCount = numberValue(linkEvidence.result_count) ?? totalCount;
|
|
999
|
+
const storedResultCount = numberValue(linkEvidence.stored_result_count) ?? results.length;
|
|
1000
|
+
const omittedResultCount = numberValue(linkEvidence.omitted_result_count) ?? Math.max(0, resultCount - storedResultCount);
|
|
1001
|
+
const omittedSuccessCount = numberValue(linkEvidence.omitted_success_count) ?? 0;
|
|
965
1002
|
const okCount = results.filter((result) => linkStatusResultOk(result, check)).length;
|
|
966
1003
|
const failures = results.filter((result) => !linkStatusResultOk(result, check)).map((result) => ({
|
|
967
1004
|
code: "link_status_failed",
|
|
@@ -969,7 +1006,10 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
969
1006
|
status: numberValue(result.status) ?? null,
|
|
970
1007
|
method: stringValue(result.method) ?? null,
|
|
971
1008
|
error: stringValue(result.error) ?? null,
|
|
972
|
-
|
|
1009
|
+
content_type: stringValue(result.content_type) ?? null,
|
|
1010
|
+
bytes: linkStatusObservedBytes(result) ?? null,
|
|
1011
|
+
min_bytes: check.min_bytes ?? null,
|
|
1012
|
+
allowed_content_types: check.allowed_content_types ?? null
|
|
973
1013
|
}));
|
|
974
1014
|
if (stringValue(linkEvidence.error)) {
|
|
975
1015
|
failures.push({ code: "link_status_capture_failed", error: stringValue(linkEvidence.error) ?? "" });
|
|
@@ -1006,6 +1046,13 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
1006
1046
|
failed_count: failures.length,
|
|
1007
1047
|
truncated: linkEvidence.truncated === true,
|
|
1008
1048
|
max_links: numberValue(linkEvidence.max_links) ?? check.max_links ?? 100,
|
|
1049
|
+
result_count: resultCount,
|
|
1050
|
+
stored_result_count: storedResultCount,
|
|
1051
|
+
omitted_result_count: omittedResultCount,
|
|
1052
|
+
omitted_success_count: omittedSuccessCount,
|
|
1053
|
+
results_compacted: linkEvidence.results_compacted === true || omittedResultCount > 0,
|
|
1054
|
+
min_bytes: check.min_bytes ?? null,
|
|
1055
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
1009
1056
|
status_counts: statusCounts,
|
|
1010
1057
|
failures: failures.slice(0, 20)
|
|
1011
1058
|
};
|
|
@@ -1534,6 +1581,8 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1534
1581
|
min_count: check.min_count ?? null,
|
|
1535
1582
|
allowed_statuses: linkStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
1536
1583
|
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
1584
|
+
min_bytes: check.min_bytes ?? null,
|
|
1585
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
1537
1586
|
viewports: summaries.map((summary) => toJsonValue(summary)),
|
|
1538
1587
|
failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue(summary.viewport) ?? null, failure })) : [])
|
|
1539
1588
|
},
|
|
@@ -2137,15 +2186,41 @@ function linkStatusIsAllowed(status, check) {
|
|
|
2137
2186
|
const allowed = linkStatusAllowedStatuses(check);
|
|
2138
2187
|
return Array.isArray(allowed) && allowed.length ? allowed.includes(status) : status >= 200 && status < 400;
|
|
2139
2188
|
}
|
|
2189
|
+
function linkStatusObservedBytes(result) {
|
|
2190
|
+
const bytes = typeof result.bytes === "number" && Number.isFinite(result.bytes) ? result.bytes : undefined;
|
|
2191
|
+
const contentLength = typeof result.content_length === "number" && Number.isFinite(result.content_length) ? result.content_length : undefined;
|
|
2192
|
+
const observed = Math.max(bytes || 0, contentLength || 0);
|
|
2193
|
+
return observed > 0 ? observed : undefined;
|
|
2194
|
+
}
|
|
2195
|
+
function normalizeLinkStatusContentType(value) {
|
|
2196
|
+
if (typeof value !== "string") return undefined;
|
|
2197
|
+
const normalized = (value.split(";")[0] || "").trim().toLowerCase();
|
|
2198
|
+
return normalized || undefined;
|
|
2199
|
+
}
|
|
2200
|
+
function linkStatusContentTypeOk(result, check) {
|
|
2201
|
+
if (!Array.isArray(check.allowed_content_types) || !check.allowed_content_types.length) return true;
|
|
2202
|
+
const actual = normalizeLinkStatusContentType(result.content_type);
|
|
2203
|
+
if (!actual) return false;
|
|
2204
|
+
return check.allowed_content_types.some((contentType) => {
|
|
2205
|
+
const normalized = normalizeLinkStatusContentType(contentType);
|
|
2206
|
+
if (!normalized) return false;
|
|
2207
|
+
if (normalized.endsWith("/*")) return actual.startsWith(normalized.slice(0, -1));
|
|
2208
|
+
return actual === normalized;
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2140
2211
|
function linkStatusResultOk(result, check) {
|
|
2141
2212
|
if (!result || typeof result !== "object" || Array.isArray(result)) return false;
|
|
2142
2213
|
if (!linkStatusIsAllowed(result.status, check)) return false;
|
|
2143
2214
|
if (typeof result.error === "string" && result.error.trim()) return false;
|
|
2144
2215
|
if (result.ok === false) return false;
|
|
2216
|
+
if (!linkStatusContentTypeOk(result, check)) return false;
|
|
2145
2217
|
if (check.require_nonzero_bytes === true) {
|
|
2146
|
-
const
|
|
2147
|
-
|
|
2148
|
-
|
|
2218
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
2219
|
+
if (observedBytes === undefined || observedBytes <= 0) return false;
|
|
2220
|
+
}
|
|
2221
|
+
if (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) {
|
|
2222
|
+
const observedBytes = linkStatusObservedBytes(result);
|
|
2223
|
+
if (observedBytes === undefined || observedBytes < check.min_bytes) return false;
|
|
2149
2224
|
}
|
|
2150
2225
|
return true;
|
|
2151
2226
|
}
|
|
@@ -2164,6 +2239,10 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
2164
2239
|
}
|
|
2165
2240
|
const results = Array.isArray(linkEvidence.results) ? linkEvidence.results.filter((result) => result && typeof result === "object" && !Array.isArray(result)) : [];
|
|
2166
2241
|
const totalCount = typeof linkEvidence.total_count === "number" && Number.isFinite(linkEvidence.total_count) ? linkEvidence.total_count : results.length;
|
|
2242
|
+
const resultCount = typeof linkEvidence.result_count === "number" && Number.isFinite(linkEvidence.result_count) ? linkEvidence.result_count : totalCount;
|
|
2243
|
+
const storedResultCount = typeof linkEvidence.stored_result_count === "number" && Number.isFinite(linkEvidence.stored_result_count) ? linkEvidence.stored_result_count : results.length;
|
|
2244
|
+
const omittedResultCount = typeof linkEvidence.omitted_result_count === "number" && Number.isFinite(linkEvidence.omitted_result_count) ? linkEvidence.omitted_result_count : Math.max(0, resultCount - storedResultCount);
|
|
2245
|
+
const omittedSuccessCount = typeof linkEvidence.omitted_success_count === "number" && Number.isFinite(linkEvidence.omitted_success_count) ? linkEvidence.omitted_success_count : 0;
|
|
2167
2246
|
const okCount = results.filter((result) => linkStatusResultOk(result, check)).length;
|
|
2168
2247
|
const failures = results
|
|
2169
2248
|
.filter((result) => !linkStatusResultOk(result, check))
|
|
@@ -2173,11 +2252,10 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
2173
2252
|
status: typeof result.status === "number" && Number.isFinite(result.status) ? result.status : null,
|
|
2174
2253
|
method: typeof result.method === "string" ? result.method : null,
|
|
2175
2254
|
error: typeof result.error === "string" ? result.error : null,
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
: null,
|
|
2255
|
+
content_type: typeof result.content_type === "string" ? result.content_type : null,
|
|
2256
|
+
bytes: linkStatusObservedBytes(result) ?? null,
|
|
2257
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
2258
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
2181
2259
|
}));
|
|
2182
2260
|
if (typeof linkEvidence.error === "string" && linkEvidence.error.trim()) {
|
|
2183
2261
|
failures.push({ code: "link_status_capture_failed", error: linkEvidence.error });
|
|
@@ -2212,6 +2290,13 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
2212
2290
|
failed_count: failures.length,
|
|
2213
2291
|
truncated: linkEvidence.truncated === true,
|
|
2214
2292
|
max_links: typeof linkEvidence.max_links === "number" ? linkEvidence.max_links : check.max_links || 100,
|
|
2293
|
+
result_count: resultCount,
|
|
2294
|
+
stored_result_count: storedResultCount,
|
|
2295
|
+
omitted_result_count: omittedResultCount,
|
|
2296
|
+
omitted_success_count: omittedSuccessCount,
|
|
2297
|
+
results_compacted: linkEvidence.results_compacted === true || omittedResultCount > 0,
|
|
2298
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
2299
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
2215
2300
|
status_counts: linkEvidence.status_counts && typeof linkEvidence.status_counts === "object" && !Array.isArray(linkEvidence.status_counts) ? linkEvidence.status_counts : {},
|
|
2216
2301
|
failures: failures.slice(0, 20),
|
|
2217
2302
|
};
|
|
@@ -2908,6 +2993,8 @@ function assessProfile(profile, evidence) {
|
|
|
2908
2993
|
min_count: check.min_count ?? null,
|
|
2909
2994
|
allowed_statuses: linkStatusAllowedStatuses(check) || ["2xx", "3xx"],
|
|
2910
2995
|
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
2996
|
+
min_bytes: check.min_bytes ?? null,
|
|
2997
|
+
allowed_content_types: check.allowed_content_types ?? null,
|
|
2911
2998
|
viewports: summaries,
|
|
2912
2999
|
failures: failed.flatMap((summary) => Array.isArray(summary.failures)
|
|
2913
3000
|
? summary.failures.map((failure) => ({ viewport: summary.viewport || null, failure }))
|
|
@@ -4055,6 +4142,28 @@ function linkProbeAllowed(status, check) {
|
|
|
4055
4142
|
: null;
|
|
4056
4143
|
return allowed ? allowed.includes(status) : status >= 200 && status < 400;
|
|
4057
4144
|
}
|
|
4145
|
+
function linkProbeObservedBytes(result) {
|
|
4146
|
+
const bytes = typeof result.bytes === "number" && Number.isFinite(result.bytes) ? result.bytes : undefined;
|
|
4147
|
+
const contentLength = typeof result.content_length === "number" && Number.isFinite(result.content_length) ? result.content_length : undefined;
|
|
4148
|
+
const observed = Math.max(bytes || 0, contentLength || 0);
|
|
4149
|
+
return observed > 0 ? observed : undefined;
|
|
4150
|
+
}
|
|
4151
|
+
function normalizeLinkProbeContentType(value) {
|
|
4152
|
+
if (typeof value !== "string") return undefined;
|
|
4153
|
+
const normalized = (value.split(";")[0] || "").trim().toLowerCase();
|
|
4154
|
+
return normalized || undefined;
|
|
4155
|
+
}
|
|
4156
|
+
function linkProbeContentTypeAllowed(result, check) {
|
|
4157
|
+
if (!Array.isArray(check.allowed_content_types) || !check.allowed_content_types.length) return true;
|
|
4158
|
+
const actual = normalizeLinkProbeContentType(result.content_type);
|
|
4159
|
+
if (!actual) return false;
|
|
4160
|
+
return check.allowed_content_types.some((contentType) => {
|
|
4161
|
+
const normalized = normalizeLinkProbeContentType(contentType);
|
|
4162
|
+
if (!normalized) return false;
|
|
4163
|
+
if (normalized.endsWith("/*")) return actual.startsWith(normalized.slice(0, -1));
|
|
4164
|
+
return actual === normalized;
|
|
4165
|
+
});
|
|
4166
|
+
}
|
|
4058
4167
|
function linkProbeResponseFields(response, method) {
|
|
4059
4168
|
const contentLengthHeader = response.headers && typeof response.headers.get === "function" ? response.headers.get("content-length") : null;
|
|
4060
4169
|
const contentLength = contentLengthHeader && /^\d+$/.test(contentLengthHeader) ? Number(contentLengthHeader) : null;
|
|
@@ -4069,6 +4178,7 @@ function linkProbeResponseFields(response, method) {
|
|
|
4069
4178
|
}
|
|
4070
4179
|
async function probeLinkStatus(candidate, check) {
|
|
4071
4180
|
const requireNonzeroBytes = check.require_nonzero_bytes === true;
|
|
4181
|
+
const minBytes = typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? Math.max(1, Math.floor(check.min_bytes)) : null;
|
|
4072
4182
|
const allowGetFallback = check.allow_get_fallback !== false;
|
|
4073
4183
|
const result = {
|
|
4074
4184
|
url: candidate.url,
|
|
@@ -4086,7 +4196,7 @@ async function probeLinkStatus(candidate, check) {
|
|
|
4086
4196
|
};
|
|
4087
4197
|
const applyResponse = async (response, method, readBytes) => {
|
|
4088
4198
|
Object.assign(result, linkProbeResponseFields(response, method));
|
|
4089
|
-
if (readBytes
|
|
4199
|
+
if (readBytes) {
|
|
4090
4200
|
try {
|
|
4091
4201
|
const buffer = await response.arrayBuffer();
|
|
4092
4202
|
result.bytes = buffer.byteLength;
|
|
@@ -4095,7 +4205,9 @@ async function probeLinkStatus(candidate, check) {
|
|
|
4095
4205
|
}
|
|
4096
4206
|
}
|
|
4097
4207
|
result.ok = linkProbeAllowed(result.status, check)
|
|
4098
|
-
&& (
|
|
4208
|
+
&& linkProbeContentTypeAllowed(result, check)
|
|
4209
|
+
&& (!requireNonzeroBytes || ((linkProbeObservedBytes(result) || 0) > 0))
|
|
4210
|
+
&& (minBytes === null || ((linkProbeObservedBytes(result) || 0) >= minBytes))
|
|
4099
4211
|
&& !result.error;
|
|
4100
4212
|
};
|
|
4101
4213
|
try {
|
|
@@ -4112,9 +4224,9 @@ async function probeLinkStatus(candidate, check) {
|
|
|
4112
4224
|
method: "GET",
|
|
4113
4225
|
redirect: "follow",
|
|
4114
4226
|
cache: "no-store",
|
|
4115
|
-
headers: { Range: "bytes=0-
|
|
4227
|
+
headers: { Range: "bytes=0-" + String((minBytes || 1) - 1) },
|
|
4116
4228
|
});
|
|
4117
|
-
await applyResponse(response, "GET", requireNonzeroBytes);
|
|
4229
|
+
await applyResponse(response, "GET", requireNonzeroBytes || minBytes !== null);
|
|
4118
4230
|
return result;
|
|
4119
4231
|
} catch (error) {
|
|
4120
4232
|
result.error = String(error && error.message ? error.message : error).slice(0, 500);
|
|
@@ -4122,6 +4234,30 @@ async function probeLinkStatus(candidate, check) {
|
|
|
4122
4234
|
return result;
|
|
4123
4235
|
}
|
|
4124
4236
|
}
|
|
4237
|
+
function compactLinkProbeResults(results) {
|
|
4238
|
+
const allResults = Array.isArray(results) ? results : [];
|
|
4239
|
+
if (allResults.length <= 20) {
|
|
4240
|
+
return {
|
|
4241
|
+
result_count: allResults.length,
|
|
4242
|
+
stored_result_count: allResults.length,
|
|
4243
|
+
omitted_result_count: 0,
|
|
4244
|
+
omitted_success_count: 0,
|
|
4245
|
+
results_compacted: false,
|
|
4246
|
+
results: allResults,
|
|
4247
|
+
};
|
|
4248
|
+
}
|
|
4249
|
+
const successResults = allResults.filter((result) => result && result.ok);
|
|
4250
|
+
const sampledSuccesses = new Set(successResults.slice(0, 5));
|
|
4251
|
+
const storedResults = allResults.filter((result) => result && (!result.ok || sampledSuccesses.has(result)));
|
|
4252
|
+
return {
|
|
4253
|
+
result_count: allResults.length,
|
|
4254
|
+
stored_result_count: storedResults.length,
|
|
4255
|
+
omitted_result_count: allResults.length - storedResults.length,
|
|
4256
|
+
omitted_success_count: Math.max(0, successResults.length - sampledSuccesses.size),
|
|
4257
|
+
results_compacted: storedResults.length < allResults.length,
|
|
4258
|
+
results: storedResults,
|
|
4259
|
+
};
|
|
4260
|
+
}
|
|
4125
4261
|
async function collectLinkStatus(check) {
|
|
4126
4262
|
const selector = linkStatusSelector(check);
|
|
4127
4263
|
const candidateResult = await collectLinkCandidates(selector);
|
|
@@ -4168,13 +4304,17 @@ async function collectLinkStatus(check) {
|
|
|
4168
4304
|
status: result.status,
|
|
4169
4305
|
method: result.method,
|
|
4170
4306
|
error: result.error,
|
|
4171
|
-
|
|
4307
|
+
content_type: result.content_type,
|
|
4308
|
+
bytes: linkProbeObservedBytes(result) || null,
|
|
4309
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
4310
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
4172
4311
|
}));
|
|
4173
4312
|
const statusCounts = {};
|
|
4174
4313
|
for (const result of results) {
|
|
4175
4314
|
const key = result.status == null ? "error" : String(result.status);
|
|
4176
4315
|
statusCounts[key] = (statusCounts[key] || 0) + 1;
|
|
4177
4316
|
}
|
|
4317
|
+
const compactedResults = compactLinkProbeResults(results);
|
|
4178
4318
|
return {
|
|
4179
4319
|
version: "riddle-proof.link-status.v1",
|
|
4180
4320
|
selector,
|
|
@@ -4182,6 +4322,8 @@ async function collectLinkStatus(check) {
|
|
|
4182
4322
|
same_origin_only: linkProbeSameOriginOnly(check),
|
|
4183
4323
|
dedupe: linkProbeDedupe(check),
|
|
4184
4324
|
require_nonzero_bytes: check.require_nonzero_bytes === true,
|
|
4325
|
+
min_bytes: typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes) ? check.min_bytes : null,
|
|
4326
|
+
allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
|
|
4185
4327
|
allowed_statuses: Array.isArray(check.allowed_statuses) && check.allowed_statuses.length
|
|
4186
4328
|
? check.allowed_statuses
|
|
4187
4329
|
: typeof check.expected_status === "number"
|
|
@@ -4194,7 +4336,7 @@ async function collectLinkStatus(check) {
|
|
|
4194
4336
|
failed_count: failures.length,
|
|
4195
4337
|
status_counts: statusCounts,
|
|
4196
4338
|
failures: failures.slice(0, 20),
|
|
4197
|
-
|
|
4339
|
+
...compactedResults,
|
|
4198
4340
|
};
|
|
4199
4341
|
}
|
|
4200
4342
|
async function frameEvidence(selector) {
|