@goflag/cli 0.2.1 → 0.2.3
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-6WOWKLXY.js → chunk-F74SRZVM.js} +140 -12
- package/dist/chunk-F74SRZVM.js.map +1 -0
- package/dist/cli.js +34 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +34 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-6WOWKLXY.js.map +0 -1
|
@@ -1925,10 +1925,10 @@ function dedupeBy(items, key) {
|
|
|
1925
1925
|
return out;
|
|
1926
1926
|
}
|
|
1927
1927
|
function splitRoute(pathname) {
|
|
1928
|
-
const
|
|
1929
|
-
if (
|
|
1930
|
-
const locale = localeIdentity(
|
|
1931
|
-
const rest =
|
|
1928
|
+
const segments2 = pathname.split("/").filter(Boolean);
|
|
1929
|
+
if (segments2.length > 0 && looksLikeLocaleSegment(segments2[0])) {
|
|
1930
|
+
const locale = localeIdentity(segments2[0]);
|
|
1931
|
+
const rest = segments2.slice(1).join("/");
|
|
1932
1932
|
return { route: rest ? `/${rest}` : "/", locale };
|
|
1933
1933
|
}
|
|
1934
1934
|
return { route: pathname || "/", locale: "x-default" };
|
|
@@ -2289,7 +2289,12 @@ function parseSitemap(body) {
|
|
|
2289
2289
|
|
|
2290
2290
|
// src/lib/core/sitemap/discover.ts
|
|
2291
2291
|
var DEFAULTS = {
|
|
2292
|
-
|
|
2292
|
+
// A sitemap is one document and it can be large: this repository's own
|
|
2293
|
+
// openfinanceguide serves 3.5 MB across 4008 URLs, and that took 1.2 s on a
|
|
2294
|
+
// good connection. Eight seconds was tight enough that the fetch failed
|
|
2295
|
+
// intermittently, and every failure silently dropped the crawl to link
|
|
2296
|
+
// following. Twenty is generous for a document fetched once per run.
|
|
2297
|
+
timeoutMs: 2e4,
|
|
2293
2298
|
maxUrls: 5e3,
|
|
2294
2299
|
maxSitemaps: 50,
|
|
2295
2300
|
crawlDepth: 1,
|
|
@@ -2332,6 +2337,10 @@ async function discoverSitemap(baseUrl, options = {}) {
|
|
|
2332
2337
|
const doc = await fetchDoc(candidate.url, options);
|
|
2333
2338
|
if (!doc.body) {
|
|
2334
2339
|
if (diagnostics.status === 0) diagnostics.status = doc.status;
|
|
2340
|
+
if (doc.error) {
|
|
2341
|
+
diagnostics.unreachable ??= `${candidate.url}: ${doc.error}`;
|
|
2342
|
+
diagnostics.warnings.push(`Sitemap unreachable at ${candidate.url} \u2014 ${doc.error}`);
|
|
2343
|
+
}
|
|
2335
2344
|
continue;
|
|
2336
2345
|
}
|
|
2337
2346
|
const parsed = parseSitemap(doc.body);
|
|
@@ -2472,8 +2481,9 @@ async function fetchDoc(url, options) {
|
|
|
2472
2481
|
}
|
|
2473
2482
|
}
|
|
2474
2483
|
return { status: res.status, body: await res.text() };
|
|
2475
|
-
} catch {
|
|
2476
|
-
|
|
2484
|
+
} catch (err) {
|
|
2485
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2486
|
+
return { status: 0, error: reason || "network error" };
|
|
2477
2487
|
} finally {
|
|
2478
2488
|
cleanup();
|
|
2479
2489
|
restoreTls();
|
|
@@ -2489,6 +2499,90 @@ function relaxTlsIfRequested2(enabled) {
|
|
|
2489
2499
|
};
|
|
2490
2500
|
}
|
|
2491
2501
|
|
|
2502
|
+
// src/lib/core/coverage.ts
|
|
2503
|
+
var DEFAULT_PER_FAMILY = 3;
|
|
2504
|
+
var DEFAULT_THRESHOLD = 8;
|
|
2505
|
+
function segments(url) {
|
|
2506
|
+
try {
|
|
2507
|
+
return new URL(url).pathname.split("/").filter(Boolean);
|
|
2508
|
+
} catch {
|
|
2509
|
+
return [];
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
function shapeOf(parts, locales) {
|
|
2513
|
+
return parts.map((part) => locales.has(part.toLowerCase()) ? "{locale}" : part);
|
|
2514
|
+
}
|
|
2515
|
+
function patternOf(parts, varDepths) {
|
|
2516
|
+
const out = [];
|
|
2517
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
2518
|
+
const prefix = parts.slice(0, i).join("/");
|
|
2519
|
+
out.push(varDepths.has(`${i}:${prefix}`) ? `{${i}}` : parts[i]);
|
|
2520
|
+
}
|
|
2521
|
+
return `/${out.join("/")}`;
|
|
2522
|
+
}
|
|
2523
|
+
function representatives(sorted2, k) {
|
|
2524
|
+
if (sorted2.length <= k) return [...sorted2];
|
|
2525
|
+
if (k <= 1) return [sorted2[0]];
|
|
2526
|
+
const picked = [];
|
|
2527
|
+
for (let i = 0; i < k; i += 1) {
|
|
2528
|
+
picked.push(sorted2[Math.round(i * (sorted2.length - 1) / (k - 1))]);
|
|
2529
|
+
}
|
|
2530
|
+
return [...new Set(picked)];
|
|
2531
|
+
}
|
|
2532
|
+
function selectByStructure(urls, options = {}) {
|
|
2533
|
+
const perFamily = options.perFamily ?? DEFAULT_PER_FAMILY;
|
|
2534
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
2535
|
+
const locales = new Set((options.locales ?? []).map((l) => l.toLowerCase()));
|
|
2536
|
+
const parsed = urls.map((url) => ({ url, parts: shapeOf(segments(url), locales) }));
|
|
2537
|
+
const childrenByPrefix = /* @__PURE__ */ new Map();
|
|
2538
|
+
for (const { parts } of parsed) {
|
|
2539
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
2540
|
+
const key = `${i}:${parts.slice(0, i).join("/")}`;
|
|
2541
|
+
const set = childrenByPrefix.get(key) ?? /* @__PURE__ */ new Set();
|
|
2542
|
+
set.add(parts[i]);
|
|
2543
|
+
childrenByPrefix.set(key, set);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
const varDepths = /* @__PURE__ */ new Set();
|
|
2547
|
+
for (const [key, children] of childrenByPrefix) {
|
|
2548
|
+
const colon = key.indexOf(":");
|
|
2549
|
+
const depth = Number(key.slice(0, colon));
|
|
2550
|
+
const prefix = key.slice(colon + 1);
|
|
2551
|
+
const isFirstSection = depth === 0 || depth === 1 && prefix === "{locale}";
|
|
2552
|
+
if (isFirstSection) continue;
|
|
2553
|
+
if (children.size >= threshold) varDepths.add(key);
|
|
2554
|
+
}
|
|
2555
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2556
|
+
for (const { url, parts } of parsed) {
|
|
2557
|
+
const pattern = patternOf(parts, varDepths);
|
|
2558
|
+
const localePart = segments(url).find((p) => locales.has(p.toLowerCase())) ?? "";
|
|
2559
|
+
const key = `${localePart}|${pattern}`;
|
|
2560
|
+
const group = groups.get(key) ?? { pattern, urls: [] };
|
|
2561
|
+
group.urls.push(url);
|
|
2562
|
+
groups.set(key, group);
|
|
2563
|
+
}
|
|
2564
|
+
const keep = /* @__PURE__ */ new Set();
|
|
2565
|
+
const families = /* @__PURE__ */ new Map();
|
|
2566
|
+
for (const { pattern, urls: groupUrls } of groups.values()) {
|
|
2567
|
+
const sorted2 = [...groupUrls].sort();
|
|
2568
|
+
const chosen = sorted2.length < threshold ? sorted2 : representatives(sorted2, perFamily);
|
|
2569
|
+
for (const url of chosen) keep.add(url);
|
|
2570
|
+
if (sorted2.length >= threshold) {
|
|
2571
|
+
const seen = families.get(pattern);
|
|
2572
|
+
families.set(pattern, {
|
|
2573
|
+
pattern,
|
|
2574
|
+
size: (seen?.size ?? 0) + sorted2.length,
|
|
2575
|
+
sampled: (seen?.sampled ?? 0) + chosen.length
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
return {
|
|
2580
|
+
urls: urls.filter((url) => keep.has(url)),
|
|
2581
|
+
families: [...families.values()].sort((a, b) => b.size - a.size),
|
|
2582
|
+
total: urls.length
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2492
2586
|
// src/lib/rules/advisory.ts
|
|
2493
2587
|
function resolveEvidencePath(extraction, path) {
|
|
2494
2588
|
let current = extraction;
|
|
@@ -3402,7 +3496,11 @@ async function runAudit(inputUrl, options = {}) {
|
|
|
3402
3496
|
allowInsecureTls: options.allowInsecureTls,
|
|
3403
3497
|
crawlFallback: false
|
|
3404
3498
|
}).catch(() => void 0);
|
|
3405
|
-
const
|
|
3499
|
+
const allSitemapUrls = (discovery?.urls ?? []).map((u) => u.loc);
|
|
3500
|
+
const coverageMode = options.coverage ?? (allSitemapUrls.length > 0 ? "structural" : "all");
|
|
3501
|
+
const selection = coverageMode === "structural" && allSitemapUrls.length > 0 ? selectByStructure(allSitemapUrls, { locales: options.locales }) : void 0;
|
|
3502
|
+
const sitemapUrls = selection?.urls ?? allSitemapUrls;
|
|
3503
|
+
const effectiveMaxPages = selection ? Math.max(options.maxPages ?? maxPages, selection.urls.length + 5) : maxPages;
|
|
3406
3504
|
const robots = await probeRobots(origin, {
|
|
3407
3505
|
signal: options.signal,
|
|
3408
3506
|
timeoutMs: options.timeoutMs
|
|
@@ -3411,7 +3509,7 @@ async function runAudit(inputUrl, options = {}) {
|
|
|
3411
3509
|
entryUrl: entry,
|
|
3412
3510
|
seedUrls: sitemapUrls,
|
|
3413
3511
|
depth,
|
|
3414
|
-
maxPages,
|
|
3512
|
+
maxPages: effectiveMaxPages,
|
|
3415
3513
|
include: options.include,
|
|
3416
3514
|
exclude: options.exclude,
|
|
3417
3515
|
inspectOptions: {
|
|
@@ -3621,14 +3719,26 @@ async function runAudit(inputUrl, options = {}) {
|
|
|
3621
3719
|
pagesFailed: linkReport.diagnostics.pagesFailed,
|
|
3622
3720
|
truncated: crawlResult.truncated || linkReport.truncated,
|
|
3623
3721
|
warnings,
|
|
3722
|
+
// What the run actually looked at. A sampled audit that does not say so
|
|
3723
|
+
// is a partial audit wearing the face of a complete one, which is worse
|
|
3724
|
+
// than the cap it replaces.
|
|
3725
|
+
coverage: {
|
|
3726
|
+
mode: coverageMode,
|
|
3727
|
+
...selection ? {
|
|
3728
|
+
considered: selection.total,
|
|
3729
|
+
selected: selection.urls.length,
|
|
3730
|
+
families: selection.families
|
|
3731
|
+
} : {}
|
|
3732
|
+
},
|
|
3624
3733
|
...ignoredHoles > 0 ? { ignoredHoles } : {},
|
|
3625
3734
|
...duplicatePages > 0 ? { duplicatePages } : {},
|
|
3626
3735
|
...discovery ? {
|
|
3627
3736
|
sitemap: {
|
|
3628
3737
|
found: discovery.diagnostics.found,
|
|
3629
3738
|
sitemapUrl: discovery.diagnostics.sitemapUrl,
|
|
3630
|
-
urlCount:
|
|
3631
|
-
uncrawled: countUncrawled(sitemapUrls, pages)
|
|
3739
|
+
urlCount: allSitemapUrls.length,
|
|
3740
|
+
uncrawled: countUncrawled(sitemapUrls, pages),
|
|
3741
|
+
...discovery.diagnostics.unreachable ? { unreachable: discovery.diagnostics.unreachable } : {}
|
|
3632
3742
|
}
|
|
3633
3743
|
} : {}
|
|
3634
3744
|
}
|
|
@@ -3699,6 +3809,24 @@ function renderTerminal(report, options = {}) {
|
|
|
3699
3809
|
(report.profile === DEFAULT_PROFILE ? "" : `, profile ${report.profile}`)
|
|
3700
3810
|
)
|
|
3701
3811
|
);
|
|
3812
|
+
const coverage = report.diagnostics.coverage;
|
|
3813
|
+
if (coverage?.families?.length && coverage.considered && coverage.selected) {
|
|
3814
|
+
const biggest = coverage.families[0];
|
|
3815
|
+
const audited = report.diagnostics.pagesCrawled;
|
|
3816
|
+
const capped = audited < coverage.selected ? `, capped by --max-pages` : "";
|
|
3817
|
+
lines.push(
|
|
3818
|
+
c(
|
|
3819
|
+
ANSI.dim,
|
|
3820
|
+
`COVERAGE ${audited} of ${coverage.considered} pages audited${capped} \xB7 ${coverage.families.length} families sampled, largest ${biggest.sampled}/${biggest.size} ${biggest.pattern}`
|
|
3821
|
+
)
|
|
3822
|
+
);
|
|
3823
|
+
lines.push(
|
|
3824
|
+
c(
|
|
3825
|
+
ANSI.dim,
|
|
3826
|
+
` Template rules are conclusive. Copy rules \u2014 title.length, description.length \u2014 are sampled.`
|
|
3827
|
+
)
|
|
3828
|
+
);
|
|
3829
|
+
}
|
|
3702
3830
|
lines.push("");
|
|
3703
3831
|
lines.push(
|
|
3704
3832
|
[
|
|
@@ -4275,4 +4403,4 @@ export {
|
|
|
4275
4403
|
summarize,
|
|
4276
4404
|
Logger
|
|
4277
4405
|
};
|
|
4278
|
-
//# sourceMappingURL=chunk-
|
|
4406
|
+
//# sourceMappingURL=chunk-F74SRZVM.js.map
|