@reddoorla/maintenance 0.72.0 → 0.74.0
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/airtable-6L3JD7FY.js +9 -0
- package/dist/{announce-HJCGEVJA.js → announce-V6EYPPUR.js} +4 -4
- package/dist/{blux-FKZYMKLF.js → blux-OL5HKP26.js} +950 -69
- package/dist/blux-OL5HKP26.js.map +1 -0
- package/dist/{bump-deps-GZEJAUKI.js → bump-deps-OQR3IXLN.js} +2 -2
- package/dist/chunk-2OP4JKIQ.js +19 -0
- package/dist/chunk-2OP4JKIQ.js.map +1 -0
- package/dist/{chunk-BJCA7O4A.js → chunk-3PU2QWOJ.js} +2 -2
- package/dist/{chunk-GI4M2AMO.js → chunk-4BHNP6Y2.js} +2 -2
- package/dist/{chunk-KRHPS3R4.js → chunk-6OYICQSJ.js} +47 -3
- package/dist/chunk-6OYICQSJ.js.map +1 -0
- package/dist/{chunk-BWKK2ZXS.js → chunk-GXVX4HUH.js} +32 -2
- package/dist/chunk-GXVX4HUH.js.map +1 -0
- package/dist/{chunk-KLB2OLPW.js → chunk-HBFMTM2E.js} +3 -3
- package/dist/{chunk-SQJ53Z7R.js → chunk-I55YWV6T.js} +3 -3
- package/dist/{chunk-7P7YETHG.js → chunk-IDBMYDVH.js} +2 -2
- package/dist/{chunk-JGLAWXUC.js → chunk-JR67FZVC.js} +7 -3
- package/dist/chunk-JR67FZVC.js.map +1 -0
- package/dist/{chunk-APTAITXZ.js → chunk-KKL3RYFQ.js} +2 -2
- package/dist/{chunk-NRISG4XD.js → chunk-NUIBQPB3.js} +42 -11
- package/dist/chunk-NUIBQPB3.js.map +1 -0
- package/dist/{chunk-BHE5CUWI.js → chunk-PERP7LSS.js} +269 -18
- package/dist/chunk-PERP7LSS.js.map +1 -0
- package/dist/chunk-RHPDQW6D.js +29 -0
- package/dist/chunk-RHPDQW6D.js.map +1 -0
- package/dist/{chunk-HR5SONJC.js → chunk-SK44IFZN.js} +2 -2
- package/dist/cli/bin.js +31 -19
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +6 -6
- package/dist/{convert-to-pnpm-SVFDLXZS.js → convert-to-pnpm-OVMASATZ.js} +2 -2
- package/dist/{digest-VX7Q4MNF.js → digest-LEMZS5ZK.js} +99 -7
- package/dist/digest-LEMZS5ZK.js.map +1 -0
- package/dist/{ensure-site-O7G5ZJS7.js → ensure-site-3SOAWTZF.js} +2 -2
- package/dist/forms/index.js +1 -1
- package/dist/{github-signals-QVW2LB7N.js → github-signals-TF3VSNIN.js} +4 -4
- package/dist/{health-endpoint-IYR5VBUT.js → health-endpoint-JZGYCMTV.js} +2 -2
- package/dist/index.d.ts +104 -19
- package/dist/index.js +122 -30
- package/dist/index.js.map +1 -1
- package/dist/{init-CU6PVZRG.js → init-7PTP4O2K.js} +4 -4
- package/dist/{launch-JWXCTJRG.js → launch-W33FWWOZ.js} +6 -6
- package/dist/{lighthouse-airtable-J5TLASJC.js → lighthouse-airtable-ZXNMDEPE.js} +3 -3
- package/dist/{onboard-6UQE5QIO.js → onboard-PX36YDZA.js} +2 -2
- package/dist/{orchestrate-ZDH24Z6Y.js → orchestrate-NYRYEKQM.js} +3 -3
- package/dist/{preflight-BNTMWTPG.js → preflight-JCQFKB6M.js} +4 -4
- package/dist/{renovate-dispatch-C3ALLACX.js → renovate-dispatch-6II2JVOX.js} +29 -27
- package/dist/renovate-dispatch-6II2JVOX.js.map +1 -0
- package/dist/{report-YLFSGNWR.js → report-POHQEV6F.js} +21 -5
- package/dist/report-POHQEV6F.js.map +1 -0
- package/dist/screenouts-7P5GOZMV.js +57 -0
- package/dist/screenouts-7P5GOZMV.js.map +1 -0
- package/dist/{self-updating-64FNRQB5.js → self-updating-2YSVNMPP.js} +2 -2
- package/dist/{selftest-7DPNGL6R.js → selftest-OO2WDSDM.js} +5 -5
- package/dist/{smoke-suite-TWILWDSF.js → smoke-suite-CSP23NZM.js} +2 -2
- package/dist/spam-classifier-VG4ICIWS.js +217 -0
- package/dist/spam-classifier-VG4ICIWS.js.map +1 -0
- package/dist/submissions-BNRGY3JQ.js +122 -0
- package/dist/submissions-BNRGY3JQ.js.map +1 -0
- package/dist/submissions-MDKWJYMM.js +281 -0
- package/dist/submissions-MDKWJYMM.js.map +1 -0
- package/dist/{svelte-codemods-RVDTTXFD.js → svelte-codemods-V54KOBWU.js} +2 -2
- package/dist/{sync-configs-BUUD6L5Q.js → sync-configs-UBCJRASZ.js} +2 -2
- package/dist/{upgrade-HHKWYJRN.js → upgrade-WR6ICPAG.js} +2 -2
- package/dist/{websites-EH2ULELZ.js → websites-U6642XSE.js} +10 -2
- package/dist/{write-audits-to-airtable-LZDYTNX5.js → write-audits-to-airtable-22T6D733.js} +4 -4
- package/package.json +1 -1
- package/dist/airtable-U2GTDTK5.js +0 -9
- package/dist/blux-FKZYMKLF.js.map +0 -1
- package/dist/chunk-BHE5CUWI.js.map +0 -1
- package/dist/chunk-BWKK2ZXS.js.map +0 -1
- package/dist/chunk-JGLAWXUC.js.map +0 -1
- package/dist/chunk-KRHPS3R4.js.map +0 -1
- package/dist/chunk-NRISG4XD.js.map +0 -1
- package/dist/chunk-QTHKOIFJ.js +0 -13
- package/dist/chunk-QTHKOIFJ.js.map +0 -1
- package/dist/digest-VX7Q4MNF.js.map +0 -1
- package/dist/renovate-dispatch-C3ALLACX.js.map +0 -1
- package/dist/report-YLFSGNWR.js.map +0 -1
- /package/dist/{airtable-U2GTDTK5.js.map → airtable-6L3JD7FY.js.map} +0 -0
- /package/dist/{announce-HJCGEVJA.js.map → announce-V6EYPPUR.js.map} +0 -0
- /package/dist/{bump-deps-GZEJAUKI.js.map → bump-deps-OQR3IXLN.js.map} +0 -0
- /package/dist/{chunk-BJCA7O4A.js.map → chunk-3PU2QWOJ.js.map} +0 -0
- /package/dist/{chunk-GI4M2AMO.js.map → chunk-4BHNP6Y2.js.map} +0 -0
- /package/dist/{chunk-KLB2OLPW.js.map → chunk-HBFMTM2E.js.map} +0 -0
- /package/dist/{chunk-SQJ53Z7R.js.map → chunk-I55YWV6T.js.map} +0 -0
- /package/dist/{chunk-7P7YETHG.js.map → chunk-IDBMYDVH.js.map} +0 -0
- /package/dist/{chunk-APTAITXZ.js.map → chunk-KKL3RYFQ.js.map} +0 -0
- /package/dist/{chunk-HR5SONJC.js.map → chunk-SK44IFZN.js.map} +0 -0
- /package/dist/{convert-to-pnpm-SVFDLXZS.js.map → convert-to-pnpm-OVMASATZ.js.map} +0 -0
- /package/dist/{ensure-site-O7G5ZJS7.js.map → ensure-site-3SOAWTZF.js.map} +0 -0
- /package/dist/{github-signals-QVW2LB7N.js.map → github-signals-TF3VSNIN.js.map} +0 -0
- /package/dist/{health-endpoint-IYR5VBUT.js.map → health-endpoint-JZGYCMTV.js.map} +0 -0
- /package/dist/{init-CU6PVZRG.js.map → init-7PTP4O2K.js.map} +0 -0
- /package/dist/{launch-JWXCTJRG.js.map → launch-W33FWWOZ.js.map} +0 -0
- /package/dist/{lighthouse-airtable-J5TLASJC.js.map → lighthouse-airtable-ZXNMDEPE.js.map} +0 -0
- /package/dist/{onboard-6UQE5QIO.js.map → onboard-PX36YDZA.js.map} +0 -0
- /package/dist/{orchestrate-ZDH24Z6Y.js.map → orchestrate-NYRYEKQM.js.map} +0 -0
- /package/dist/{preflight-BNTMWTPG.js.map → preflight-JCQFKB6M.js.map} +0 -0
- /package/dist/{self-updating-64FNRQB5.js.map → self-updating-2YSVNMPP.js.map} +0 -0
- /package/dist/{selftest-7DPNGL6R.js.map → selftest-OO2WDSDM.js.map} +0 -0
- /package/dist/{smoke-suite-TWILWDSF.js.map → smoke-suite-CSP23NZM.js.map} +0 -0
- /package/dist/{svelte-codemods-RVDTTXFD.js.map → svelte-codemods-V54KOBWU.js.map} +0 -0
- /package/dist/{sync-configs-BUUD6L5Q.js.map → sync-configs-UBCJRASZ.js.map} +0 -0
- /package/dist/{upgrade-HHKWYJRN.js.map → upgrade-WR6ICPAG.js.map} +0 -0
- /package/dist/{websites-EH2ULELZ.js.map → websites-U6642XSE.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-LZDYTNX5.js.map → write-audits-to-airtable-22T6D733.js.map} +0 -0
|
@@ -1119,6 +1119,67 @@ function parseHtmlLinks(html, baseUrl) {
|
|
|
1119
1119
|
function family(pathname) {
|
|
1120
1120
|
return pathname.split("/").filter(Boolean)[0] ?? "";
|
|
1121
1121
|
}
|
|
1122
|
+
var ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1123
|
+
"pdf",
|
|
1124
|
+
"doc",
|
|
1125
|
+
"docx",
|
|
1126
|
+
"xls",
|
|
1127
|
+
"xlsx",
|
|
1128
|
+
"ppt",
|
|
1129
|
+
"pptx",
|
|
1130
|
+
"csv",
|
|
1131
|
+
"txt",
|
|
1132
|
+
"rtf",
|
|
1133
|
+
"jpg",
|
|
1134
|
+
"jpeg",
|
|
1135
|
+
"png",
|
|
1136
|
+
"gif",
|
|
1137
|
+
"webp",
|
|
1138
|
+
"avif",
|
|
1139
|
+
"svg",
|
|
1140
|
+
"ico",
|
|
1141
|
+
"bmp",
|
|
1142
|
+
"tif",
|
|
1143
|
+
"tiff",
|
|
1144
|
+
"css",
|
|
1145
|
+
"js",
|
|
1146
|
+
"mjs",
|
|
1147
|
+
"map",
|
|
1148
|
+
"json",
|
|
1149
|
+
"xml",
|
|
1150
|
+
"webmanifest",
|
|
1151
|
+
"rss",
|
|
1152
|
+
"atom",
|
|
1153
|
+
"zip",
|
|
1154
|
+
"gz",
|
|
1155
|
+
"tar",
|
|
1156
|
+
"rar",
|
|
1157
|
+
"7z",
|
|
1158
|
+
"dmg",
|
|
1159
|
+
"exe",
|
|
1160
|
+
"mp3",
|
|
1161
|
+
"mp4",
|
|
1162
|
+
"webm",
|
|
1163
|
+
"mov",
|
|
1164
|
+
"avi",
|
|
1165
|
+
"wav",
|
|
1166
|
+
"ogg",
|
|
1167
|
+
"m4a",
|
|
1168
|
+
"m4v",
|
|
1169
|
+
"woff",
|
|
1170
|
+
"woff2",
|
|
1171
|
+
"ttf",
|
|
1172
|
+
"otf",
|
|
1173
|
+
"eot"
|
|
1174
|
+
]);
|
|
1175
|
+
function isPageRoutePath(pathname) {
|
|
1176
|
+
const last = pathname.split("/").filter(Boolean).pop() ?? "";
|
|
1177
|
+
const dot = last.lastIndexOf(".");
|
|
1178
|
+
if (dot <= 0) return true;
|
|
1179
|
+
const ext = last.slice(dot + 1).toLowerCase();
|
|
1180
|
+
if (ext === "html" || ext === "htm") return true;
|
|
1181
|
+
return !ASSET_EXTENSIONS.has(ext);
|
|
1182
|
+
}
|
|
1122
1183
|
function sampleRoutePaths(urlsOrPaths, cap = DEFAULT_CAP) {
|
|
1123
1184
|
const seen = /* @__PURE__ */ new Set(["/"]);
|
|
1124
1185
|
const buckets = /* @__PURE__ */ new Map();
|
|
@@ -1129,9 +1190,11 @@ function sampleRoutePaths(urlsOrPaths, cap = DEFAULT_CAP) {
|
|
|
1129
1190
|
} catch {
|
|
1130
1191
|
continue;
|
|
1131
1192
|
}
|
|
1132
|
-
|
|
1193
|
+
pathname = pathname.replace(/\/+$/, "");
|
|
1194
|
+
if (pathname === "") continue;
|
|
1133
1195
|
if (seen.has(pathname)) continue;
|
|
1134
1196
|
seen.add(pathname);
|
|
1197
|
+
if (!isPageRoutePath(pathname)) continue;
|
|
1135
1198
|
const fam = family(pathname);
|
|
1136
1199
|
const arr = buckets.get(fam) ?? [];
|
|
1137
1200
|
arr.push(pathname);
|
|
@@ -1183,32 +1246,174 @@ async function discoverRoutes(deployedUrl, deps, cap = DEFAULT_CAP) {
|
|
|
1183
1246
|
function isBroken(status) {
|
|
1184
1247
|
return status === null || status >= 400;
|
|
1185
1248
|
}
|
|
1249
|
+
function isOkStatus(status) {
|
|
1250
|
+
return status !== null && status >= 200 && status < 400;
|
|
1251
|
+
}
|
|
1252
|
+
function decodeEntities(s) {
|
|
1253
|
+
return s.replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/�?39;|'/gi, "'").replace(/ /gi, " ");
|
|
1254
|
+
}
|
|
1255
|
+
function extractTitleMeta(html) {
|
|
1256
|
+
const t = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
|
|
1257
|
+
const title = t?.[1] ? decodeEntities(t[1]).trim() || null : null;
|
|
1258
|
+
let metaDescription = null;
|
|
1259
|
+
const metaTags = html.match(/<meta\b[^>]*>/gi) ?? [];
|
|
1260
|
+
for (const tag of metaTags) {
|
|
1261
|
+
if (!/\bname\s*=\s*["']description["']/i.test(tag)) continue;
|
|
1262
|
+
const content = /\bcontent\s*=\s*("([^"]*)"|'([^']*)')/i.exec(tag);
|
|
1263
|
+
const raw = content?.[2] ?? content?.[3] ?? "";
|
|
1264
|
+
metaDescription = decodeEntities(raw).trim() || null;
|
|
1265
|
+
break;
|
|
1266
|
+
}
|
|
1267
|
+
return { title, metaDescription };
|
|
1268
|
+
}
|
|
1269
|
+
var REVERIFY_RETRY_DELAYS_MS = [15e3, 45e3];
|
|
1270
|
+
var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1271
|
+
function isRetryableStatus(status) {
|
|
1272
|
+
return status === null || status === 403 || status === 408 || status === 429 || status >= 500;
|
|
1273
|
+
}
|
|
1274
|
+
async function fetchStatusesWithCooldown(urls, verify, sleep) {
|
|
1275
|
+
const byUrl = /* @__PURE__ */ new Map();
|
|
1276
|
+
for (const url of new Set(urls)) byUrl.set(url, await verify.fetchPage(url));
|
|
1277
|
+
for (const delayMs of REVERIFY_RETRY_DELAYS_MS) {
|
|
1278
|
+
const pending = [...byUrl.entries()].filter(([, f]) => !isOkStatus(f.status) && isRetryableStatus(f.status)).map(([u]) => u);
|
|
1279
|
+
if (pending.length === 0) break;
|
|
1280
|
+
await sleep(delayMs);
|
|
1281
|
+
for (const url of pending) byUrl.set(url, await verify.fetchPage(url));
|
|
1282
|
+
}
|
|
1283
|
+
return byUrl;
|
|
1284
|
+
}
|
|
1285
|
+
async function reverifyRoutes(routes, verify, sleep = defaultSleep) {
|
|
1286
|
+
const statusIdx = [];
|
|
1287
|
+
const titleMetaIdx = [];
|
|
1288
|
+
routes.forEach((r, i) => {
|
|
1289
|
+
if (!isOkStatus(r.status)) statusIdx.push(i);
|
|
1290
|
+
else if (!(r.title ?? "").trim() || !(r.metaDescription ?? "").trim()) titleMetaIdx.push(i);
|
|
1291
|
+
});
|
|
1292
|
+
const statusFetches = await fetchStatusesWithCooldown(
|
|
1293
|
+
statusIdx.map((i) => routes[i].url),
|
|
1294
|
+
verify,
|
|
1295
|
+
sleep
|
|
1296
|
+
);
|
|
1297
|
+
const fetchedByIdx = /* @__PURE__ */ new Map();
|
|
1298
|
+
for (const i of titleMetaIdx) fetchedByIdx.set(i, await verify.fetchPage(routes[i].url));
|
|
1299
|
+
const out = [...routes];
|
|
1300
|
+
const reverified = [];
|
|
1301
|
+
for (const i of statusIdx) {
|
|
1302
|
+
const r = routes[i];
|
|
1303
|
+
const fetched = statusFetches.get(r.url);
|
|
1304
|
+
reverified.push({ url: r.url, browserStatus: r.status, fetchedStatus: fetched.status });
|
|
1305
|
+
const ok = isOkStatus(fetched.status);
|
|
1306
|
+
out[i] = {
|
|
1307
|
+
...r,
|
|
1308
|
+
status: fetched.status,
|
|
1309
|
+
title: ok ? fetched.title : null,
|
|
1310
|
+
metaDescription: ok ? fetched.metaDescription : null
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
for (const i of titleMetaIdx) {
|
|
1314
|
+
const r = routes[i];
|
|
1315
|
+
const fetched = fetchedByIdx.get(i);
|
|
1316
|
+
if (!isOkStatus(fetched.status)) continue;
|
|
1317
|
+
out[i] = {
|
|
1318
|
+
...r,
|
|
1319
|
+
title: (r.title ?? "").trim() ? r.title : fetched.title,
|
|
1320
|
+
metaDescription: (r.metaDescription ?? "").trim() ? r.metaDescription : fetched.metaDescription
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
return { routes: out, reverified };
|
|
1324
|
+
}
|
|
1325
|
+
function excuseChallengedEngineChecks(routes) {
|
|
1326
|
+
const excused = [];
|
|
1327
|
+
const excusable = (e) => !e.ok && typeof e.status === "number" && !isOkStatus(e.status) && isRetryableStatus(e.status);
|
|
1328
|
+
const out = routes.map((route) => {
|
|
1329
|
+
if (!isOkStatus(route.status)) return route;
|
|
1330
|
+
const desktop = route.desktop.map((d) => {
|
|
1331
|
+
if (!excusable(d)) return d;
|
|
1332
|
+
excused.push(
|
|
1333
|
+
`${route.url} [${d.engine}]: browser saw ${d.status}, route re-verified reachable (${route.status})`
|
|
1334
|
+
);
|
|
1335
|
+
return { ...d, ok: true };
|
|
1336
|
+
});
|
|
1337
|
+
const mobile = route.mobile.map((m) => {
|
|
1338
|
+
if (!excusable(m)) return m;
|
|
1339
|
+
excused.push(
|
|
1340
|
+
`${route.url} [${m.device}]: browser saw ${m.status}, route re-verified reachable (${route.status})`
|
|
1341
|
+
);
|
|
1342
|
+
return { ...m, ok: true };
|
|
1343
|
+
});
|
|
1344
|
+
return { ...route, desktop, mobile };
|
|
1345
|
+
});
|
|
1346
|
+
return { routes: out, excused };
|
|
1347
|
+
}
|
|
1348
|
+
async function reverifyLinks(links, verify, sleep = defaultSleep) {
|
|
1349
|
+
const suspects = links.filter((l) => isBroken(l.status) && isRetryableStatus(l.status));
|
|
1350
|
+
if (suspects.length === 0) return { links, reverified: [] };
|
|
1351
|
+
const fetched = await fetchStatusesWithCooldown(
|
|
1352
|
+
suspects.map((l) => l.url),
|
|
1353
|
+
verify,
|
|
1354
|
+
sleep
|
|
1355
|
+
);
|
|
1356
|
+
const suspectUrls = new Set(suspects.map((l) => l.url));
|
|
1357
|
+
const reverified = [];
|
|
1358
|
+
const out = links.map((l) => {
|
|
1359
|
+
if (!suspectUrls.has(l.url)) return l;
|
|
1360
|
+
const finalStatus = fetched.get(l.url).status;
|
|
1361
|
+
reverified.push({ url: l.url, firstStatus: l.status, fetchedStatus: finalStatus });
|
|
1362
|
+
return { url: l.url, status: finalStatus };
|
|
1363
|
+
});
|
|
1364
|
+
return { links: out, reverified };
|
|
1365
|
+
}
|
|
1366
|
+
var fmtEntryStatus = (s) => typeof s === "number" && !isOkStatus(s) ? ` \u2192 ${s}` : "";
|
|
1186
1367
|
function summarizeBrowser(routes, links, familyCounts) {
|
|
1187
1368
|
const desktopChecks = routes.flatMap((r) => r.desktop);
|
|
1188
1369
|
const mobileChecks = routes.flatMap((r) => r.mobile);
|
|
1189
1370
|
const desktopOk = routes.length > 0 && routes.every((r) => r.desktop.length > 0 && r.desktop.every((d) => d.ok));
|
|
1190
1371
|
const mobileOk = routes.length > 0 && routes.every((r) => r.mobile.length > 0 && r.mobile.every((m) => m.ok));
|
|
1191
|
-
const
|
|
1372
|
+
const desktopFailures = routes.flatMap(
|
|
1373
|
+
(r) => r.desktop.length === 0 ? [`${r.url}: no desktop observations`] : r.desktop.filter((d) => !d.ok).map((d) => `${r.url} [${d.engine}]${fmtEntryStatus(d.status)}`)
|
|
1374
|
+
);
|
|
1375
|
+
const mobileFailures = routes.flatMap(
|
|
1376
|
+
(r) => r.mobile.length === 0 ? [`${r.url}: no mobile observations`] : r.mobile.filter((m) => !m.ok).map((m) => `${r.url} [${m.device}]${fmtEntryStatus(m.status)}`)
|
|
1377
|
+
);
|
|
1378
|
+
const brokenLinkUrls = links.filter((l) => isBroken(l.status)).map((l) => `${l.url} \u2192 ${l.status ?? "no response"}`);
|
|
1379
|
+
const brokenLinks = brokenLinkUrls.length;
|
|
1192
1380
|
const linksOk = links.length > 0 && brokenLinks === 0;
|
|
1193
|
-
const
|
|
1194
|
-
const
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1381
|
+
const unreachableUrls = routes.filter((r) => !isOkStatus(r.status)).map((r) => `${r.url} \u2192 ${r.status ?? "no response"}`);
|
|
1382
|
+
const reachableOk = routes.length > 0 && unreachableUrls.length === 0;
|
|
1383
|
+
const titleMetaProblems = [];
|
|
1384
|
+
for (const r of routes) {
|
|
1385
|
+
const t = (r.title ?? "").trim();
|
|
1197
1386
|
const desc = (r.metaDescription ?? "").trim();
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1387
|
+
if (t.length === 0) titleMetaProblems.push(`${r.url}: empty title`);
|
|
1388
|
+
else if (t.length > 70) titleMetaProblems.push(`${r.url}: title ${t.length} chars (max 70)`);
|
|
1389
|
+
if (desc.length === 0) titleMetaProblems.push(`${r.url}: missing meta description`);
|
|
1390
|
+
}
|
|
1391
|
+
const byTitle = /* @__PURE__ */ new Map();
|
|
1392
|
+
for (const r of routes) {
|
|
1393
|
+
const t = (r.title ?? "").trim();
|
|
1394
|
+
if (t.length === 0) continue;
|
|
1395
|
+
byTitle.set(t, [...byTitle.get(t) ?? [], r.url]);
|
|
1396
|
+
}
|
|
1397
|
+
for (const [t, urls] of byTitle) {
|
|
1398
|
+
if (urls.length > 1) titleMetaProblems.push(`duplicate title "${t}": ${urls.join(" + ")}`);
|
|
1399
|
+
}
|
|
1400
|
+
const titleMetaOk = routes.length > 0 && titleMetaProblems.length === 0;
|
|
1202
1401
|
const engines = [...new Set(desktopChecks.map((d) => d.engine))];
|
|
1203
1402
|
const devices = [...new Set(mobileChecks.map((m) => m.device))];
|
|
1204
1403
|
const families = Object.entries(familyCounts).map(([f, n]) => f === "/" ? "/" : `${f} \xD7${n}`).join(", ");
|
|
1205
|
-
const
|
|
1404
|
+
const firstOf = (xs, n) => xs.slice(0, n).join("; ") + (xs.length > n ? ` (+${xs.length - n} more)` : "");
|
|
1405
|
+
const note = `${routes.length} routes (${families}); desktop ${engines.join("/") || "\u2014"}; mobile ${devices.join("/") || "\u2014"}; ${links.length} links, ${brokenLinks} broken` + (unreachableUrls.length > 0 ? `; unreachable: ${firstOf(unreachableUrls, 3)}` : "") + (titleMetaProblems.length > 0 ? `; title/meta: ${firstOf(titleMetaProblems, 3)}` : "") + (desktopFailures.length > 0 ? `; desktop failing: ${firstOf(desktopFailures, 3)}` : "") + (mobileFailures.length > 0 ? `; mobile failing: ${firstOf(mobileFailures, 3)}` : "") + (brokenLinkUrls.length > 0 ? `; broken: ${firstOf(brokenLinkUrls, 3)}` : "");
|
|
1206
1406
|
return {
|
|
1207
1407
|
desktopOk,
|
|
1208
1408
|
mobileOk,
|
|
1209
1409
|
linksOk,
|
|
1210
1410
|
reachableOk,
|
|
1211
1411
|
titleMetaOk,
|
|
1412
|
+
unreachableUrls,
|
|
1413
|
+
titleMetaProblems,
|
|
1414
|
+
desktopFailures,
|
|
1415
|
+
mobileFailures,
|
|
1416
|
+
brokenLinkUrls,
|
|
1212
1417
|
brokenLinks,
|
|
1213
1418
|
routesChecked: routes.length,
|
|
1214
1419
|
note
|
|
@@ -1222,24 +1427,47 @@ async function browserAudit(ctx) {
|
|
|
1222
1427
|
}
|
|
1223
1428
|
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
1224
1429
|
const discoverDeps = ctx.discoverDeps ?? defaultDiscoverDeps();
|
|
1430
|
+
const verifyDeps = ctx.verifyDeps ?? defaultVerifyDeps();
|
|
1225
1431
|
const runner = ctx.browserRunner ?? await defaultBrowserRunner();
|
|
1226
1432
|
try {
|
|
1227
1433
|
const discovered = await discoverRoutes(site.deployedUrl, discoverDeps);
|
|
1228
|
-
const
|
|
1434
|
+
const probed = await runner.probe(discovered.routes);
|
|
1435
|
+
const { routes: reverifiedRoutes, reverified } = await reverifyRoutes(probed, verifyDeps);
|
|
1436
|
+
const { routes: routeResults, excused } = excuseChallengedEngineChecks(reverifiedRoutes);
|
|
1229
1437
|
const internalLinks = [...new Set(routeResults.flatMap((r) => r.links))];
|
|
1230
|
-
const
|
|
1438
|
+
const rawLinks = await runner.checkLinks(internalLinks);
|
|
1439
|
+
const { links: linkResults, reverified: linkReverified } = await reverifyLinks(
|
|
1440
|
+
rawLinks,
|
|
1441
|
+
verifyDeps
|
|
1442
|
+
);
|
|
1231
1443
|
const summary = summarizeBrowser(
|
|
1232
1444
|
routeResults,
|
|
1233
1445
|
linkResults,
|
|
1234
1446
|
discovered.familyCounts ?? familyCountsOf(discovered.routes)
|
|
1235
1447
|
);
|
|
1448
|
+
const cleared = reverified.filter((v) => isOkStatus(v.fetchedStatus));
|
|
1449
|
+
const reverifiedUrls = cleared.map(
|
|
1450
|
+
(v) => `${v.url}: browser saw ${v.browserStatus ?? "no response"}, plain fetch ${v.fetchedStatus}`
|
|
1451
|
+
);
|
|
1452
|
+
const clearedLinks = linkReverified.filter((v) => isOkStatus(v.fetchedStatus));
|
|
1453
|
+
const linkReverifiedUrls = clearedLinks.map(
|
|
1454
|
+
(v) => `${v.url}: first check saw ${v.firstStatus ?? "no response"}, plain re-fetch ${v.fetchedStatus}`
|
|
1455
|
+
);
|
|
1456
|
+
const note = summary.note + (cleared.length > 0 ? `; ${cleared.length} route(s) re-verified reachable by plain fetch after a browser-side challenge/flake` : "") + (excused.length > 0 ? `; ${excused.length} engine/device check(s) excused (challenge on a re-verified-reachable route)` : "") + (clearedLinks.length > 0 ? `; ${clearedLinks.length} link(s) re-verified ok by plain fetch after cooldown` : "");
|
|
1236
1457
|
const status = summary.desktopOk && summary.mobileOk && summary.linksOk ? "pass" : "warn";
|
|
1237
1458
|
return {
|
|
1238
1459
|
audit: "browser",
|
|
1239
1460
|
site: label,
|
|
1240
1461
|
status,
|
|
1241
|
-
summary:
|
|
1242
|
-
details: {
|
|
1462
|
+
summary: note,
|
|
1463
|
+
details: {
|
|
1464
|
+
...summary,
|
|
1465
|
+
note,
|
|
1466
|
+
reverifiedUrls,
|
|
1467
|
+
excusedEngineChecks: excused,
|
|
1468
|
+
linkReverifiedUrls,
|
|
1469
|
+
checkedAt: now.toISOString()
|
|
1470
|
+
}
|
|
1243
1471
|
};
|
|
1244
1472
|
} finally {
|
|
1245
1473
|
await runner.close?.();
|
|
@@ -1262,6 +1490,25 @@ function defaultDiscoverDeps() {
|
|
|
1262
1490
|
}
|
|
1263
1491
|
};
|
|
1264
1492
|
}
|
|
1493
|
+
function defaultVerifyDeps() {
|
|
1494
|
+
return {
|
|
1495
|
+
fetchPage: async (url) => {
|
|
1496
|
+
try {
|
|
1497
|
+
const res = await fetch(url, {
|
|
1498
|
+
redirect: "follow",
|
|
1499
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
1500
|
+
});
|
|
1501
|
+
const isHtml = (res.headers.get("content-type") ?? "").includes("html");
|
|
1502
|
+
if (!res.ok || !isHtml) {
|
|
1503
|
+
return { status: res.status, title: null, metaDescription: null };
|
|
1504
|
+
}
|
|
1505
|
+
return { status: res.status, ...extractTitleMeta(await res.text()) };
|
|
1506
|
+
} catch {
|
|
1507
|
+
return { status: null, title: null, metaDescription: null };
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1265
1512
|
var DESKTOP_VIEWPORT = { width: 1366, height: 900 };
|
|
1266
1513
|
var PAGE_TIMEOUT_MS = 3e4;
|
|
1267
1514
|
async function defaultBrowserRunner() {
|
|
@@ -1295,11 +1542,13 @@ async function defaultBrowserRunner() {
|
|
|
1295
1542
|
const errors = [];
|
|
1296
1543
|
page.on("pageerror", (e) => errors.push(String(e)));
|
|
1297
1544
|
let ok = false;
|
|
1545
|
+
let navStatus = null;
|
|
1298
1546
|
try {
|
|
1299
1547
|
const resp = await page.goto(url, {
|
|
1300
1548
|
waitUntil: "domcontentloaded",
|
|
1301
1549
|
timeout: PAGE_TIMEOUT_MS
|
|
1302
1550
|
});
|
|
1551
|
+
navStatus = resp ? resp.status() : null;
|
|
1303
1552
|
const hasMain = await page.locator("main, [role=main]").first().isVisible().catch(() => false);
|
|
1304
1553
|
ok = !!resp && resp.ok() && errors.length === 0 && hasMain;
|
|
1305
1554
|
if (engine === "chromium") {
|
|
@@ -1323,7 +1572,7 @@ async function defaultBrowserRunner() {
|
|
|
1323
1572
|
await ctx.close().catch(() => {
|
|
1324
1573
|
});
|
|
1325
1574
|
}
|
|
1326
|
-
desktop.push({ engine, ok });
|
|
1575
|
+
desktop.push({ engine, ok, status: navStatus });
|
|
1327
1576
|
}
|
|
1328
1577
|
const mobile = [];
|
|
1329
1578
|
for (let i = 0; i < mobileTargets.length; i++) {
|
|
@@ -1334,11 +1583,13 @@ async function defaultBrowserRunner() {
|
|
|
1334
1583
|
const errors = [];
|
|
1335
1584
|
page.on("pageerror", (e) => errors.push(String(e)));
|
|
1336
1585
|
let ok = false;
|
|
1586
|
+
let navStatus = null;
|
|
1337
1587
|
try {
|
|
1338
1588
|
const resp = await page.goto(url, {
|
|
1339
1589
|
waitUntil: "domcontentloaded",
|
|
1340
1590
|
timeout: PAGE_TIMEOUT_MS
|
|
1341
1591
|
});
|
|
1592
|
+
navStatus = resp ? resp.status() : null;
|
|
1342
1593
|
const overflow = await page.evaluate("document.documentElement.scrollWidth > window.innerWidth + 2").catch(() => true);
|
|
1343
1594
|
ok = !!resp && resp.ok() && errors.length === 0 && !overflow;
|
|
1344
1595
|
} catch {
|
|
@@ -1347,7 +1598,7 @@ async function defaultBrowserRunner() {
|
|
|
1347
1598
|
await ctx.close().catch(() => {
|
|
1348
1599
|
});
|
|
1349
1600
|
}
|
|
1350
|
-
mobile.push({ device, ok });
|
|
1601
|
+
mobile.push({ device, ok, status: navStatus });
|
|
1351
1602
|
}
|
|
1352
1603
|
results.push({
|
|
1353
1604
|
url,
|
|
@@ -1895,4 +2146,4 @@ export {
|
|
|
1895
2146
|
runAudits,
|
|
1896
2147
|
runAuditsAcross
|
|
1897
2148
|
};
|
|
1898
|
-
//# sourceMappingURL=chunk-
|
|
2149
|
+
//# sourceMappingURL=chunk-PERP7LSS.js.map
|