agentimization 0.1.1 → 0.1.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/index.js +557 -146
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4815,11 +4815,11 @@ var extractMetaTags = (html) => {
|
|
|
4815
4815
|
const metaRegex = /<meta[^>]+(?:name|property)=["']([^"']+)["'][^>]+content=["']([^"']+)["']/gi;
|
|
4816
4816
|
let match;
|
|
4817
4817
|
while ((match = metaRegex.exec(html)) !== null) {
|
|
4818
|
-
meta[match[1]] = match[2];
|
|
4818
|
+
meta[match[1].toLowerCase()] = match[2];
|
|
4819
4819
|
}
|
|
4820
4820
|
const metaRegex2 = /<meta[^>]+content=["']([^"']+)["'][^>]+(?:name|property)=["']([^"']+)["']/gi;
|
|
4821
4821
|
while ((match = metaRegex2.exec(html)) !== null) {
|
|
4822
|
-
meta[match[2]] = match[1];
|
|
4822
|
+
meta[match[2].toLowerCase()] = match[1];
|
|
4823
4823
|
}
|
|
4824
4824
|
return meta;
|
|
4825
4825
|
};
|
|
@@ -4835,6 +4835,24 @@ var extractJsonLd = (html) => {
|
|
|
4835
4835
|
}
|
|
4836
4836
|
return results;
|
|
4837
4837
|
};
|
|
4838
|
+
var readAttr = (attrs, name) => {
|
|
4839
|
+
const re = new RegExp(`\\b${name}=(?:"([^"]*)"|'([^']*)')`, "i");
|
|
4840
|
+
const m = attrs.match(re);
|
|
4841
|
+
if (!m) return void 0;
|
|
4842
|
+
return m[1] ?? m[2];
|
|
4843
|
+
};
|
|
4844
|
+
var extractImages = (html) => {
|
|
4845
|
+
const images = [];
|
|
4846
|
+
const imgRegex = /<img\b([^>]*)>/gi;
|
|
4847
|
+
let match;
|
|
4848
|
+
while ((match = imgRegex.exec(html)) !== null) {
|
|
4849
|
+
const attrs = match[1];
|
|
4850
|
+
const src = readAttr(attrs, "src");
|
|
4851
|
+
if (src === void 0) continue;
|
|
4852
|
+
images.push({ src, alt: readAttr(attrs, "alt") });
|
|
4853
|
+
}
|
|
4854
|
+
return images;
|
|
4855
|
+
};
|
|
4838
4856
|
var extractHeadings = (html) => {
|
|
4839
4857
|
const headings = [];
|
|
4840
4858
|
const regex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
@@ -4946,6 +4964,53 @@ var renderingStrategy = {
|
|
|
4946
4964
|
};
|
|
4947
4965
|
}
|
|
4948
4966
|
};
|
|
4967
|
+
var substantialTextContent = {
|
|
4968
|
+
id: "substantial-text-content",
|
|
4969
|
+
name: "Substantial Text Content",
|
|
4970
|
+
category: "page-size",
|
|
4971
|
+
description: "Checks for at least 100 words of readable body text",
|
|
4972
|
+
weight: 0.8,
|
|
4973
|
+
run: async (ctx) => {
|
|
4974
|
+
const pages = ctx.sampledPages.slice(0, 10);
|
|
4975
|
+
if (pages.length === 0) {
|
|
4976
|
+
return {
|
|
4977
|
+
id: "substantial-text-content",
|
|
4978
|
+
name: "Substantial Text Content",
|
|
4979
|
+
category: "page-size",
|
|
4980
|
+
status: "skip",
|
|
4981
|
+
message: "No pages sampled"
|
|
4982
|
+
};
|
|
4983
|
+
}
|
|
4984
|
+
let withSubstantialContent = 0;
|
|
4985
|
+
let totalWords = 0;
|
|
4986
|
+
for (const page of pages) {
|
|
4987
|
+
const text = stripHtml(page.html);
|
|
4988
|
+
const words = text.split(/\s+/).filter((w) => w.length > 0).length;
|
|
4989
|
+
totalWords += words;
|
|
4990
|
+
if (words >= 100) withSubstantialContent++;
|
|
4991
|
+
}
|
|
4992
|
+
const avgWords = Math.round(totalWords / pages.length);
|
|
4993
|
+
if (withSubstantialContent === pages.length) {
|
|
4994
|
+
return {
|
|
4995
|
+
id: "substantial-text-content",
|
|
4996
|
+
name: "Substantial Text Content",
|
|
4997
|
+
category: "page-size",
|
|
4998
|
+
status: "pass",
|
|
4999
|
+
message: `All ${pages.length} pages have \u2265100 words of body text (avg ${avgWords})`,
|
|
5000
|
+
metadata: { withSubstantialContent, avgWords }
|
|
5001
|
+
};
|
|
5002
|
+
}
|
|
5003
|
+
return {
|
|
5004
|
+
id: "substantial-text-content",
|
|
5005
|
+
name: "Substantial Text Content",
|
|
5006
|
+
category: "page-size",
|
|
5007
|
+
status: withSubstantialContent > 0 ? "warn" : "fail",
|
|
5008
|
+
message: `${withSubstantialContent}/${pages.length} pages have \u2265100 words of body text (avg ${avgWords})`,
|
|
5009
|
+
suggestion: "Generative engines can't cite pages that are mostly images or short copy. Add at least 100 words of substantive text content per page.",
|
|
5010
|
+
metadata: { withSubstantialContent, avgWords }
|
|
5011
|
+
};
|
|
5012
|
+
}
|
|
5013
|
+
};
|
|
4949
5014
|
var pageSizeHtml = {
|
|
4950
5015
|
id: "page-size-html",
|
|
4951
5016
|
name: "Page Size (HTML)",
|
|
@@ -5080,6 +5145,7 @@ var contentStartPosition = {
|
|
|
5080
5145
|
};
|
|
5081
5146
|
var pageSizeChecks = [
|
|
5082
5147
|
renderingStrategy,
|
|
5148
|
+
substantialTextContent,
|
|
5083
5149
|
pageSizeHtml,
|
|
5084
5150
|
pageSizeMarkdown,
|
|
5085
5151
|
contentStartPosition
|
|
@@ -5251,11 +5317,103 @@ var tabbedContentSerialization = {
|
|
|
5251
5317
|
};
|
|
5252
5318
|
}
|
|
5253
5319
|
};
|
|
5320
|
+
var imageAltText = {
|
|
5321
|
+
id: "image-alt-text",
|
|
5322
|
+
name: "Image Alt Text Coverage",
|
|
5323
|
+
category: "content-structure",
|
|
5324
|
+
description: "Checks that at least 50% of images have descriptive alt text",
|
|
5325
|
+
weight: 0.5,
|
|
5326
|
+
run: async (ctx) => {
|
|
5327
|
+
const pages = ctx.sampledPages.slice(0, 10);
|
|
5328
|
+
if (pages.length === 0) {
|
|
5329
|
+
return {
|
|
5330
|
+
id: "image-alt-text",
|
|
5331
|
+
name: "Image Alt Text Coverage",
|
|
5332
|
+
category: "content-structure",
|
|
5333
|
+
status: "skip",
|
|
5334
|
+
message: "No pages sampled"
|
|
5335
|
+
};
|
|
5336
|
+
}
|
|
5337
|
+
const allImages = pages.flatMap((p) => extractImages(p.html));
|
|
5338
|
+
const contentImages = allImages.filter((img) => img.alt === void 0 || img.alt.trim().length > 0);
|
|
5339
|
+
const decorativeImages = allImages.length - contentImages.length;
|
|
5340
|
+
const withAlt = contentImages.filter((img) => img.alt !== void 0 && img.alt.trim().length > 0).length;
|
|
5341
|
+
if (allImages.length === 0) {
|
|
5342
|
+
return {
|
|
5343
|
+
id: "image-alt-text",
|
|
5344
|
+
name: "Image Alt Text Coverage",
|
|
5345
|
+
category: "content-structure",
|
|
5346
|
+
status: "info",
|
|
5347
|
+
message: `No images found across ${pages.length} sampled pages`
|
|
5348
|
+
};
|
|
5349
|
+
}
|
|
5350
|
+
if (contentImages.length === 0) {
|
|
5351
|
+
return {
|
|
5352
|
+
id: "image-alt-text",
|
|
5353
|
+
name: "Image Alt Text Coverage",
|
|
5354
|
+
category: "content-structure",
|
|
5355
|
+
status: "info",
|
|
5356
|
+
message: `All ${allImages.length} sampled images are decorative (alt="")`,
|
|
5357
|
+
metadata: { decorativeImages, totalImages: allImages.length }
|
|
5358
|
+
};
|
|
5359
|
+
}
|
|
5360
|
+
const ratio = withAlt / contentImages.length;
|
|
5361
|
+
const pct = Math.round(ratio * 100);
|
|
5362
|
+
const summary = `${withAlt}/${contentImages.length} content images have descriptive alt text (${pct}%)${decorativeImages > 0 ? `; ${decorativeImages} decorative skipped` : ""}`;
|
|
5363
|
+
if (ratio >= 0.5) {
|
|
5364
|
+
return {
|
|
5365
|
+
id: "image-alt-text",
|
|
5366
|
+
name: "Image Alt Text Coverage",
|
|
5367
|
+
category: "content-structure",
|
|
5368
|
+
status: "pass",
|
|
5369
|
+
message: summary,
|
|
5370
|
+
metadata: { withAlt, contentImages: contentImages.length, decorativeImages, pct }
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
return {
|
|
5374
|
+
id: "image-alt-text",
|
|
5375
|
+
name: "Image Alt Text Coverage",
|
|
5376
|
+
category: "content-structure",
|
|
5377
|
+
status: ratio >= 0.25 ? "warn" : "fail",
|
|
5378
|
+
message: summary,
|
|
5379
|
+
suggestion: `Add descriptive alt text to at least 50% of content images. AI agents and screen readers rely on alt text to understand visual content. Mark purely decorative images with alt="" so they don't dilute the ratio.`,
|
|
5380
|
+
metadata: { withAlt, contentImages: contentImages.length, decorativeImages, pct }
|
|
5381
|
+
};
|
|
5382
|
+
}
|
|
5383
|
+
};
|
|
5254
5384
|
var contentStructureChecks = [
|
|
5255
5385
|
markdownCodeFenceValidity,
|
|
5256
5386
|
sectionHeaderQuality,
|
|
5257
|
-
tabbedContentSerialization
|
|
5387
|
+
tabbedContentSerialization,
|
|
5388
|
+
imageAltText
|
|
5258
5389
|
];
|
|
5390
|
+
var httpsEnabled = {
|
|
5391
|
+
id: "https-enabled",
|
|
5392
|
+
name: "HTTPS Enabled",
|
|
5393
|
+
category: "url-stability",
|
|
5394
|
+
description: "Checks if the site is served over HTTPS",
|
|
5395
|
+
weight: 0.7,
|
|
5396
|
+
requiresNetwork: true,
|
|
5397
|
+
run: async (ctx) => {
|
|
5398
|
+
if (ctx.baseUrl.protocol === "https:") {
|
|
5399
|
+
return {
|
|
5400
|
+
id: "https-enabled",
|
|
5401
|
+
name: "HTTPS Enabled",
|
|
5402
|
+
category: "url-stability",
|
|
5403
|
+
status: "pass",
|
|
5404
|
+
message: "Site is served over HTTPS"
|
|
5405
|
+
};
|
|
5406
|
+
}
|
|
5407
|
+
return {
|
|
5408
|
+
id: "https-enabled",
|
|
5409
|
+
name: "HTTPS Enabled",
|
|
5410
|
+
category: "url-stability",
|
|
5411
|
+
status: "fail",
|
|
5412
|
+
message: `Site is served over ${ctx.baseUrl.protocol.replace(":", "")} \u2014 AI crawlers de-prioritize non-HTTPS sources`,
|
|
5413
|
+
suggestion: "Serve your site over HTTPS. AI crawlers like GPTBot, ClaudeBot, and PerplexityBot strongly prefer HTTPS and may skip plain HTTP entirely."
|
|
5414
|
+
};
|
|
5415
|
+
}
|
|
5416
|
+
};
|
|
5259
5417
|
var httpStatusCodes = {
|
|
5260
5418
|
id: "http-status-codes",
|
|
5261
5419
|
name: "HTTP Status Codes",
|
|
@@ -5367,6 +5525,7 @@ var cacheHeaderHygiene = {
|
|
|
5367
5525
|
}
|
|
5368
5526
|
};
|
|
5369
5527
|
var urlStabilityChecks = [
|
|
5528
|
+
httpsEnabled,
|
|
5370
5529
|
httpStatusCodes,
|
|
5371
5530
|
redirectBehavior,
|
|
5372
5531
|
cacheHeaderHygiene
|
|
@@ -5772,6 +5931,185 @@ var faqSchema = {
|
|
|
5772
5931
|
};
|
|
5773
5932
|
}
|
|
5774
5933
|
};
|
|
5934
|
+
var metaDescription = {
|
|
5935
|
+
id: "meta-description",
|
|
5936
|
+
name: "Meta Description",
|
|
5937
|
+
category: "geo-signals",
|
|
5938
|
+
description: "Checks for a meta description between 50 and 160 characters",
|
|
5939
|
+
weight: 0.5,
|
|
5940
|
+
run: async (ctx) => {
|
|
5941
|
+
const pages = ctx.sampledPages.slice(0, 10);
|
|
5942
|
+
if (pages.length === 0) {
|
|
5943
|
+
return {
|
|
5944
|
+
id: "meta-description",
|
|
5945
|
+
name: "Meta Description",
|
|
5946
|
+
category: "geo-signals",
|
|
5947
|
+
status: "skip",
|
|
5948
|
+
message: "No pages sampled"
|
|
5949
|
+
};
|
|
5950
|
+
}
|
|
5951
|
+
let withGoodDescription = 0;
|
|
5952
|
+
let missing = 0;
|
|
5953
|
+
let tooShort = 0;
|
|
5954
|
+
let tooLong = 0;
|
|
5955
|
+
for (const page of pages) {
|
|
5956
|
+
const meta = extractMetaTags(page.html);
|
|
5957
|
+
const description = meta["description"]?.trim();
|
|
5958
|
+
if (!description) {
|
|
5959
|
+
missing++;
|
|
5960
|
+
continue;
|
|
5961
|
+
}
|
|
5962
|
+
const len = description.length;
|
|
5963
|
+
if (len >= 50 && len <= 160) withGoodDescription++;
|
|
5964
|
+
else if (len < 50) tooShort++;
|
|
5965
|
+
else tooLong++;
|
|
5966
|
+
}
|
|
5967
|
+
if (withGoodDescription === pages.length) {
|
|
5968
|
+
return {
|
|
5969
|
+
id: "meta-description",
|
|
5970
|
+
name: "Meta Description",
|
|
5971
|
+
category: "geo-signals",
|
|
5972
|
+
status: "pass",
|
|
5973
|
+
message: `All ${pages.length} pages have a meta description between 50\u2013160 characters`,
|
|
5974
|
+
metadata: { withGoodDescription }
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
if (missing === pages.length) {
|
|
5978
|
+
return {
|
|
5979
|
+
id: "meta-description",
|
|
5980
|
+
name: "Meta Description",
|
|
5981
|
+
category: "geo-signals",
|
|
5982
|
+
status: "fail",
|
|
5983
|
+
message: "No meta description found on any sampled page",
|
|
5984
|
+
suggestion: 'Add a <meta name="description"> between 50 and 160 characters to every page. Generative engines quote meta descriptions when summarizing your content.'
|
|
5985
|
+
};
|
|
5986
|
+
}
|
|
5987
|
+
const detail = [
|
|
5988
|
+
missing > 0 ? `${missing} missing` : null,
|
|
5989
|
+
tooShort > 0 ? `${tooShort} too short` : null,
|
|
5990
|
+
tooLong > 0 ? `${tooLong} too long` : null
|
|
5991
|
+
].filter(Boolean).join(" \xB7 ");
|
|
5992
|
+
return {
|
|
5993
|
+
id: "meta-description",
|
|
5994
|
+
name: "Meta Description",
|
|
5995
|
+
category: "geo-signals",
|
|
5996
|
+
status: missing >= pages.length / 2 ? "fail" : "warn",
|
|
5997
|
+
message: `${withGoodDescription}/${pages.length} pages have meta descriptions in the 50\u2013160 char range${detail ? ` \xB7 ${detail}` : ""}`,
|
|
5998
|
+
suggestion: missing > 0 ? 'Add a <meta name="description"> between 50 and 160 characters to every page. Some pages are missing it entirely.' : "Aim for 50\u2013160 characters. Shorter descriptions lack context for AI; longer ones get truncated.",
|
|
5999
|
+
metadata: { withGoodDescription, missing, tooShort, tooLong }
|
|
6000
|
+
};
|
|
6001
|
+
}
|
|
6002
|
+
};
|
|
6003
|
+
var openGraphTags = {
|
|
6004
|
+
id: "open-graph-tags",
|
|
6005
|
+
name: "Open Graph Tags",
|
|
6006
|
+
category: "geo-signals",
|
|
6007
|
+
description: "Checks for og:title, og:description, og:image, and og:url",
|
|
6008
|
+
weight: 0.5,
|
|
6009
|
+
run: async (ctx) => {
|
|
6010
|
+
const pages = ctx.sampledPages.slice(0, 10);
|
|
6011
|
+
if (pages.length === 0) {
|
|
6012
|
+
return {
|
|
6013
|
+
id: "open-graph-tags",
|
|
6014
|
+
name: "Open Graph Tags",
|
|
6015
|
+
category: "geo-signals",
|
|
6016
|
+
status: "skip",
|
|
6017
|
+
message: "No pages sampled"
|
|
6018
|
+
};
|
|
6019
|
+
}
|
|
6020
|
+
const required = ["og:title", "og:description", "og:image", "og:url"];
|
|
6021
|
+
let fullCoverage = 0;
|
|
6022
|
+
let partialCoverage = 0;
|
|
6023
|
+
const missingCounts = { "og:title": 0, "og:description": 0, "og:image": 0, "og:url": 0 };
|
|
6024
|
+
for (const page of pages) {
|
|
6025
|
+
const meta = extractMetaTags(page.html);
|
|
6026
|
+
const missing = required.filter((tag) => !meta[tag]);
|
|
6027
|
+
for (const tag of missing) missingCounts[tag] = (missingCounts[tag] ?? 0) + 1;
|
|
6028
|
+
if (missing.length === 0) fullCoverage++;
|
|
6029
|
+
else if (missing.length < required.length) partialCoverage++;
|
|
6030
|
+
}
|
|
6031
|
+
if (fullCoverage === pages.length) {
|
|
6032
|
+
return {
|
|
6033
|
+
id: "open-graph-tags",
|
|
6034
|
+
name: "Open Graph Tags",
|
|
6035
|
+
category: "geo-signals",
|
|
6036
|
+
status: "pass",
|
|
6037
|
+
message: `All ${pages.length} pages have complete Open Graph tags`
|
|
6038
|
+
};
|
|
6039
|
+
}
|
|
6040
|
+
const mostMissing = Object.entries(missingCounts).filter(([, n]) => n > 0).sort(([, a], [, b]) => b - a).map(([tag]) => tag);
|
|
6041
|
+
const noneCovered = pages.length - fullCoverage - partialCoverage;
|
|
6042
|
+
return {
|
|
6043
|
+
id: "open-graph-tags",
|
|
6044
|
+
name: "Open Graph Tags",
|
|
6045
|
+
category: "geo-signals",
|
|
6046
|
+
status: fullCoverage + partialCoverage === 0 ? "fail" : "warn",
|
|
6047
|
+
message: `${fullCoverage}/${pages.length} pages have complete Open Graph tags${partialCoverage > 0 ? ` \xB7 ${partialCoverage} partial` : ""}${noneCovered > 0 ? ` \xB7 ${noneCovered} with none` : ""}${mostMissing.length > 0 ? ` \xB7 most often missing: ${mostMissing.slice(0, 2).join(", ")}` : ""}`,
|
|
6048
|
+
suggestion: "Add og:title, og:description, og:image, and og:url to every page. AI engines and link previews use these to render rich citations of your content.",
|
|
6049
|
+
metadata: { fullCoverage, partialCoverage, noneCovered, missingCounts }
|
|
6050
|
+
};
|
|
6051
|
+
}
|
|
6052
|
+
};
|
|
6053
|
+
var externalCitations = {
|
|
6054
|
+
id: "external-citations",
|
|
6055
|
+
name: "External Citations",
|
|
6056
|
+
category: "geo-signals",
|
|
6057
|
+
description: "Checks for at least 2 outbound links to external sources per page",
|
|
6058
|
+
weight: 0.5,
|
|
6059
|
+
run: async (ctx) => {
|
|
6060
|
+
if (ctx.mode === "local") {
|
|
6061
|
+
return {
|
|
6062
|
+
id: "external-citations",
|
|
6063
|
+
name: "External Citations",
|
|
6064
|
+
category: "geo-signals",
|
|
6065
|
+
status: "info",
|
|
6066
|
+
message: "External link detection requires a live origin to compare against"
|
|
6067
|
+
};
|
|
6068
|
+
}
|
|
6069
|
+
const pages = ctx.sampledPages.slice(0, 10);
|
|
6070
|
+
if (pages.length === 0) {
|
|
6071
|
+
return {
|
|
6072
|
+
id: "external-citations",
|
|
6073
|
+
name: "External Citations",
|
|
6074
|
+
category: "geo-signals",
|
|
6075
|
+
status: "skip",
|
|
6076
|
+
message: "No pages sampled"
|
|
6077
|
+
};
|
|
6078
|
+
}
|
|
6079
|
+
const origin = ctx.baseUrl.origin;
|
|
6080
|
+
let pagesWithCitations = 0;
|
|
6081
|
+
let totalExternal = 0;
|
|
6082
|
+
for (const page of pages) {
|
|
6083
|
+
const links = extractLinks(page.html, origin);
|
|
6084
|
+
const external = links.filter((l) => {
|
|
6085
|
+
const u = new URL(l);
|
|
6086
|
+
return u.protocol.startsWith("http") && u.origin !== origin;
|
|
6087
|
+
});
|
|
6088
|
+
totalExternal += external.length;
|
|
6089
|
+
if (external.length >= 2) pagesWithCitations++;
|
|
6090
|
+
}
|
|
6091
|
+
const avgExternal = Math.round(totalExternal / pages.length);
|
|
6092
|
+
if (pagesWithCitations >= pages.length * 0.7) {
|
|
6093
|
+
return {
|
|
6094
|
+
id: "external-citations",
|
|
6095
|
+
name: "External Citations",
|
|
6096
|
+
category: "geo-signals",
|
|
6097
|
+
status: "pass",
|
|
6098
|
+
message: `${pagesWithCitations}/${pages.length} pages have \u22652 outbound links (avg ${avgExternal}/page)`,
|
|
6099
|
+
metadata: { pagesWithCitations, avgExternal }
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
6102
|
+
return {
|
|
6103
|
+
id: "external-citations",
|
|
6104
|
+
name: "External Citations",
|
|
6105
|
+
category: "geo-signals",
|
|
6106
|
+
status: pagesWithCitations > 0 ? "warn" : "fail",
|
|
6107
|
+
message: `Only ${pagesWithCitations}/${pages.length} pages have \u22652 outbound links (avg ${avgExternal}/page)`,
|
|
6108
|
+
suggestion: "Add at least 2 outbound links to authoritative external sources per page. Citing sources signals credibility to generative engines, which weigh outbound links when deciding what to cite.",
|
|
6109
|
+
metadata: { pagesWithCitations, avgExternal }
|
|
6110
|
+
};
|
|
6111
|
+
}
|
|
6112
|
+
};
|
|
5775
6113
|
var canonicalUrlConsistency = {
|
|
5776
6114
|
id: "canonical-url-consistency",
|
|
5777
6115
|
name: "Canonical URL Consistency",
|
|
@@ -5842,6 +6180,9 @@ var geoSignalChecks = [
|
|
|
5842
6180
|
contentFreshness,
|
|
5843
6181
|
eeatSignals,
|
|
5844
6182
|
faqSchema,
|
|
6183
|
+
metaDescription,
|
|
6184
|
+
openGraphTags,
|
|
6185
|
+
externalCitations,
|
|
5845
6186
|
canonicalUrlConsistency
|
|
5846
6187
|
];
|
|
5847
6188
|
var mcpServerCard = {
|
|
@@ -6551,11 +6892,11 @@ var auditLocal = async (dirPath, config = {}) => {
|
|
|
6551
6892
|
};
|
|
6552
6893
|
|
|
6553
6894
|
// src/ui/app.tsx
|
|
6554
|
-
import { useState as useState4
|
|
6555
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
6895
|
+
import { useState as useState4 } from "react";
|
|
6896
|
+
import { Box as Box6, Text as Text6, Static, useStdout } from "ink";
|
|
6556
6897
|
|
|
6557
6898
|
// src/ui/hero-card.tsx
|
|
6558
|
-
import {
|
|
6899
|
+
import { useState } from "react";
|
|
6559
6900
|
import { Box, Text } from "ink";
|
|
6560
6901
|
|
|
6561
6902
|
// src/ui/color.ts
|
|
@@ -6600,11 +6941,16 @@ var toInkColor = (input) => {
|
|
|
6600
6941
|
return out;
|
|
6601
6942
|
};
|
|
6602
6943
|
|
|
6944
|
+
// src/hooks/use-mount-effect.ts
|
|
6945
|
+
import { useEffect } from "react";
|
|
6946
|
+
var useMountEffect = (effect) => {
|
|
6947
|
+
useEffect(effect, []);
|
|
6948
|
+
};
|
|
6949
|
+
|
|
6603
6950
|
// src/ui/hero-card.tsx
|
|
6604
6951
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6605
|
-
var
|
|
6952
|
+
var DEFAULT_CARD_WIDTH = 46;
|
|
6606
6953
|
var PATTERN_ROWS = 4;
|
|
6607
|
-
var PATTERN_COLS = CARD_WIDTH - 4;
|
|
6608
6954
|
var PATTERN_GLYPHS = ["\u2591", "\u2592", "\u2593", "\u2588"];
|
|
6609
6955
|
var ACCENT_BY_PHASE = {
|
|
6610
6956
|
init: "oklch(0.787 0.119 304.8)",
|
|
@@ -6677,10 +7023,10 @@ var MiniLoader = ({
|
|
|
6677
7023
|
intervalMs = 120
|
|
6678
7024
|
}) => {
|
|
6679
7025
|
const [frame, setFrame] = useState(0);
|
|
6680
|
-
|
|
7026
|
+
useMountEffect(() => {
|
|
6681
7027
|
const timer = setInterval(() => setFrame((f) => (f + 1) % 1e3), intervalMs);
|
|
6682
7028
|
return () => clearInterval(timer);
|
|
6683
|
-
}
|
|
7029
|
+
});
|
|
6684
7030
|
const segments = [];
|
|
6685
7031
|
let current = null;
|
|
6686
7032
|
for (let c = 0; c < MINI_COLS; c++) {
|
|
@@ -6702,6 +7048,7 @@ var MiniLoader = ({
|
|
|
6702
7048
|
var Pattern = ({
|
|
6703
7049
|
accent,
|
|
6704
7050
|
frame,
|
|
7051
|
+
cols,
|
|
6705
7052
|
overlay
|
|
6706
7053
|
}) => {
|
|
6707
7054
|
const overlayStart = overlay ? overlay.col : -1;
|
|
@@ -6710,7 +7057,7 @@ var Pattern = ({
|
|
|
6710
7057
|
const segments = [];
|
|
6711
7058
|
let current = null;
|
|
6712
7059
|
const isOverlayRow = overlay && r === overlay.row;
|
|
6713
|
-
for (let c = 0; c <
|
|
7060
|
+
for (let c = 0; c < cols; c++) {
|
|
6714
7061
|
let char;
|
|
6715
7062
|
let color;
|
|
6716
7063
|
if (isOverlayRow && c >= overlayStart && c < overlayEnd) {
|
|
@@ -6758,13 +7105,13 @@ var shimmerColor = (index, head, cycleLen, accent) => {
|
|
|
6758
7105
|
};
|
|
6759
7106
|
var Shimmer = ({ text, accent }) => {
|
|
6760
7107
|
const [head, setHead] = useState(0);
|
|
6761
|
-
|
|
7108
|
+
useMountEffect(() => {
|
|
6762
7109
|
const cycleLen2 = text.length + SHIMMER_GAP;
|
|
6763
7110
|
const timer = setInterval(() => {
|
|
6764
7111
|
setHead((h) => (h + 1) % cycleLen2);
|
|
6765
7112
|
}, SHIMMER_INTERVAL_MS);
|
|
6766
7113
|
return () => clearInterval(timer);
|
|
6767
|
-
}
|
|
7114
|
+
});
|
|
6768
7115
|
const cycleLen = text.length + SHIMMER_GAP;
|
|
6769
7116
|
const segments = [];
|
|
6770
7117
|
let current = null;
|
|
@@ -6781,6 +7128,14 @@ var Shimmer = ({ text, accent }) => {
|
|
|
6781
7128
|
if (current) segments.push(current);
|
|
6782
7129
|
return /* @__PURE__ */ jsx(Text, { children: segments.map((s, i) => /* @__PURE__ */ jsx(Text, { color: toInkColor(s.color), children: s.text }, i)) });
|
|
6783
7130
|
};
|
|
7131
|
+
var AnimatedPattern = ({ accent, cols, overlay }) => {
|
|
7132
|
+
const [frame, setFrame] = useState(0);
|
|
7133
|
+
useMountEffect(() => {
|
|
7134
|
+
const timer = setInterval(() => setFrame((f) => (f + 1) % 1e3), 120);
|
|
7135
|
+
return () => clearInterval(timer);
|
|
7136
|
+
});
|
|
7137
|
+
return /* @__PURE__ */ jsx(Pattern, { accent, frame, cols, overlay });
|
|
7138
|
+
};
|
|
6784
7139
|
var HeroCard = ({
|
|
6785
7140
|
target,
|
|
6786
7141
|
isLocal,
|
|
@@ -6789,18 +7144,21 @@ var HeroCard = ({
|
|
|
6789
7144
|
checksTotal,
|
|
6790
7145
|
grade,
|
|
6791
7146
|
score,
|
|
6792
|
-
gradeColor
|
|
7147
|
+
gradeColor,
|
|
7148
|
+
width = DEFAULT_CARD_WIDTH
|
|
6793
7149
|
}) => {
|
|
6794
|
-
const
|
|
6795
|
-
useEffect(() => {
|
|
6796
|
-
if (phase === "done" || phase === "error") return;
|
|
6797
|
-
const timer = setInterval(() => setFrame((f) => (f + 1) % 1e3), 120);
|
|
6798
|
-
return () => clearInterval(timer);
|
|
6799
|
-
}, [phase]);
|
|
7150
|
+
const cols = width - 4;
|
|
6800
7151
|
const accent = phase === "done" && gradeColor ? gradeColor : ACCENT_BY_PHASE[phase];
|
|
6801
7152
|
const status = STATUS_BY_PHASE(phase, isLocal, checksDone, checksTotal);
|
|
6802
|
-
const targetLabel = truncate(target,
|
|
7153
|
+
const targetLabel = truncate(target, cols);
|
|
6803
7154
|
const showGrade = phase === "done" && grade !== void 0 && score !== void 0;
|
|
7155
|
+
const isActive = phase !== "done" && phase !== "error";
|
|
7156
|
+
const overlay = {
|
|
7157
|
+
row: PATTERN_ROWS - 1,
|
|
7158
|
+
col: cols - "agentimization".length - 1,
|
|
7159
|
+
chars: Array.from(" agentimization"),
|
|
7160
|
+
color: accent
|
|
7161
|
+
};
|
|
6804
7162
|
return /* @__PURE__ */ jsxs(
|
|
6805
7163
|
Box,
|
|
6806
7164
|
{
|
|
@@ -6808,21 +7166,9 @@ var HeroCard = ({
|
|
|
6808
7166
|
borderStyle: "round",
|
|
6809
7167
|
borderColor: dim(accent, FRAME_INNER_FACTOR),
|
|
6810
7168
|
paddingX: 1,
|
|
6811
|
-
width
|
|
7169
|
+
width,
|
|
6812
7170
|
children: [
|
|
6813
|
-
/* @__PURE__ */ jsx(
|
|
6814
|
-
Pattern,
|
|
6815
|
-
{
|
|
6816
|
-
accent,
|
|
6817
|
-
frame,
|
|
6818
|
-
overlay: {
|
|
6819
|
-
row: PATTERN_ROWS - 1,
|
|
6820
|
-
col: PATTERN_COLS - "agentimization".length - 1,
|
|
6821
|
-
chars: Array.from(" agentimization"),
|
|
6822
|
-
color: accent
|
|
6823
|
-
}
|
|
6824
|
-
}
|
|
6825
|
-
),
|
|
7171
|
+
isActive ? /* @__PURE__ */ jsx(AnimatedPattern, { accent, cols, overlay }) : /* @__PURE__ */ jsx(Pattern, { accent, frame: 0, cols, overlay }),
|
|
6826
7172
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { children: targetLabel }) }),
|
|
6827
7173
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: showGrade ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6828
7174
|
/* @__PURE__ */ jsx(Text, { color: toInkColor(accent), children: grade }),
|
|
@@ -6835,7 +7181,7 @@ var HeroCard = ({
|
|
|
6835
7181
|
" ",
|
|
6836
7182
|
status
|
|
6837
7183
|
] })
|
|
6838
|
-
] }) : /* @__PURE__ */ jsx(Shimmer, { text: status, accent }) })
|
|
7184
|
+
] }) : /* @__PURE__ */ jsx(Shimmer, { text: status, accent }, status) })
|
|
6839
7185
|
]
|
|
6840
7186
|
}
|
|
6841
7187
|
);
|
|
@@ -6881,7 +7227,7 @@ var ACCENT_BLUE = "oklch(0.766 0.111 259.9)";
|
|
|
6881
7227
|
var TEXT_DIM = "oklch(0.550 0.034 277.1)";
|
|
6882
7228
|
|
|
6883
7229
|
// src/ui/result-card.tsx
|
|
6884
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7230
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
6885
7231
|
var SCORE_PAIRS = {
|
|
6886
7232
|
pass: { bright: "oklch(0.858 0.109 142.7)", dim: "oklch(0.584 0.102 139.1)" },
|
|
6887
7233
|
warn: { bright: "oklch(0.824 0.101 52.6)", dim: "oklch(0.572 0.072 53.0)" },
|
|
@@ -6889,8 +7235,7 @@ var SCORE_PAIRS = {
|
|
|
6889
7235
|
empty: { bright: "oklch(0.404 0.032 280.2)", dim: "oklch(0.324 0.032 282.0)" }
|
|
6890
7236
|
};
|
|
6891
7237
|
var RESULT_CARD_WIDTH = 46;
|
|
6892
|
-
var
|
|
6893
|
-
var SCORE_FIELD_ROWS = 2;
|
|
7238
|
+
var SCORE_FIELD_ROWS = 1;
|
|
6894
7239
|
var SCORE_GLYPHS = ["\u2591", "\u2592", "\u2593", "\u2588"];
|
|
6895
7240
|
var scoreCell = (row, col) => {
|
|
6896
7241
|
const v = (row * 7 + col * 13) % 17;
|
|
@@ -6901,16 +7246,18 @@ var scoreCell = (row, col) => {
|
|
|
6901
7246
|
var ScoreField = ({
|
|
6902
7247
|
passWidth,
|
|
6903
7248
|
warnWidth,
|
|
6904
|
-
failWidth
|
|
7249
|
+
failWidth,
|
|
7250
|
+
barWidth
|
|
6905
7251
|
}) => {
|
|
7252
|
+
const emptyWidth = Math.max(0, barWidth - passWidth - warnWidth - failWidth);
|
|
6906
7253
|
const colorAtCol = (col, bright) => {
|
|
6907
|
-
const pair = col <
|
|
7254
|
+
const pair = col < emptyWidth ? SCORE_PAIRS.empty : col < emptyWidth + passWidth ? SCORE_PAIRS.pass : col < emptyWidth + passWidth + warnWidth ? SCORE_PAIRS.warn : SCORE_PAIRS.fail;
|
|
6908
7255
|
return bright ? pair.bright : pair.dim;
|
|
6909
7256
|
};
|
|
6910
7257
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: Array.from({ length: SCORE_FIELD_ROWS }, (_, r) => {
|
|
6911
7258
|
const segments = [];
|
|
6912
7259
|
let current = null;
|
|
6913
|
-
for (let c = 0; c <
|
|
7260
|
+
for (let c = 0; c < barWidth; c++) {
|
|
6914
7261
|
const cell = scoreCell(r, c);
|
|
6915
7262
|
const color = colorAtCol(c, cell.bright);
|
|
6916
7263
|
if (current && current.color === color) {
|
|
@@ -6924,15 +7271,21 @@ var ScoreField = ({
|
|
|
6924
7271
|
return /* @__PURE__ */ jsx2(Text2, { children: segments.map((s, i) => /* @__PURE__ */ jsx2(Text2, { color: toInkColor(s.color), children: s.text }, i)) }, r);
|
|
6925
7272
|
}) });
|
|
6926
7273
|
};
|
|
6927
|
-
var ResultCard = ({
|
|
7274
|
+
var ResultCard = ({
|
|
7275
|
+
result,
|
|
7276
|
+
target,
|
|
7277
|
+
width = RESULT_CARD_WIDTH
|
|
7278
|
+
}) => {
|
|
6928
7279
|
const { grade, overall_score, summary, latency_ms } = result;
|
|
6929
7280
|
const gc = GRADE_COLORS[grade] ?? "red";
|
|
7281
|
+
const barWidth = width - 4;
|
|
6930
7282
|
const total = summary.total || 1;
|
|
6931
|
-
const passWidth = Math.max(Math.round(summary.passed / total *
|
|
6932
|
-
const warnWidth = Math.max(Math.round(summary.warned / total *
|
|
6933
|
-
const failWidth = Math.max(Math.round(summary.failed / total *
|
|
7283
|
+
const passWidth = Math.max(Math.round(summary.passed / total * barWidth), summary.passed > 0 ? 1 : 0);
|
|
7284
|
+
const warnWidth = Math.max(Math.round(summary.warned / total * barWidth), summary.warned > 0 ? 1 : 0);
|
|
7285
|
+
const failWidth = Math.max(Math.round(summary.failed / total * barWidth), summary.failed > 0 ? 1 : 0);
|
|
6934
7286
|
const seconds = (latency_ms / 1e3).toFixed(1);
|
|
6935
|
-
const
|
|
7287
|
+
const headerChrome = `${grade} ${overall_score}/100`.length + 3 + 3 + `${seconds}s`.length;
|
|
7288
|
+
const targetMax = Math.max(8, barWidth - headerChrome);
|
|
6936
7289
|
const targetLabel = target.length <= targetMax ? target : target.slice(0, targetMax - 1) + "\u2026";
|
|
6937
7290
|
return /* @__PURE__ */ jsxs2(
|
|
6938
7291
|
Box2,
|
|
@@ -6942,7 +7295,7 @@ var ResultCard = ({ result, target }) => {
|
|
|
6942
7295
|
borderStyle: "round",
|
|
6943
7296
|
borderColor: dim(gc, FRAME_INNER_FACTOR),
|
|
6944
7297
|
paddingX: 1,
|
|
6945
|
-
width
|
|
7298
|
+
width,
|
|
6946
7299
|
children: [
|
|
6947
7300
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
6948
7301
|
/* @__PURE__ */ jsx2(Text2, { color: toInkColor(gc), children: grade }),
|
|
@@ -6951,22 +7304,34 @@ var ResultCard = ({ result, target }) => {
|
|
|
6951
7304
|
overall_score,
|
|
6952
7305
|
"/100"
|
|
6953
7306
|
] }),
|
|
7307
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7308
|
+
" ",
|
|
7309
|
+
targetLabel
|
|
7310
|
+
] }),
|
|
6954
7311
|
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
6955
7312
|
" ",
|
|
6956
7313
|
seconds,
|
|
6957
7314
|
"s"
|
|
6958
7315
|
] })
|
|
6959
7316
|
] }),
|
|
6960
|
-
/* @__PURE__ */ jsx2(Text2, { children: targetLabel }),
|
|
6961
7317
|
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(
|
|
6962
7318
|
ScoreField,
|
|
6963
7319
|
{
|
|
6964
7320
|
passWidth,
|
|
6965
7321
|
warnWidth,
|
|
6966
|
-
failWidth
|
|
7322
|
+
failWidth,
|
|
7323
|
+
barWidth
|
|
6967
7324
|
}
|
|
6968
7325
|
) }),
|
|
6969
7326
|
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
7327
|
+
summary.skipped > 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
7328
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7329
|
+
STATUS_ICONS.skip,
|
|
7330
|
+
" ",
|
|
7331
|
+
summary.skipped
|
|
7332
|
+
] }),
|
|
7333
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " " })
|
|
7334
|
+
] }) : null,
|
|
6970
7335
|
/* @__PURE__ */ jsxs2(Text2, { color: toInkColor(SCORE_PAIRS.pass.bright), children: [
|
|
6971
7336
|
STATUS_ICONS.pass,
|
|
6972
7337
|
" ",
|
|
@@ -6983,12 +7348,6 @@ var ResultCard = ({ result, target }) => {
|
|
|
6983
7348
|
STATUS_ICONS.fail,
|
|
6984
7349
|
" ",
|
|
6985
7350
|
summary.failed
|
|
6986
|
-
] }),
|
|
6987
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " " }),
|
|
6988
|
-
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
6989
|
-
STATUS_ICONS.skip,
|
|
6990
|
-
" ",
|
|
6991
|
-
summary.skipped
|
|
6992
7351
|
] })
|
|
6993
7352
|
] })
|
|
6994
7353
|
]
|
|
@@ -7022,14 +7381,13 @@ var CheckLine = ({ check, loaderColor }) => {
|
|
|
7022
7381
|
r.id
|
|
7023
7382
|
] })
|
|
7024
7383
|
] }),
|
|
7025
|
-
/* @__PURE__ */ jsxs3(
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
] }) : null
|
|
7384
|
+
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingLeft: 2, children: [
|
|
7385
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: r.message }),
|
|
7386
|
+
showSuggestion ? /* @__PURE__ */ jsxs3(Text3, { color: toInkColor("oklch(0.858 0.109 142.7)"), children: [
|
|
7387
|
+
"\u2192 ",
|
|
7388
|
+
r.suggestion
|
|
7389
|
+
] }) : null
|
|
7390
|
+
] })
|
|
7033
7391
|
] });
|
|
7034
7392
|
}
|
|
7035
7393
|
return null;
|
|
@@ -7210,7 +7568,7 @@ var copyToClipboard = async (text) => {
|
|
|
7210
7568
|
// src/ui/action-menu.tsx
|
|
7211
7569
|
import { writeFileSync } from "fs";
|
|
7212
7570
|
import { resolve } from "path";
|
|
7213
|
-
import { Fragment as
|
|
7571
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
7214
7572
|
var FEEDBACK_OK = "oklch(0.858 0.109 142.7)";
|
|
7215
7573
|
var FEEDBACK_ERR = "oklch(0.718 0.181 10.0)";
|
|
7216
7574
|
var buildMenuOptions = (hasIssues) => [
|
|
@@ -7338,7 +7696,7 @@ var ActionMenu = ({
|
|
|
7338
7696
|
] }) : null
|
|
7339
7697
|
] }, opt.value);
|
|
7340
7698
|
}) }),
|
|
7341
|
-
feedback ? /* @__PURE__ */ jsxs4(
|
|
7699
|
+
feedback ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
7342
7700
|
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, paddingLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { color: toInkColor(feedback.startsWith("\u2713") ? FEEDBACK_OK : FEEDBACK_ERR), children: feedback }) }),
|
|
7343
7701
|
/* @__PURE__ */ jsx4(Box4, { height: 1, children: /* @__PURE__ */ jsx4(Text4, { children: " " }) })
|
|
7344
7702
|
] }) : null
|
|
@@ -7471,7 +7829,7 @@ var resolveTarget = (input) => {
|
|
|
7471
7829
|
};
|
|
7472
7830
|
|
|
7473
7831
|
// src/ui/app.tsx
|
|
7474
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
7832
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
7475
7833
|
var App = ({
|
|
7476
7834
|
target: initialTarget,
|
|
7477
7835
|
isLocal: initialIsLocal,
|
|
@@ -7481,22 +7839,95 @@ var App = ({
|
|
|
7481
7839
|
}) => {
|
|
7482
7840
|
const [target, setTarget] = useState4(initialTarget);
|
|
7483
7841
|
const [isLocal, setIsLocal] = useState4(initialIsLocal);
|
|
7842
|
+
const [retryError, setRetryError] = useState4(null);
|
|
7843
|
+
const { stdout } = useStdout();
|
|
7844
|
+
const [columns, setColumns] = useState4(stdout?.columns ?? 80);
|
|
7845
|
+
useMountEffect(() => {
|
|
7846
|
+
if (!stdout) return;
|
|
7847
|
+
const onResize = () => setColumns(stdout.columns);
|
|
7848
|
+
stdout.on("resize", onResize);
|
|
7849
|
+
return () => {
|
|
7850
|
+
stdout.off("resize", onResize);
|
|
7851
|
+
};
|
|
7852
|
+
});
|
|
7853
|
+
const cardWidth = Math.max(40, Math.min(columns - 2, 72));
|
|
7854
|
+
const handleRetry = (newInput) => {
|
|
7855
|
+
const resolved = resolveTarget(newInput);
|
|
7856
|
+
if ("error" in resolved) {
|
|
7857
|
+
setRetryError(resolved.error);
|
|
7858
|
+
return;
|
|
7859
|
+
}
|
|
7860
|
+
setRetryError(null);
|
|
7861
|
+
setTarget(resolved.target);
|
|
7862
|
+
setIsLocal(resolved.isLocal);
|
|
7863
|
+
};
|
|
7864
|
+
if (retryError) {
|
|
7865
|
+
return /* @__PURE__ */ jsx6(ErrorCard, { message: retryError, target, cardWidth, onRetry: handleRetry });
|
|
7866
|
+
}
|
|
7867
|
+
return /* @__PURE__ */ jsx6(
|
|
7868
|
+
AuditRunner,
|
|
7869
|
+
{
|
|
7870
|
+
target,
|
|
7871
|
+
isLocal,
|
|
7872
|
+
categories,
|
|
7873
|
+
sampleSize,
|
|
7874
|
+
autoDetectedFrom,
|
|
7875
|
+
cardWidth,
|
|
7876
|
+
onRetry: handleRetry
|
|
7877
|
+
},
|
|
7878
|
+
`${target}|${isLocal}`
|
|
7879
|
+
);
|
|
7880
|
+
};
|
|
7881
|
+
var ErrorCard = ({
|
|
7882
|
+
message,
|
|
7883
|
+
target,
|
|
7884
|
+
cardWidth,
|
|
7885
|
+
onRetry
|
|
7886
|
+
}) => {
|
|
7887
|
+
const errorColor = "oklch(0.718 0.181 10.0)";
|
|
7888
|
+
const lines = message.split("\n");
|
|
7889
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
7890
|
+
/* @__PURE__ */ jsxs6(
|
|
7891
|
+
Box6,
|
|
7892
|
+
{
|
|
7893
|
+
flexDirection: "column",
|
|
7894
|
+
marginTop: 1,
|
|
7895
|
+
borderStyle: "round",
|
|
7896
|
+
borderColor: dim(errorColor, FRAME_INNER_FACTOR),
|
|
7897
|
+
paddingX: 1,
|
|
7898
|
+
width: cardWidth,
|
|
7899
|
+
children: [
|
|
7900
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: toInkColor(errorColor), children: "Error" }),
|
|
7901
|
+
/* @__PURE__ */ jsx6(Text6, { children: target }),
|
|
7902
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx6(Text6, { dimColor: i > 0, children: line }, i)) })
|
|
7903
|
+
]
|
|
7904
|
+
}
|
|
7905
|
+
),
|
|
7906
|
+
/* @__PURE__ */ jsx6(ErrorActions, { onRetry })
|
|
7907
|
+
] });
|
|
7908
|
+
};
|
|
7909
|
+
var AuditRunner = ({
|
|
7910
|
+
target,
|
|
7911
|
+
isLocal,
|
|
7912
|
+
categories,
|
|
7913
|
+
sampleSize,
|
|
7914
|
+
autoDetectedFrom,
|
|
7915
|
+
cardWidth,
|
|
7916
|
+
onRetry
|
|
7917
|
+
}) => {
|
|
7484
7918
|
const [phase, setPhase] = useState4("init");
|
|
7485
7919
|
const [checks, setChecks] = useState4([]);
|
|
7486
7920
|
const [result, setResult] = useState4(null);
|
|
7487
7921
|
const [error, setError] = useState4(null);
|
|
7488
7922
|
const [networkSkipped, setNetworkSkipped] = useState4(0);
|
|
7489
|
-
|
|
7923
|
+
useMountEffect(() => {
|
|
7490
7924
|
const onEvent = (event) => {
|
|
7491
7925
|
switch (event.type) {
|
|
7492
7926
|
case "phase":
|
|
7493
7927
|
setPhase(event.phase);
|
|
7494
7928
|
break;
|
|
7495
7929
|
case "check-start":
|
|
7496
|
-
setChecks((prev) => [
|
|
7497
|
-
...prev,
|
|
7498
|
-
{ ...event.check, status: "running" }
|
|
7499
|
-
]);
|
|
7930
|
+
setChecks((prev) => [...prev, { ...event.check, status: "running" }]);
|
|
7500
7931
|
break;
|
|
7501
7932
|
case "check-complete":
|
|
7502
7933
|
setChecks(
|
|
@@ -7512,7 +7943,7 @@ var App = ({
|
|
|
7512
7943
|
const config = { categories, sampleSize, onEvent };
|
|
7513
7944
|
const res = isLocal ? await auditLocal(target, config) : await audit(target, config);
|
|
7514
7945
|
if (isLocal) {
|
|
7515
|
-
setNetworkSkipped(
|
|
7946
|
+
setNetworkSkipped(ALL_CHECKS.length - res.summary.total);
|
|
7516
7947
|
}
|
|
7517
7948
|
setResult(res);
|
|
7518
7949
|
setPhase("done");
|
|
@@ -7527,89 +7958,69 @@ var App = ({
|
|
|
7527
7958
|
}
|
|
7528
7959
|
};
|
|
7529
7960
|
run();
|
|
7530
|
-
}
|
|
7531
|
-
const handleRetry = (newInput) => {
|
|
7532
|
-
const resolved = resolveTarget(newInput);
|
|
7533
|
-
if ("error" in resolved) {
|
|
7534
|
-
setError(resolved.error);
|
|
7535
|
-
return;
|
|
7536
|
-
}
|
|
7537
|
-
setError(null);
|
|
7538
|
-
setResult(null);
|
|
7539
|
-
setChecks([]);
|
|
7540
|
-
setNetworkSkipped(0);
|
|
7541
|
-
setPhase("init");
|
|
7542
|
-
setTarget(resolved.target);
|
|
7543
|
-
setIsLocal(resolved.isLocal);
|
|
7544
|
-
};
|
|
7961
|
+
});
|
|
7545
7962
|
const checksByCategory = /* @__PURE__ */ new Map();
|
|
7546
7963
|
for (const check of checks) {
|
|
7547
7964
|
const existing = checksByCategory.get(check.category) ?? [];
|
|
7548
7965
|
existing.push(check);
|
|
7549
7966
|
checksByCategory.set(check.category, existing);
|
|
7550
7967
|
}
|
|
7968
|
+
const categoryEntries = [...checksByCategory.entries()];
|
|
7969
|
+
const committedCount = phase === "done" ? categoryEntries.length : Math.max(0, categoryEntries.length - 1);
|
|
7970
|
+
const committedCategories = categoryEntries.slice(0, committedCount);
|
|
7971
|
+
const liveCategory = categoryEntries[committedCount];
|
|
7551
7972
|
if (phase === "error") {
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
7555
|
-
/* @__PURE__ */ jsxs6(
|
|
7556
|
-
Box6,
|
|
7557
|
-
{
|
|
7558
|
-
flexDirection: "column",
|
|
7559
|
-
marginTop: 1,
|
|
7560
|
-
borderStyle: "round",
|
|
7561
|
-
borderColor: dim(errorColor, FRAME_INNER_FACTOR),
|
|
7562
|
-
paddingX: 1,
|
|
7563
|
-
width: RESULT_CARD_WIDTH,
|
|
7564
|
-
children: [
|
|
7565
|
-
/* @__PURE__ */ jsx6(Text6, { bold: true, color: toInkColor(errorColor), children: "Error" }),
|
|
7566
|
-
/* @__PURE__ */ jsx6(Text6, { children: target }),
|
|
7567
|
-
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx6(Text6, { dimColor: i > 0, children: line }, i)) })
|
|
7568
|
-
]
|
|
7569
|
-
}
|
|
7570
|
-
),
|
|
7571
|
-
/* @__PURE__ */ jsx6(ErrorActions, { onRetry: handleRetry })
|
|
7572
|
-
] });
|
|
7573
|
-
}
|
|
7574
|
-
const checksDone = checks.filter((c) => c.status === "done").length;
|
|
7575
|
-
const accent = result ? GRADE_COLORS[result.grade] ?? ACCENT_BY_PHASE[phase] : ACCENT_BY_PHASE[phase];
|
|
7576
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
7577
|
-
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
|
|
7578
|
-
HeroCard,
|
|
7973
|
+
return /* @__PURE__ */ jsx6(
|
|
7974
|
+
ErrorCard,
|
|
7579
7975
|
{
|
|
7976
|
+
message: error ?? "Unknown error",
|
|
7580
7977
|
target,
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
checksDone,
|
|
7584
|
-
checksTotal: checks.length,
|
|
7585
|
-
grade: result?.grade,
|
|
7586
|
-
score: result?.overall_score,
|
|
7587
|
-
gradeColor: result ? GRADE_COLORS[result.grade] : void 0
|
|
7978
|
+
cardWidth,
|
|
7979
|
+
onRetry
|
|
7588
7980
|
}
|
|
7589
|
-
)
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
] })
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
/* @__PURE__ */
|
|
7611
|
-
|
|
7612
|
-
|
|
7981
|
+
);
|
|
7982
|
+
}
|
|
7983
|
+
const checksDone = checks.filter((c) => c.status === "done").length;
|
|
7984
|
+
const accent = result ? GRADE_COLORS[result.grade] ?? ACCENT_BY_PHASE[phase] : ACCENT_BY_PHASE[phase];
|
|
7985
|
+
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
7986
|
+
/* @__PURE__ */ jsx6(Static, { items: committedCategories, children: ([cat, catChecks]) => /* @__PURE__ */ jsx6(Box6, { paddingLeft: 1, children: /* @__PURE__ */ jsx6(CategorySection, { category: cat, checks: catChecks, loaderColor: accent }) }, cat) }),
|
|
7987
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, children: [
|
|
7988
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
|
|
7989
|
+
HeroCard,
|
|
7990
|
+
{
|
|
7991
|
+
target,
|
|
7992
|
+
isLocal,
|
|
7993
|
+
phase,
|
|
7994
|
+
checksDone,
|
|
7995
|
+
checksTotal: checks.length,
|
|
7996
|
+
grade: result?.grade,
|
|
7997
|
+
score: result?.overall_score,
|
|
7998
|
+
gradeColor: result ? GRADE_COLORS[result.grade] : void 0,
|
|
7999
|
+
width: cardWidth
|
|
8000
|
+
}
|
|
8001
|
+
) }),
|
|
8002
|
+
autoDetectedFrom ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
8003
|
+
"auto-detected build output (was ",
|
|
8004
|
+
autoDetectedFrom,
|
|
8005
|
+
")"
|
|
8006
|
+
] }) : null,
|
|
8007
|
+
isLocal && networkSkipped > 0 && phase === "done" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
8008
|
+
networkSkipped,
|
|
8009
|
+
" network-only checks skipped"
|
|
8010
|
+
] }) : null,
|
|
8011
|
+
liveCategory ? /* @__PURE__ */ jsx6(
|
|
8012
|
+
CategorySection,
|
|
8013
|
+
{
|
|
8014
|
+
category: liveCategory[0],
|
|
8015
|
+
checks: liveCategory[1],
|
|
8016
|
+
loaderColor: accent
|
|
8017
|
+
}
|
|
8018
|
+
) : null,
|
|
8019
|
+
result ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
8020
|
+
/* @__PURE__ */ jsx6(ResultCard, { result, target, width: cardWidth }),
|
|
8021
|
+
/* @__PURE__ */ jsx6(ActionMenu, { result, target, isLocal, onRetry })
|
|
8022
|
+
] }) : null
|
|
8023
|
+
] })
|
|
7613
8024
|
] });
|
|
7614
8025
|
};
|
|
7615
8026
|
|