@forkpoint/agent-lighthouse-core 0.2.0 → 0.2.2

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.mjs CHANGED
@@ -507,7 +507,8 @@ function extractImages($) {
507
507
  width: $(el).attr("width") ?? void 0,
508
508
  height: $(el).attr("height") ?? void 0,
509
509
  loading: $(el).attr("loading") ?? void 0,
510
- role: $(el).attr("role") ?? void 0
510
+ role: $(el).attr("role") ?? void 0,
511
+ ariaHidden: $(el).attr("aria-hidden") ?? void 0
511
512
  });
512
513
  });
513
514
  return images;
@@ -4647,9 +4648,6 @@ function matchesAnyType2(schema, types) {
4647
4648
  function allSchemas3(ctx) {
4648
4649
  return ctx.pages.flatMap((p) => flattenJsonLd(p.structuredData ?? p.jsonLd));
4649
4650
  }
4650
- function hasProps3(obj, keys) {
4651
- return keys.filter((k) => !obj[k]);
4652
- }
4653
4651
  var ServiceProductSchemaAudit = class extends Audit {
4654
4652
  static meta = {
4655
4653
  id: "3.8",
@@ -4702,13 +4700,19 @@ var ServiceProductSchemaAudit = class extends Audit {
4702
4700
  }
4703
4701
  );
4704
4702
  }
4705
- const requiredProps = ["name", "description", "provider"];
4706
4703
  const first2 = serviceProducts[0];
4707
- const missing = hasProps3(first2, requiredProps);
4704
+ const isProduct = matchesAnyType2(first2, ["Product"]);
4705
+ const hasName = Boolean(first2["name"]);
4706
+ const hasDescription = Boolean(first2["description"]);
4707
+ const hasProviderOrBrand = isProduct ? Boolean(first2["brand"] || first2["manufacturer"] || first2["provider"] || first2["offers"]) : Boolean(first2["provider"]);
4708
+ const missing = [];
4709
+ if (!hasName) missing.push("name");
4710
+ if (!hasDescription) missing.push("description");
4711
+ if (!hasProviderOrBrand) missing.push(isProduct ? "brand/offers" : "provider");
4708
4712
  const valid = missing.length === 0;
4709
4713
  if (valid) {
4710
4714
  return this.pass(
4711
- `Service/Product schema found with name, description, and provider.`,
4715
+ `Service/Product schema found with name, description, and ${isProduct ? "brand/offers" : "provider"}.`,
4712
4716
  "Service or Product schema with name, description, and provider.",
4713
4717
  `Complete Service/Product schema (${serviceProducts.length} total)`
4714
4718
  );
@@ -5354,7 +5358,7 @@ function matchesAnyType7(schema, types) {
5354
5358
  function allSchemas8(ctx) {
5355
5359
  return ctx.pages.flatMap((p) => flattenJsonLd(p.structuredData ?? p.jsonLd));
5356
5360
  }
5357
- function hasProps4(obj, keys) {
5361
+ function hasProps3(obj, keys) {
5358
5362
  return keys.filter((k) => !obj[k]);
5359
5363
  }
5360
5364
  var AuthorSchemaAudit = class extends Audit {
@@ -5435,11 +5439,11 @@ var AuthorSchemaAudit = class extends Audit {
5435
5439
  }
5436
5440
  const requiredProps = ["name", "jobTitle", "sameAs", "affiliation"];
5437
5441
  const bestPerson = allPersons.reduce((best, p) => {
5438
- const bestMissing = hasProps4(best, requiredProps).length;
5439
- const currentMissing = hasProps4(p, requiredProps).length;
5442
+ const bestMissing = hasProps3(best, requiredProps).length;
5443
+ const currentMissing = hasProps3(p, requiredProps).length;
5440
5444
  return currentMissing < bestMissing ? p : best;
5441
5445
  }, allPersons[0]);
5442
- const missing = hasProps4(bestPerson, requiredProps);
5446
+ const missing = hasProps3(bestPerson, requiredProps);
5443
5447
  const complete = missing.length === 0;
5444
5448
  if (complete) {
5445
5449
  return this.pass(
@@ -11360,7 +11364,7 @@ var DecorativeImagesAudit = class extends Audit {
11360
11364
  for (const img of images) {
11361
11365
  if (img.alt === "") {
11362
11366
  decorativeCount++;
11363
- if (img.role === "presentation") {
11367
+ if (img.role === "presentation" || img.role === "none" || img.ariaHidden === "true") {
11364
11368
  correctlyMarked++;
11365
11369
  }
11366
11370
  }
@@ -12584,8 +12588,19 @@ var CorsAiFilesAudit = class extends Audit {
12584
12588
  async audit(ctx) {
12585
12589
  const page = ctx.pages?.[0];
12586
12590
  const aiPaths = ["/llms.txt", "/.well-known/ai-catalog.json"];
12591
+ const hasRootFiles = ctx.rootFiles && Object.keys(ctx.rootFiles).length > 0;
12592
+ const existingAiPaths = hasRootFiles ? aiPaths.filter((p) => ctx.rootFiles[p]?.status === 200) : aiPaths;
12593
+ if (hasRootFiles && existingAiPaths.length === 0) {
12594
+ return this.warn(
12595
+ "No AI files (/llms.txt, /.well-known/ai-catalog.json) found to check CORS headers.",
12596
+ "OPTIONS requests to /llms.txt and /.well-known/ai-catalog.json return Access-Control-Allow-Origin",
12597
+ "No applicable AI files found",
12598
+ void 0,
12599
+ page?.url
12600
+ );
12601
+ }
12587
12602
  const corsResults = [];
12588
- for (const path of aiPaths) {
12603
+ for (const path of existingAiPaths) {
12589
12604
  try {
12590
12605
  const result = await ctx.fetch({
12591
12606
  url: `${ctx.baseUrl}${path}`,
@@ -15075,9 +15090,33 @@ var AboutCredentialsAudit = class extends Audit {
15075
15090
  }
15076
15091
  };
15077
15092
  async audit(ctx) {
15078
- const aboutPaths = ["/about/", "/about-us/", "/about"];
15079
- let aboutResult = ctx.rootFiles["/about/"] ?? ctx.rootFiles["/about-us/"];
15080
- if (!aboutResult || aboutResult.status !== 200) {
15093
+ const aboutPaths = [
15094
+ "/about/",
15095
+ "/about-us/",
15096
+ "/about",
15097
+ "/pages/about",
15098
+ "/pages/about-us",
15099
+ "/pages/our-story",
15100
+ "/our-story"
15101
+ ];
15102
+ let aboutResult = void 0;
15103
+ for (const path of aboutPaths) {
15104
+ const file = ctx.rootFiles[path];
15105
+ if (file && file.status === 200 && file.body) {
15106
+ aboutResult = file;
15107
+ break;
15108
+ }
15109
+ }
15110
+ if (!aboutResult && ctx.pages) {
15111
+ const matchingPage = ctx.pages.find((p) => {
15112
+ const urlLower = p.url.toLowerCase();
15113
+ return urlLower.includes("/about") || urlLower.includes("/our-story") || urlLower.includes("/who-we-are");
15114
+ });
15115
+ if (matchingPage?.fetchResult?.status === 200 && matchingPage.fetchResult.body) {
15116
+ aboutResult = matchingPage.fetchResult;
15117
+ }
15118
+ }
15119
+ if (!aboutResult) {
15081
15120
  for (const path of aboutPaths) {
15082
15121
  try {
15083
15122
  const result = await ctx.fetch({
@@ -15346,7 +15385,11 @@ var TRUST_PATTERNS = [
15346
15385
  /\baward/i,
15347
15386
  /\bas\s+seen\s+(in|on)\b/i,
15348
15387
  /\bpartner(s|ed|ship)?\b/i,
15349
- /\bcertif(ied|icate|ication)\b/i
15388
+ /\bcertif(ied|icate|ication)\b/i,
15389
+ /\b(money[-\s]back\s+guarantee|satisfaction\s+guarantee|guaranteed)\b/i,
15390
+ /\b(reviews|customer\s+rating|verified\s+(buyer|customer|review)|happy\s+customers)\b/i,
15391
+ /\b(sustainable|organic|fair\s+trade|b\s+corp|handcrafted|handmade)\b/i,
15392
+ /\b(secure\s+checkout|free\s+returns|free\s+shipping)\b/i
15350
15393
  ];
15351
15394
  var TrustSignalsAudit = class extends Audit {
15352
15395
  static meta = {
@@ -22052,7 +22095,11 @@ async function runScan(url, onProgress, pageOverrides, signal) {
22052
22095
  "/policies/terms-of-service",
22053
22096
  "/about/",
22054
22097
  "/about-us/",
22055
- "/about"
22098
+ "/about",
22099
+ "/pages/about",
22100
+ "/pages/about-us",
22101
+ "/pages/our-story",
22102
+ "/our-story"
22056
22103
  ];
22057
22104
  logger.debug({ count: rootFilePaths.length }, "[orchestrator] Phase 1: Fetching root files");
22058
22105
  const rootResults = await Promise.all(