@canonry/canonry 4.165.2 → 4.167.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.
Files changed (26) hide show
  1. package/assets/assets/{AuditHistoryPanel-DYBAiNCu.js → AuditHistoryPanel-Dvz6QFtW.js} +1 -1
  2. package/assets/assets/{BacklinksPage-p19FCIyh.js → BacklinksPage-BHOtOIr5.js} +1 -1
  3. package/assets/assets/{HistoryPage-CRSuA-J0.js → HistoryPage-Bc5Nx6dX.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-Bgr9VeUk.js → MeasurementPropertyPage-D4FIFiWv.js} +1 -1
  5. package/assets/assets/ProjectPage-CT5ZD7_A.js +9 -0
  6. package/assets/assets/{RunRow-_RMMaLPt.js → RunRow-CB2oG45_.js} +1 -1
  7. package/assets/assets/{RunsPage-CaDPSEfE.js → RunsPage-OLbocIOY.js} +1 -1
  8. package/assets/assets/SettingsPage-DVdPBrap.js +1 -0
  9. package/assets/assets/{SiteHealthSection-Byp_Fu5K.js → SiteHealthSection-B1bjMCqY.js} +3 -3
  10. package/assets/assets/{TrafficPage-BXkXNKYB.js → TrafficPage-BUWXjHhw.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-CReS8q0H.js → TrafficSourceDetailPage-DwoFzTu7.js} +1 -1
  12. package/assets/assets/{extract-error-message-CHW-A3AQ.js → extract-error-message-CNH2-nPH.js} +1 -1
  13. package/assets/assets/{index-CcS2bDjR.js → index-DdXb59Rg.js} +20 -20
  14. package/assets/assets/{react-sigma_core.esm.min-Cm0RA3Iv.js → react-sigma_core.esm.min-DLRh4X_G.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-BJLIMWL2.js → chunk-7M3QVRJD.js} +134 -57
  17. package/dist/{chunk-J56XZGG6.js → chunk-P5LR3NDQ.js} +6 -5
  18. package/dist/{chunk-UVDX2Y4N.js → chunk-SXENNCJF.js} +104 -96
  19. package/dist/{chunk-P2CQ57UV.js → chunk-TP4KXU3E.js} +20 -2
  20. package/dist/cli.js +36 -29
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-QGBYYNWV.js → intelligence-service-ULDOVV5Y.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +11 -11
  25. package/assets/assets/ProjectPage-D6-9qYKU.js +0 -9
  26. package/assets/assets/SettingsPage-Dcp71zcu.js +0 -1
@@ -1,4 +1,4 @@
1
- import{g as Bt,t as S,r as C}from"./vendor-tanstack-Dock0eH1.js";import{e as Ht,G as Wt}from"./SiteHealthSection-Byp_Fu5K.js";import"./index-CcS2bDjR.js";import"./vendor-radix-4UOaVWza.js";import"./vendor-recharts-D8rt5lKn.js";import"./vendor-lucide-nYKv-BB7.js";import"./vendor-markdown-Bn7fRwrx.js";import"./vendor-yaml-GWJz5H2G.js";import"./ChartPrimitives-BoYe2M-i.js";function Vt(r,n){if(typeof r!="object"||!r)return r;var i=r[Symbol.toPrimitive];if(i!==void 0){var e=i.call(r,n);if(typeof e!="object")return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}function ae(r){var n=Vt(r,"string");return typeof n=="symbol"?n:n+""}function A(r,n){if(!(r instanceof n))throw new TypeError("Cannot call a class as a function")}function He(r,n){for(var i=0;i<n.length;i++){var e=n[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(r,ae(e.key),e)}}function L(r,n,i){return n&&He(r.prototype,n),i&&He(r,i),Object.defineProperty(r,"prototype",{writable:!1}),r}function Q(r){return Q=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(n){return n.__proto__||Object.getPrototypeOf(n)},Q(r)}function yt(){try{var r=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(yt=function(){return!!r})()}function jt(r){if(r===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return r}function Zt(r,n){if(n&&(typeof n=="object"||typeof n=="function"))return n;if(n!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return jt(r)}function I(r,n,i){return n=Q(n),Zt(r,yt()?Reflect.construct(n,i||[],Q(r).constructor):n.apply(r,i))}function xe(r,n){return xe=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,e){return i.__proto__=e,i},xe(r,n)}function G(r,n){if(typeof n!="function"&&n!==null)throw new TypeError("Super expression must either be null or a function");r.prototype=Object.create(n&&n.prototype,{constructor:{value:r,writable:!0,configurable:!0}}),Object.defineProperty(r,"prototype",{writable:!1}),n&&xe(r,n)}function qt(r){if(Array.isArray(r))return r}function Yt(r,n){var i=r==null?null:typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(i!=null){var e,t,a,o,s=[],l=!0,h=!1;try{if(a=(i=i.call(r)).next,n===0){if(Object(i)!==i)return;l=!1}else for(;!(l=(e=a.call(i)).done)&&(s.push(e.value),s.length!==n);l=!0);}catch(u){h=!0,t=u}finally{try{if(!l&&i.return!=null&&(o=i.return(),Object(o)!==o))return}finally{if(h)throw t}}return s}}function Se(r,n){(n==null||n>r.length)&&(n=r.length);for(var i=0,e=Array(n);i<n;i++)e[i]=r[i];return e}function Tt(r,n){if(r){if(typeof r=="string")return Se(r,n);var i={}.toString.call(r).slice(8,-1);return i==="Object"&&r.constructor&&(i=r.constructor.name),i==="Map"||i==="Set"?Array.from(r):i==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Se(r,n):void 0}}function Xt(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
1
+ import{g as Bt,t as S,r as C}from"./vendor-tanstack-Dock0eH1.js";import{e as Ht,G as Wt}from"./SiteHealthSection-B1bjMCqY.js";import"./index-DdXb59Rg.js";import"./vendor-radix-4UOaVWza.js";import"./vendor-recharts-D8rt5lKn.js";import"./vendor-lucide-nYKv-BB7.js";import"./vendor-markdown-Bn7fRwrx.js";import"./vendor-yaml-GWJz5H2G.js";import"./ChartPrimitives-BoYe2M-i.js";function Vt(r,n){if(typeof r!="object"||!r)return r;var i=r[Symbol.toPrimitive];if(i!==void 0){var e=i.call(r,n);if(typeof e!="object")return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}function ae(r){var n=Vt(r,"string");return typeof n=="symbol"?n:n+""}function A(r,n){if(!(r instanceof n))throw new TypeError("Cannot call a class as a function")}function He(r,n){for(var i=0;i<n.length;i++){var e=n[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(r,ae(e.key),e)}}function L(r,n,i){return n&&He(r.prototype,n),i&&He(r,i),Object.defineProperty(r,"prototype",{writable:!1}),r}function Q(r){return Q=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(n){return n.__proto__||Object.getPrototypeOf(n)},Q(r)}function yt(){try{var r=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(yt=function(){return!!r})()}function jt(r){if(r===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return r}function Zt(r,n){if(n&&(typeof n=="object"||typeof n=="function"))return n;if(n!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return jt(r)}function I(r,n,i){return n=Q(n),Zt(r,yt()?Reflect.construct(n,i||[],Q(r).constructor):n.apply(r,i))}function xe(r,n){return xe=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,e){return i.__proto__=e,i},xe(r,n)}function G(r,n){if(typeof n!="function"&&n!==null)throw new TypeError("Super expression must either be null or a function");r.prototype=Object.create(n&&n.prototype,{constructor:{value:r,writable:!0,configurable:!0}}),Object.defineProperty(r,"prototype",{writable:!1}),n&&xe(r,n)}function qt(r){if(Array.isArray(r))return r}function Yt(r,n){var i=r==null?null:typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(i!=null){var e,t,a,o,s=[],l=!0,h=!1;try{if(a=(i=i.call(r)).next,n===0){if(Object(i)!==i)return;l=!1}else for(;!(l=(e=a.call(i)).done)&&(s.push(e.value),s.length!==n);l=!0);}catch(u){h=!0,t=u}finally{try{if(!l&&i.return!=null&&(o=i.return(),Object(o)!==o))return}finally{if(h)throw t}}return s}}function Se(r,n){(n==null||n>r.length)&&(n=r.length);for(var i=0,e=Array(n);i<n;i++)e[i]=r[i];return e}function Tt(r,n){if(r){if(typeof r=="string")return Se(r,n);var i={}.toString.call(r).slice(8,-1);return i==="Object"&&r.constructor&&(i=r.constructor.name),i==="Map"||i==="Set"?Array.from(r):i==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Se(r,n):void 0}}function Xt(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
2
2
  In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function ee(r,n){return qt(r)||Yt(r,n)||Tt(r,n)||Xt()}var pe={black:"#000000",silver:"#C0C0C0",gray:"#808080",grey:"#808080",white:"#FFFFFF",maroon:"#800000",red:"#FF0000",purple:"#800080",fuchsia:"#FF00FF",green:"#008000",lime:"#00FF00",olive:"#808000",yellow:"#FFFF00",navy:"#000080",blue:"#0000FF",teal:"#008080",aqua:"#00FFFF",darkblue:"#00008B",mediumblue:"#0000CD",darkgreen:"#006400",darkcyan:"#008B8B",deepskyblue:"#00BFFF",darkturquoise:"#00CED1",mediumspringgreen:"#00FA9A",springgreen:"#00FF7F",cyan:"#00FFFF",midnightblue:"#191970",dodgerblue:"#1E90FF",lightseagreen:"#20B2AA",forestgreen:"#228B22",seagreen:"#2E8B57",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",limegreen:"#32CD32",mediumseagreen:"#3CB371",turquoise:"#40E0D0",royalblue:"#4169E1",steelblue:"#4682B4",darkslateblue:"#483D8B",mediumturquoise:"#48D1CC",indigo:"#4B0082",darkolivegreen:"#556B2F",cadetblue:"#5F9EA0",cornflowerblue:"#6495ED",rebeccapurple:"#663399",mediumaquamarine:"#66CDAA",dimgray:"#696969",dimgrey:"#696969",slateblue:"#6A5ACD",olivedrab:"#6B8E23",slategray:"#708090",slategrey:"#708090",lightslategray:"#778899",lightslategrey:"#778899",mediumslateblue:"#7B68EE",lawngreen:"#7CFC00",chartreuse:"#7FFF00",aquamarine:"#7FFFD4",skyblue:"#87CEEB",lightskyblue:"#87CEFA",blueviolet:"#8A2BE2",darkred:"#8B0000",darkmagenta:"#8B008B",saddlebrown:"#8B4513",darkseagreen:"#8FBC8F",lightgreen:"#90EE90",mediumpurple:"#9370DB",darkviolet:"#9400D3",palegreen:"#98FB98",darkorchid:"#9932CC",yellowgreen:"#9ACD32",sienna:"#A0522D",brown:"#A52A2A",darkgray:"#A9A9A9",darkgrey:"#A9A9A9",lightblue:"#ADD8E6",greenyellow:"#ADFF2F",paleturquoise:"#AFEEEE",lightsteelblue:"#B0C4DE",powderblue:"#B0E0E6",firebrick:"#B22222",darkgoldenrod:"#B8860B",mediumorchid:"#BA55D3",rosybrown:"#BC8F8F",darkkhaki:"#BDB76B",mediumvioletred:"#C71585",indianred:"#CD5C5C",peru:"#CD853F",chocolate:"#D2691E",tan:"#D2B48C",lightgray:"#D3D3D3",lightgrey:"#D3D3D3",thistle:"#D8BFD8",orchid:"#DA70D6",goldenrod:"#DAA520",palevioletred:"#DB7093",crimson:"#DC143C",gainsboro:"#DCDCDC",plum:"#DDA0DD",burlywood:"#DEB887",lightcyan:"#E0FFFF",lavender:"#E6E6FA",darksalmon:"#E9967A",violet:"#EE82EE",palegoldenrod:"#EEE8AA",lightcoral:"#F08080",khaki:"#F0E68C",aliceblue:"#F0F8FF",honeydew:"#F0FFF0",azure:"#F0FFFF",sandybrown:"#F4A460",wheat:"#F5DEB3",beige:"#F5F5DC",whitesmoke:"#F5F5F5",mintcream:"#F5FFFA",ghostwhite:"#F8F8FF",salmon:"#FA8072",antiquewhite:"#FAEBD7",linen:"#FAF0E6",lightgoldenrodyellow:"#FAFAD2",oldlace:"#FDF5E6",magenta:"#FF00FF",deeppink:"#FF1493",orangered:"#FF4500",tomato:"#FF6347",hotpink:"#FF69B4",coral:"#FF7F50",darkorange:"#FF8C00",lightsalmon:"#FFA07A",orange:"#FFA500",lightpink:"#FFB6C1",pink:"#FFC0CB",gold:"#FFD700",peachpuff:"#FFDAB9",navajowhite:"#FFDEAD",moccasin:"#FFE4B5",bisque:"#FFE4C4",mistyrose:"#FFE4E1",blanchedalmond:"#FFEBCD",papayawhip:"#FFEFD5",lavenderblush:"#FFF0F5",seashell:"#FFF5EE",cornsilk:"#FFF8DC",lemonchiffon:"#FFFACD",floralwhite:"#FFFAF0",snow:"#FFFAFA",lightyellow:"#FFFFE0",ivory:"#FFFFF0"},Ct=new Int8Array(4),be=new Int32Array(Ct.buffer,0,1),$t=new Float32Array(Ct.buffer,0,1),Kt=/^\s*rgba?\s*\(/,Jt=/^\s*rgba?\s*\(\s*([0-9]*)\s*,\s*([0-9]*)\s*,\s*([0-9]*)(?:\s*,\s*(.*)?)?\)\s*$/;function Qt(r){var n=0,i=0,e=0,t=1;if(r[0]==="#")r.length===4?(n=parseInt(r.charAt(1)+r.charAt(1),16),i=parseInt(r.charAt(2)+r.charAt(2),16),e=parseInt(r.charAt(3)+r.charAt(3),16)):(n=parseInt(r.charAt(1)+r.charAt(2),16),i=parseInt(r.charAt(3)+r.charAt(4),16),e=parseInt(r.charAt(5)+r.charAt(6),16)),r.length===9&&(t=parseInt(r.charAt(7)+r.charAt(8),16)/255);else if(Kt.test(r)){var a=r.match(Jt);a&&(n=+a[1],i=+a[2],e=+a[3],a[4]&&(t=+a[4]))}return{r:n,g:i,b:e,a:t}}var J={};for(var le in pe)J[le]=oe(pe[le]),J[pe[le]]=J[le];function Rt(r,n,i,e,t){return be[0]=e<<24|i<<16|n<<8|r,be[0]=be[0]&4278190079,$t[0]}function oe(r){if(r=r.toLowerCase(),typeof J[r]<"u")return J[r];var n=Qt(r),i=n.r,e=n.g,t=n.b,a=n.a;a=a*255|0;var o=Rt(i,e,t,a);return J[r]=o,o}var Ee={};function _t(r){if(typeof Ee[r]<"u")return Ee[r];var n=(r&16711680)>>>16,i=(r&65280)>>>8,e=r&255,t=255,a=Rt(n,i,e,t);return Ee[r]=a,a}function We(r,n,i,e){return i+(n<<8)+(r<<16)}function Ve(r,n,i,e,t,a){var o=Math.floor(i/a*t),s=Math.floor(r.drawingBufferHeight/a-e/a*t),l=new Uint8Array(4);r.bindFramebuffer(r.FRAMEBUFFER,n),r.readPixels(o,s,1,1,r.RGBA,r.UNSIGNED_BYTE,l);var h=ee(l,4),u=h[0],d=h[1],f=h[2],g=h[3];return[u,d,f,g]}function c(r,n,i){return(n=ae(n))in r?Object.defineProperty(r,n,{value:i,enumerable:!0,configurable:!0,writable:!0}):r[n]=i,r}function je(r,n){var i=Object.keys(r);if(Object.getOwnPropertySymbols){var e=Object.getOwnPropertySymbols(r);n&&(e=e.filter(function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable})),i.push.apply(i,e)}return i}function b(r){for(var n=1;n<arguments.length;n++){var i=arguments[n]!=null?arguments[n]:{};n%2?je(Object(i),!0).forEach(function(e){c(r,e,i[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(i)):je(Object(i)).forEach(function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(i,e))})}return r}function ei(r,n){for(;!{}.hasOwnProperty.call(r,n)&&(r=Q(r))!==null;);return r}function Ae(){return Ae=typeof Reflect<"u"&&Reflect.get?Reflect.get.bind():function(r,n,i){var e=ei(r,n);if(e){var t=Object.getOwnPropertyDescriptor(e,n);return t.get?t.get.call(arguments.length<3?r:i):t.value}},Ae.apply(null,arguments)}function wt(r,n,i,e){var t=Ae(Q(r.prototype),n,i);return typeof t=="function"?function(a){return t.apply(i,a)}:t}function ti(r){return r.normalized?1:r.size}function ye(r){var n=0;return r.forEach(function(i){return n+=ti(i)}),n}function xt(r,n,i){var e=r==="VERTEX"?n.VERTEX_SHADER:n.FRAGMENT_SHADER,t=n.createShader(e);if(t===null)throw new Error("loadShader: error while creating the shader");n.shaderSource(t,i),n.compileShader(t);var a=n.getShaderParameter(t,n.COMPILE_STATUS);if(!a){var o=n.getShaderInfoLog(t);throw n.deleteShader(t),new Error(`loadShader: error while compiling the shader:
3
3
  `.concat(o,`
4
4
  `).concat(i))}return t}function ii(r,n){return xt("VERTEX",r,n)}function ri(r,n){return xt("FRAGMENT",r,n)}function ni(r,n){var i=r.createProgram();if(i===null)throw new Error("loadProgram: error while creating the program.");var e,t;for(e=0,t=n.length;e<t;e++)r.attachShader(i,n[e]);r.linkProgram(i);var a=r.getProgramParameter(i,r.LINK_STATUS);if(!a){var o=r.getProgramInfoLog(i);throw r.deleteProgram(i),new Error("loadProgram: error while linking the program: ".concat(o))}return i}function Ze(r){var n=r.gl,i=r.buffer,e=r.program,t=r.vertexShader,a=r.fragmentShader;n.deleteShader(t),n.deleteShader(a),n.deleteProgram(e),n.deleteBuffer(i)}var qe=`#define PICKING_MODE
package/assets/index.html CHANGED
@@ -12,7 +12,7 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-CcS2bDjR.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-DdXb59Rg.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Dock0eH1.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-4UOaVWza.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-D8rt5lKn.js">
@@ -236,6 +236,7 @@ import {
236
236
  deriveCitedUrlCandidates,
237
237
  deriveSiteHealthState,
238
238
  deriveWinnabilityClass,
239
+ describeError,
239
240
  determineAnswerMentioned,
240
241
  discoveryBucketSchema,
241
242
  discoveryHarvestDtoSchema,
@@ -583,7 +584,7 @@ import {
583
584
  wordpressSchemaDeployResultDtoSchema,
584
585
  wordpressSchemaStatusResultDtoSchema,
585
586
  wordpressStatusDtoSchema
586
- } from "./chunk-P2CQ57UV.js";
587
+ } from "./chunk-TP4KXU3E.js";
587
588
 
588
589
  // src/intelligence-service.ts
589
590
  import { eq as eq59, desc as desc26, asc as asc11, and as and47, ne as ne7, or as or13, inArray as inArray21, gte as gte14, lte as lte12 } from "drizzle-orm";
@@ -1342,6 +1343,14 @@ var siteCrawlSnapshots = sqliteTable("site_crawl_snapshots", {
1342
1343
  deadLinkState: text("dead_link_state").notNull().default("disabled"),
1343
1344
  deadLinksChecked: integer("dead_links_checked").notNull().default(0),
1344
1345
  deadLinksFound: integer("dead_links_found").notNull().default(0),
1346
+ /**
1347
+ * Internal link targets the crawler could not fetch at all, so their state is
1348
+ * unknown. These are NOT dead links and are excluded from both `found` and
1349
+ * `deadLinksChecked` — a timeout or a connection reset under crawl
1350
+ * concurrency says nothing about the URL, and reporting one as broken put
1351
+ * fabricated findings in front of clients.
1352
+ */
1353
+ deadLinksUnverified: integer("dead_links_unverified").notNull().default(0),
1345
1354
  /**
1346
1355
  * Whether nav/header/footer link detection ran for this scan, and if not,
1347
1356
  * why. NULL means the scan predates detection, which reads report as
@@ -3073,6 +3082,33 @@ CREATE TABLE IF NOT EXISTS _migrations (
3073
3082
  applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
3074
3083
  );
3075
3084
  `;
3085
+ function reclassifyFabricatedDeadLinks(tx) {
3086
+ const affected = tx.all(sql2`
3087
+ SELECT run_id AS runId, attempt_id AS attemptId,
3088
+ COUNT(DISTINCT CASE WHEN json_extract(evidence, '$.statusCode') IS NULL THEN target_url END) AS fabricatedTargets,
3089
+ SUM(CASE WHEN json_extract(evidence, '$.statusCode') IS NOT NULL THEN 1 ELSE 0 END) AS realFindings,
3090
+ SUM(CASE WHEN json_extract(evidence, '$.statusCode') IS NULL THEN 1 ELSE 0 END) AS fabricatedFindings
3091
+ FROM site_crawl_findings
3092
+ WHERE finding_type = 'dead-link'
3093
+ GROUP BY run_id, attempt_id
3094
+ HAVING fabricatedFindings > 0
3095
+ `);
3096
+ for (const row of affected) {
3097
+ tx.run(sql2`
3098
+ UPDATE site_crawl_snapshots
3099
+ SET dead_links_unverified = ${row.fabricatedTargets},
3100
+ dead_links_found = ${row.realFindings},
3101
+ findings_count = ${row.realFindings},
3102
+ dead_links_checked = MAX(0, dead_links_checked - ${row.fabricatedTargets})
3103
+ WHERE run_id = ${row.runId} AND attempt_id = ${row.attemptId}
3104
+ `);
3105
+ }
3106
+ tx.run(sql2`
3107
+ DELETE FROM site_crawl_findings
3108
+ WHERE finding_type = 'dead-link'
3109
+ AND json_extract(evidence, '$.statusCode') IS NULL
3110
+ `);
3111
+ }
3076
3112
  function relinkOrphanedSnapshotQueryIds(tx) {
3077
3113
  const orphans = tx.all(sql2`
3078
3114
  SELECT qs.id AS snapId, qs.query_text AS text, r.project_id AS projectId
@@ -6431,6 +6467,36 @@ var MIGRATION_VERSIONS = [
6431
6467
  `CREATE INDEX IF NOT EXISTS idx_ai_user_fetch_verification_manifests_project_ts
6432
6468
  ON ai_user_fetch_verification_manifests_hourly(project_id, ts_hour)`
6433
6469
  ]
6470
+ },
6471
+ {
6472
+ version: 140,
6473
+ name: "site-crawl-dead-links-unverified",
6474
+ // A "dead link" and "a link we could not check" were the same bucket, and
6475
+ // the second one is far more common than the first. The crawler marks a
6476
+ // page `fetch-error` both when the site answers 4xx/5xx AND when the fetch
6477
+ // never completed at all, and the dead-link derivation accepted either —
6478
+ // so a timeout or a reset connection under crawl concurrency became a
6479
+ // reported broken link. On one 228-page site that produced 15 findings
6480
+ // across 6 URLs, every one `statusCode: null`, and every one of those 6
6481
+ // served a 200 in under a second on a manual check. It was client-facing
6482
+ // output.
6483
+ //
6484
+ // Unverified targets now get their own count, and are excluded from both
6485
+ // `dead_links_found` and `dead_links_checked` — calling them "checked"
6486
+ // was the other half of the overstatement.
6487
+ //
6488
+ // The stored rows ARE reclassified, in `run()`, because the evidence to do
6489
+ // it survives on the rows themselves: each finding carries its own
6490
+ // `evidence.statusCode`, so a fabricated row is identifiable one row at a
6491
+ // time and nothing is inferred from the blended totals. Leaving them would
6492
+ // keep the bug live on every past scan — the read path serves stored
6493
+ // findings, so rescanning one project would fix only that project.
6494
+ statements: [
6495
+ `ALTER TABLE site_crawl_snapshots ADD COLUMN dead_links_unverified INTEGER NOT NULL DEFAULT 0`
6496
+ ],
6497
+ run: (tx) => {
6498
+ reclassifyFabricatedDeadLinks(tx);
6499
+ }
6434
6500
  }
6435
6501
  ];
6436
6502
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -13633,7 +13699,7 @@ function assertLimits(limits) {
13633
13699
  // ../api-routes/src/measurement-service.ts
13634
13700
  var DEFAULT_DISCOVERY_URL_LIMIT = 1e4;
13635
13701
  function messageOf(error) {
13636
- return error instanceof Error ? error.message : String(error);
13702
+ return describeError(error);
13637
13703
  }
13638
13704
  async function measurementServiceRoutes(app, opts) {
13639
13705
  app.post("/projects/:name/measurement-discovery", {
@@ -16722,7 +16788,7 @@ function availableStableKey(preferred, taken) {
16722
16788
  throw validationError(`Discovery could not find a free stable key for '${preferred}'.`);
16723
16789
  }
16724
16790
  function messageOf2(error) {
16725
- return error instanceof Error ? error.message : String(error);
16791
+ return describeError(error);
16726
16792
  }
16727
16793
  async function measurementDiscoveryV2Routes(app) {
16728
16794
  app.post("/projects/:name/measurement-plan/draft/actions/import-sitemap", async (request, reply) => {
@@ -19091,7 +19157,7 @@ async function applyRoutes(app, opts) {
19091
19157
  try {
19092
19158
  cronExpr = resolvePreset(schedSpec.preset);
19093
19159
  } catch (err) {
19094
- const msg = err instanceof Error ? err.message : String(err);
19160
+ const msg = describeError(err);
19095
19161
  throw validationError(msg);
19096
19162
  }
19097
19163
  } else if (schedSpec.cron) {
@@ -35138,7 +35204,7 @@ async function scheduleRoutes(app, opts) {
35138
35204
  try {
35139
35205
  cronExpr = resolvePreset(preset);
35140
35206
  } catch (err) {
35141
- const msg = err instanceof Error ? err.message : String(err);
35207
+ const msg = describeError(err);
35142
35208
  throw validationError(msg);
35143
35209
  }
35144
35210
  } else {
@@ -37625,7 +37691,7 @@ function gscErrorToAppError(err, context) {
37625
37691
  status != null ? { upstreamStatus: status } : void 0
37626
37692
  );
37627
37693
  }
37628
- return providerError(`${context}: ${err instanceof Error ? err.message : String(err)}`);
37694
+ return providerError(`${context}: ${describeError(err)}`);
37629
37695
  }
37630
37696
  async function googleRoutes(app, opts) {
37631
37697
  if (opts.googleStateSecret === void 0) {
@@ -37759,7 +37825,7 @@ async function googleRoutes(app, opts) {
37759
37825
  try {
37760
37826
  tokens = await exchangeCode(googleClientId, googleClientSecret, code, redirectUri);
37761
37827
  } catch (err) {
37762
- const msg = err instanceof Error ? err.message : String(err);
37828
+ const msg = describeError(err);
37763
37829
  return reply.type("text/html").send(
37764
37830
  `<html><body style="font-family:system-ui;padding:40px;max-width:600px;margin:0 auto">
37765
37831
  <h2 style="color:#ef4444">Token exchange failed</h2>
@@ -38380,7 +38446,7 @@ async function googleRoutes(app, opts) {
38380
38446
  });
38381
38447
  results.push({ sitemapUrl, status: "accepted", submittedAt });
38382
38448
  } catch (err) {
38383
- results.push({ sitemapUrl, status: "error", error: err instanceof Error ? err.message : String(err) });
38449
+ results.push({ sitemapUrl, status: "error", error: describeError(err) });
38384
38450
  }
38385
38451
  }
38386
38452
  const accepted = results.filter((result) => result.status === "accepted").length;
@@ -38555,7 +38621,7 @@ async function googleRoutes(app, opts) {
38555
38621
  status: "success"
38556
38622
  });
38557
38623
  } catch (err) {
38558
- const msg = err instanceof Error ? err.message : String(err);
38624
+ const msg = describeError(err);
38559
38625
  results.push({
38560
38626
  url,
38561
38627
  type: "URL_UPDATED",
@@ -42867,7 +42933,7 @@ async function adsRoutes(app, opts) {
42867
42933
  try {
42868
42934
  account = await opts.verifyAdsAccount(parsed.data.apiKey);
42869
42935
  } catch (err) {
42870
- const message = err instanceof Error ? err.message : String(err);
42936
+ const message = describeError(err);
42871
42937
  throw validationError(`OpenAI Ads API rejected the key: ${message}`);
42872
42938
  }
42873
42939
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -44204,7 +44270,7 @@ async function bingRoutes(app, opts) {
44204
44270
  sites = await getSites(apiKey);
44205
44271
  bingLog("info", "connect.verify-key", { domain: project.canonicalDomain, siteCount: sites.length });
44206
44272
  } catch (e) {
44207
- const msg = e instanceof Error ? e.message : String(e);
44273
+ const msg = describeError(e);
44208
44274
  bingLog("error", "connect.verify-key-failed", { domain: project.canonicalDomain, error: msg });
44209
44275
  throw validationError(`Failed to verify Bing API key: ${msg}`);
44210
44276
  }
@@ -44472,7 +44538,7 @@ async function bingRoutes(app, opts) {
44472
44538
  } catch (e) {
44473
44539
  bingLog("warn", "inspect-url.crawl-issues-lookup-failed", {
44474
44540
  domain: project.canonicalDomain,
44475
- error: e instanceof Error ? e.message : String(e)
44541
+ error: describeError(e)
44476
44542
  });
44477
44543
  }
44478
44544
  }
@@ -44505,7 +44571,7 @@ async function bingRoutes(app, opts) {
44505
44571
  discoveryDate
44506
44572
  };
44507
44573
  } catch (e) {
44508
- const msg = e instanceof Error ? e.message : String(e);
44574
+ const msg = describeError(e);
44509
44575
  bingLog("error", "inspect-url.failed", { domain: project.canonicalDomain, url, error: msg });
44510
44576
  app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq42(runs.id, runId)).run();
44511
44577
  throw e;
@@ -44583,7 +44649,7 @@ async function bingRoutes(app, opts) {
44583
44649
  }
44584
44650
  bingLog("info", "index-submit.batch-ok", { domain: project.canonicalDomain, batchSize: batch.length, urls: batch });
44585
44651
  } catch (e) {
44586
- const msg = e instanceof Error ? e.message : String(e);
44652
+ const msg = describeError(e);
44587
44653
  const now = (/* @__PURE__ */ new Date()).toISOString();
44588
44654
  for (const url of batch) {
44589
44655
  results.push({ url, status: "error", submittedAt: now, error: msg });
@@ -44598,7 +44664,7 @@ async function bingRoutes(app, opts) {
44598
44664
  results.push({ url, status: "success", submittedAt: (/* @__PURE__ */ new Date()).toISOString() });
44599
44665
  bingLog("info", "index-submit.ok", { domain: project.canonicalDomain, url });
44600
44666
  } catch (e) {
44601
- const msg = e instanceof Error ? e.message : String(e);
44667
+ const msg = describeError(e);
44602
44668
  results.push({ url, status: "error", submittedAt: (/* @__PURE__ */ new Date()).toISOString(), error: msg });
44603
44669
  bingLog("error", "index-submit.failed", { domain: project.canonicalDomain, url, error: msg });
44604
44670
  }
@@ -45156,7 +45222,7 @@ function ga4ErrorToAppError(err, context) {
45156
45222
  }
45157
45223
  return providerError(`${context}: ${err.message}`);
45158
45224
  }
45159
- return providerError(`${context}: ${err instanceof Error ? err.message : String(err)}`);
45225
+ return providerError(`${context}: ${describeError(err)}`);
45160
45226
  }
45161
45227
  async function resolveGa4AccessToken(opts, projectName, canonicalDomain) {
45162
45228
  const saConn = opts.ga4CredentialStore?.getConnection(projectName);
@@ -45222,7 +45288,7 @@ async function ga4Routes(app, opts) {
45222
45288
  await verifyConnection(clientEmail, privateKey, propertyId);
45223
45289
  gaLog("info", "connect.verified.service-account", { projectId: project.id, propertyId });
45224
45290
  } catch (e) {
45225
- const msg = e instanceof Error ? e.message : String(e);
45291
+ const msg = describeError(e);
45226
45292
  gaLog("error", "connect.verify-failed", { projectId: project.id, propertyId, error: msg });
45227
45293
  throw validationError(`Failed to verify GA4 credentials: ${msg}`);
45228
45294
  }
@@ -45267,7 +45333,7 @@ async function ga4Routes(app, opts) {
45267
45333
  await verifyConnectionWithToken(accessToken, propertyId);
45268
45334
  gaLog("info", "connect.verified.oauth", { projectId: project.id, propertyId });
45269
45335
  } catch (e) {
45270
- const msg = e instanceof Error ? e.message : String(e);
45336
+ const msg = describeError(e);
45271
45337
  gaLog("error", "connect.verify-failed.oauth", { projectId: project.id, propertyId, error: msg });
45272
45338
  throw validationError(`Failed to verify GA4 access: ${msg}`);
45273
45339
  }
@@ -45553,7 +45619,7 @@ async function ga4Routes(app, opts) {
45553
45619
  const [acquisitionSettled, leadsSettled] = await measurementFetch;
45554
45620
  const measurementNow = (/* @__PURE__ */ new Date()).toISOString();
45555
45621
  const recordMeasurementFailure = (component, reason) => {
45556
- const message = reason instanceof Error ? reason.message : String(reason);
45622
+ const message = describeError(reason);
45557
45623
  gaLog("warn", "measurement.component-failed", {
45558
45624
  projectId: project.id,
45559
45625
  runId,
@@ -45567,7 +45633,7 @@ async function ga4Routes(app, opts) {
45567
45633
  projectId: project.id,
45568
45634
  runId,
45569
45635
  component,
45570
- error: stateError instanceof Error ? stateError.message : String(stateError)
45636
+ error: describeError(stateError)
45571
45637
  });
45572
45638
  }
45573
45639
  return { status: "error", days: component === "acquisition" ? acquisitionDays : leadDays, rowCount: 0, error: message };
@@ -45651,7 +45717,7 @@ async function ga4Routes(app, opts) {
45651
45717
  ...syncedComponents ? { syncedComponents } : {}
45652
45718
  };
45653
45719
  } catch (e) {
45654
- const msg = e instanceof Error ? e.message : String(e);
45720
+ const msg = describeError(e);
45655
45721
  gaLog("error", "sync.fetch-failed", { projectId: project.id, runId, error: msg });
45656
45722
  app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq44(runs.id, runId)).run();
45657
45723
  throw e;
@@ -46532,7 +46598,7 @@ async function getSiteStatus(connection, env) {
46532
46598
  authenticatedUser: userInfo
46533
46599
  };
46534
46600
  } catch (error) {
46535
- const message = error instanceof Error ? error.message : String(error);
46601
+ const message = describeError(error);
46536
46602
  return {
46537
46603
  url: site.siteUrl,
46538
46604
  reachable: false,
@@ -46813,7 +46879,7 @@ async function bulkSetSeoMeta(connection, entries, env) {
46813
46879
  return {
46814
46880
  slug: entry.slug,
46815
46881
  status: "skipped",
46816
- error: error instanceof Error ? error.message : String(error)
46882
+ error: describeError(error)
46817
46883
  };
46818
46884
  }
46819
46885
  }
@@ -46894,7 +46960,7 @@ async function deploySchema(connection, slug, schemas, env) {
46894
46960
  return {
46895
46961
  slug,
46896
46962
  status: "failed",
46897
- error: error instanceof Error ? error.message : String(error)
46963
+ error: describeError(error)
46898
46964
  };
46899
46965
  }
46900
46966
  }
@@ -47581,7 +47647,7 @@ async function wordpressRoutes(app, opts) {
47581
47647
  });
47582
47648
  steps.push({ name: "connect", status: "completed", summary: `Connected to ${url}` });
47583
47649
  } catch (err) {
47584
- const msg = err instanceof Error ? err.message : String(err);
47650
+ const msg = describeError(err);
47585
47651
  steps.push({ name: "connect", status: "failed", error: msg });
47586
47652
  return { projectName: project.name, steps };
47587
47653
  }
@@ -47597,7 +47663,7 @@ async function wordpressRoutes(app, opts) {
47597
47663
  pageUrls = pageSummaries.map((p) => p.link).filter((link) => typeof link === "string" && link.length > 0);
47598
47664
  steps.push({ name: "audit", status: "completed", summary: `${pageCount} pages audited, ${issueCount} issues` });
47599
47665
  } catch (err) {
47600
- const msg = err instanceof Error ? err.message : String(err);
47666
+ const msg = describeError(err);
47601
47667
  steps.push({ name: "audit", status: "failed", error: msg });
47602
47668
  return { projectName: project.name, steps };
47603
47669
  }
@@ -47637,7 +47703,7 @@ async function wordpressRoutes(app, opts) {
47637
47703
  });
47638
47704
  }
47639
47705
  } catch (err) {
47640
- const msg = err instanceof Error ? err.message : String(err);
47706
+ const msg = describeError(err);
47641
47707
  steps.push({ name: "set-meta", status: "failed", error: msg });
47642
47708
  return { projectName: project.name, steps };
47643
47709
  }
@@ -47663,7 +47729,7 @@ async function wordpressRoutes(app, opts) {
47663
47729
  });
47664
47730
  }
47665
47731
  } catch (err) {
47666
- const msg = err instanceof Error ? err.message : String(err);
47732
+ const msg = describeError(err);
47667
47733
  steps.push({ name: "schema-deploy", status: "failed", error: msg });
47668
47734
  return { projectName: project.name, steps };
47669
47735
  }
@@ -47695,7 +47761,7 @@ async function wordpressRoutes(app, opts) {
47695
47761
  }
47696
47762
  }
47697
47763
  } catch (err) {
47698
- const msg = err instanceof Error ? err.message : String(err);
47764
+ const msg = describeError(err);
47699
47765
  steps.push({ name: "google-submit", status: "skipped", summary: `Google not available: ${msg}` });
47700
47766
  }
47701
47767
  try {
@@ -47720,7 +47786,7 @@ async function wordpressRoutes(app, opts) {
47720
47786
  }
47721
47787
  }
47722
47788
  } catch (err) {
47723
- const msg = err instanceof Error ? err.message : String(err);
47789
+ const msg = describeError(err);
47724
47790
  steps.push({ name: "bing-submit", status: "skipped", summary: `Bing not available: ${msg}` });
47725
47791
  }
47726
47792
  }
@@ -54153,7 +54219,7 @@ async function runBackfillTask(options) {
54153
54219
  try {
54154
54220
  allEvents = await pullForBackfill();
54155
54221
  } catch (e) {
54156
- markFailed(`${pullErrorPrefix}: ${e instanceof Error ? e.message : String(e)}`);
54222
+ markFailed(`${pullErrorPrefix}: ${describeError(e)}`);
54157
54223
  return;
54158
54224
  }
54159
54225
  if (allEvents.length === 0) {
@@ -54416,7 +54482,7 @@ async function runBackfillTask(options) {
54416
54482
  });
54417
54483
  if (commitOutcome === "source-inactive") return;
54418
54484
  } catch (e) {
54419
- markFailed(`Backfill rollup write failed: ${e instanceof Error ? e.message : String(e)}`);
54485
+ markFailed(`Backfill rollup write failed: ${describeError(e)}`);
54420
54486
  }
54421
54487
  }
54422
54488
  async function trafficRoutes(app, opts) {
@@ -54625,7 +54691,7 @@ async function trafficRoutes(app, opts) {
54625
54691
  `WordPress traffic probe failed (HTTP ${e.status}): ${e.message}${e.body ? ` \u2014 ${e.body}` : ""}`
54626
54692
  );
54627
54693
  }
54628
- const msg = e instanceof Error ? e.message : String(e);
54694
+ const msg = describeError(e);
54629
54695
  throw providerError(`WordPress traffic probe failed: ${msg}`);
54630
54696
  }
54631
54697
  });
@@ -54716,7 +54782,7 @@ async function trafficRoutes(app, opts) {
54716
54782
  `Vercel traffic probe failed (HTTP ${e.status}): ${e.message}${e.body ? ` \u2014 ${e.body}` : ""}`
54717
54783
  );
54718
54784
  }
54719
- const msg = e instanceof Error ? e.message : String(e);
54785
+ const msg = describeError(e);
54720
54786
  throw providerError(`Vercel traffic probe failed: ${msg}`);
54721
54787
  }
54722
54788
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -55583,7 +55649,7 @@ async function trafficRoutes(app, opts) {
55583
55649
  try {
55584
55650
  accessToken = await resolveAccessToken2(credential);
55585
55651
  } catch (e) {
55586
- const msg = e instanceof Error ? e.message : String(e);
55652
+ const msg = describeError(e);
55587
55653
  markFailed(msg, "PROVIDER_AUTH");
55588
55654
  throw providerError(`Failed to resolve Cloud Run access token: ${msg}`);
55589
55655
  }
@@ -55602,7 +55668,7 @@ async function trafficRoutes(app, opts) {
55602
55668
  });
55603
55669
  allEvents = page.events;
55604
55670
  } catch (e) {
55605
- const msg = e instanceof Error ? e.message : String(e);
55671
+ const msg = describeError(e);
55606
55672
  markFailed(msg, "PROVIDER_PULL");
55607
55673
  throw providerError(`Cloud Run pull failed: ${msg}`);
55608
55674
  }
@@ -55626,7 +55692,7 @@ async function trafficRoutes(app, opts) {
55626
55692
  try {
55627
55693
  pinnedDispatcher = await assertWordpressTargetAllowed(credential.baseUrl);
55628
55694
  } catch (e) {
55629
- const msg = e instanceof Error ? e.message : String(e);
55695
+ const msg = describeError(e);
55630
55696
  markFailed(msg, "PROVIDER_PULL");
55631
55697
  throw e;
55632
55698
  }
@@ -55652,7 +55718,7 @@ async function trafficRoutes(app, opts) {
55652
55718
  allEvents = collected;
55653
55719
  nextCursor = cursor;
55654
55720
  } catch (e) {
55655
- const msg = e instanceof Error ? e.message : String(e);
55721
+ const msg = describeError(e);
55656
55722
  markFailed(msg, "PROVIDER_PULL");
55657
55723
  throw providerError(`WordPress pull failed: ${msg}`);
55658
55724
  } finally {
@@ -55754,7 +55820,7 @@ async function trafficRoutes(app, opts) {
55754
55820
  }
55755
55821
  allEvents = drained.events;
55756
55822
  } catch (e) {
55757
- const msg = e instanceof Error ? e.message : String(e);
55823
+ const msg = describeError(e);
55758
55824
  markFailed(msg, "PROVIDER_PULL");
55759
55825
  throw providerError(`Vercel pull failed: ${msg}`);
55760
55826
  }
@@ -56996,7 +57062,7 @@ var BING_AUTH_CHECKS = [
56996
57062
  remediation: null
56997
57063
  };
56998
57064
  } catch (err) {
56999
- const message = err instanceof Error ? err.message : String(err);
57065
+ const message = describeError(err);
57000
57066
  return {
57001
57067
  status: CheckStatuses.fail,
57002
57068
  code: "bing.auth.verification-failed",
@@ -57078,7 +57144,7 @@ var BING_AUTH_CHECKS = [
57078
57144
  remediation: null
57079
57145
  };
57080
57146
  } catch (err) {
57081
- const message = err instanceof Error ? err.message : String(err);
57147
+ const message = describeError(err);
57082
57148
  return {
57083
57149
  status: CheckStatuses.fail,
57084
57150
  code: "bing.auth.site-check-failed",
@@ -57338,7 +57404,7 @@ async function checkServiceAccount(conn) {
57338
57404
  try {
57339
57405
  await verifyConnection(conn.clientEmail, conn.privateKey, conn.propertyId);
57340
57406
  } catch (err) {
57341
- const message = err instanceof Error ? err.message : String(err);
57407
+ const message = describeError(err);
57342
57408
  return {
57343
57409
  status: CheckStatuses.fail,
57344
57410
  code: "ga.auth.verify-failed",
@@ -57387,7 +57453,7 @@ async function checkOAuthConnection(ctx, projectName, conn) {
57387
57453
  const tokens = await refreshAccessToken(auth.clientId, auth.clientSecret, conn.refreshToken);
57388
57454
  accessToken = tokens.access_token;
57389
57455
  } catch (err) {
57390
- const message = err instanceof Error ? err.message : String(err);
57456
+ const message = describeError(err);
57391
57457
  return {
57392
57458
  status: CheckStatuses.fail,
57393
57459
  code: "ga.auth.refresh-failed",
@@ -57399,7 +57465,7 @@ async function checkOAuthConnection(ctx, projectName, conn) {
57399
57465
  try {
57400
57466
  await verifyConnectionWithToken(accessToken, conn.propertyId);
57401
57467
  } catch (err) {
57402
- const message = err instanceof Error ? err.message : String(err);
57468
+ const message = describeError(err);
57403
57469
  return {
57404
57470
  status: CheckStatuses.fail,
57405
57471
  code: "ga.auth.verify-failed",
@@ -57518,7 +57584,7 @@ async function resolveGbpToken(ctx) {
57518
57584
  const tokens = await refreshAccessToken(auth.clientId, auth.clientSecret, conn.refreshToken);
57519
57585
  return { ok: true, token: { accessToken: tokens.access_token, conn } };
57520
57586
  } catch (err) {
57521
- const message = err instanceof Error ? err.message : String(err);
57587
+ const message = describeError(err);
57522
57588
  return {
57523
57589
  ok: false,
57524
57590
  output: {
@@ -57650,7 +57716,7 @@ var accountAccessCheck = {
57650
57716
  details: { reason: err.reason ?? void 0, status: err.status }
57651
57717
  };
57652
57718
  }
57653
- const message = err instanceof Error ? err.message : String(err);
57719
+ const message = describeError(err);
57654
57720
  return {
57655
57721
  status: CheckStatuses.fail,
57656
57722
  code: "gbp.account.list-failed",
@@ -57882,7 +57948,7 @@ async function resolveAccessToken(ctx) {
57882
57948
  const tokens = await refreshAccessToken(auth.clientId, auth.clientSecret, conn.refreshToken);
57883
57949
  return { ok: true, token: { accessToken: tokens.access_token } };
57884
57950
  } catch (err) {
57885
- const message = err instanceof Error ? err.message : String(err);
57951
+ const message = describeError(err);
57886
57952
  return {
57887
57953
  ok: false,
57888
57954
  output: {
@@ -57974,7 +58040,7 @@ var propertyAccessCheck = {
57974
58040
  details: { error: err.message }
57975
58041
  };
57976
58042
  }
57977
- const message = err instanceof Error ? err.message : String(err);
58043
+ const message = describeError(err);
57978
58044
  return {
57979
58045
  status: CheckStatuses.fail,
57980
58046
  code: "google.auth.list-sites-failed",
@@ -58561,7 +58627,7 @@ async function runValidator(source, validator, fallbackId, fallbackLabel) {
58561
58627
  }
58562
58628
  return { source, output: result };
58563
58629
  } catch (e) {
58564
- const msg = e instanceof Error ? e.message : String(e);
58630
+ const msg = describeError(e);
58565
58631
  return {
58566
58632
  source,
58567
58633
  output: {
@@ -59016,7 +59082,7 @@ var WORDPRESS_PUBLISH_CHECKS = [
59016
59082
  details: { error: err.message }
59017
59083
  };
59018
59084
  }
59019
- const message = err instanceof Error ? err.message : String(err);
59085
+ const message = describeError(err);
59020
59086
  return {
59021
59087
  status: CheckStatuses.fail,
59022
59088
  code: "wordpress.publish.verification-failed",
@@ -59076,7 +59142,7 @@ async function runChecks(ctx, checks, options = {}) {
59076
59142
  try {
59077
59143
  output = await definition.run(ctx);
59078
59144
  } catch (err) {
59079
- const message = err instanceof Error ? err.message : String(err);
59145
+ const message = describeError(err);
59080
59146
  output = {
59081
59147
  status: CheckStatuses.fail,
59082
59148
  code: `${definition.id}.runtime-error`,
@@ -60335,7 +60401,7 @@ async function technicalAeoRoutes(app, opts) {
60335
60401
  return emptyCrawlSummary(project.name, legacyAuditAvailable);
60336
60402
  }
60337
60403
  const snapshot = target.snapshot;
60338
- const deadLinks = !snapshot.checkDeadLinks || snapshot.deadLinkState === "disabled" ? { state: "disabled" } : snapshot.deadLinkState === "complete" ? { state: "complete", checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound } : snapshot.deadLinkState === "partial" ? { state: "partial", checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound } : { state: "unavailable" };
60404
+ const deadLinks = !snapshot.checkDeadLinks || snapshot.deadLinkState === "disabled" ? { state: "disabled" } : snapshot.deadLinkState === "complete" ? { state: "complete", checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound, unverified: snapshot.deadLinksUnverified } : snapshot.deadLinkState === "partial" ? { state: "partial", checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound, unverified: snapshot.deadLinksUnverified } : { state: "unavailable" };
60339
60405
  return {
60340
60406
  project: project.name,
60341
60407
  hasCrawlData: true,
@@ -61502,13 +61568,23 @@ async function technicalAeoRoutes(app, opts) {
61502
61568
  return { project: project.name, runId: snapshot.runId, state: "unavailable", legacyAuditAvailable };
61503
61569
  }
61504
61570
  if (!snapshot.attemptId) {
61505
- return { project: project.name, runId: snapshot.runId, state: "partial", checkDeadLinks: true, checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound, total: 0, nextCursor: null, deadLinks: [] };
61571
+ return { project: project.name, runId: snapshot.runId, state: "partial", checkDeadLinks: true, checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound, unverified: snapshot.deadLinksUnverified, total: 0, nextCursor: null, deadLinks: [] };
61506
61572
  }
61507
61573
  const where = and45(
61508
61574
  eq57(siteCrawlFindings.projectId, project.id),
61509
61575
  eq57(siteCrawlFindings.runId, snapshot.runId),
61510
61576
  eq57(siteCrawlFindings.attemptId, snapshot.attemptId),
61511
- eq57(siteCrawlFindings.findingType, "dead-link")
61577
+ eq57(siteCrawlFindings.findingType, "dead-link"),
61578
+ // Belt and braces on the client-facing surface: a row without a status
61579
+ // code is not evidence of a broken link, and this response is where such
61580
+ // a row would be read as one. Migration 140 removed every stored row that
61581
+ // looked like this and the writer can no longer produce one, so this
61582
+ // matches nothing today — it is here because a rolling deploy can still
61583
+ // run an older writer against a migrated database for a few minutes, and
61584
+ // a fabricated broken link shown to a client is not worth the saved
61585
+ // predicate. Applied to `total` as well as the rows, or the count would
61586
+ // promise a page the list cannot fill.
61587
+ isNotNull3(sql21`json_extract(${siteCrawlFindings.evidence}, '$.statusCode')`)
61512
61588
  );
61513
61589
  const total = app.db.select({ value: count() }).from(siteCrawlFindings).where(where).get()?.value ?? 0;
61514
61590
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
@@ -61532,6 +61608,7 @@ async function technicalAeoRoutes(app, opts) {
61532
61608
  checkDeadLinks: true,
61533
61609
  checked: snapshot.deadLinksChecked,
61534
61610
  found: snapshot.deadLinksFound,
61611
+ unverified: snapshot.deadLinksUnverified,
61535
61612
  total,
61536
61613
  nextCursor: nextOffset < total ? encodeCursor2(nextOffset) : null,
61537
61614
  deadLinks
@@ -62100,7 +62177,7 @@ function buildTrafficSourceValidators(opts) {
62100
62177
  summary: `Cloud Run access token resolves for "${source.displayName}" (project ${record.gcpProjectId}).`
62101
62178
  };
62102
62179
  } catch (e) {
62103
- const msg = e instanceof Error ? e.message : String(e);
62180
+ const msg = describeError(e);
62104
62181
  return {
62105
62182
  status: CheckStatuses.fail,
62106
62183
  code: "traffic.credentials.resolve-failed",
@@ -62146,7 +62223,7 @@ function buildTrafficSourceValidators(opts) {
62146
62223
  };
62147
62224
  } catch (e) {
62148
62225
  const httpStatus = e instanceof WordpressTrafficApiError ? e.status : null;
62149
- const msg = e instanceof Error ? e.message : String(e);
62226
+ const msg = describeError(e);
62150
62227
  return {
62151
62228
  status: CheckStatuses.fail,
62152
62229
  code: httpStatus === 401 || httpStatus === 403 ? "traffic.credentials.unauthorized" : "traffic.credentials.resolve-failed",
@@ -62193,7 +62270,7 @@ function buildTrafficSourceValidators(opts) {
62193
62270
  };
62194
62271
  } catch (e) {
62195
62272
  const httpStatus = e instanceof VercelLogsApiError ? e.status : null;
62196
- const msg = e instanceof Error ? e.message : String(e);
62273
+ const msg = describeError(e);
62197
62274
  return {
62198
62275
  status: CheckStatuses.fail,
62199
62276
  code: httpStatus === 401 || httpStatus === 403 ? "traffic.credentials.unauthorized" : "traffic.credentials.resolve-failed",