@canonry/canonry 4.166.0 → 4.167.1

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 (25) hide show
  1. package/README.md +31 -19
  2. package/assets/assets/{AuditHistoryPanel-CZByTZjH.js → AuditHistoryPanel-Dvz6QFtW.js} +1 -1
  3. package/assets/assets/{BacklinksPage-DPsSKu1M.js → BacklinksPage-BHOtOIr5.js} +1 -1
  4. package/assets/assets/{HistoryPage-9acsanzg.js → HistoryPage-Bc5Nx6dX.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-gIOHHJ4x.js → MeasurementPropertyPage-D4FIFiWv.js} +1 -1
  6. package/assets/assets/{ProjectPage-CTic0OFR.js → ProjectPage-CT5ZD7_A.js} +1 -1
  7. package/assets/assets/{RunRow-cuE1cBCg.js → RunRow-CB2oG45_.js} +1 -1
  8. package/assets/assets/{RunsPage-Bt6T2Kz2.js → RunsPage-OLbocIOY.js} +1 -1
  9. package/assets/assets/{SettingsPage-ChyewFuC.js → SettingsPage-DVdPBrap.js} +1 -1
  10. package/assets/assets/{SiteHealthSection-BYM32RDv.js → SiteHealthSection-B1bjMCqY.js} +3 -3
  11. package/assets/assets/{TrafficPage-DMpuFJua.js → TrafficPage-BUWXjHhw.js} +1 -1
  12. package/assets/assets/{TrafficSourceDetailPage-D4G26uyv.js → TrafficSourceDetailPage-DwoFzTu7.js} +1 -1
  13. package/assets/assets/{extract-error-message-D7fD7zwP.js → extract-error-message-CNH2-nPH.js} +1 -1
  14. package/assets/assets/{index-BliSumN-.js → index-DdXb59Rg.js} +3 -3
  15. package/assets/assets/{react-sigma_core.esm.min-CmW2JaYh.js → react-sigma_core.esm.min-DLRh4X_G.js} +1 -1
  16. package/assets/index.html +1 -1
  17. package/dist/{chunk-KU45I42F.js → chunk-7M3QVRJD.js} +80 -4
  18. package/dist/{chunk-IUWFALUQ.js → chunk-NFG2BPQP.js} +25 -6
  19. package/dist/{chunk-QFMHIUV7.js → chunk-P5LR3NDQ.js} +1 -1
  20. package/dist/{chunk-GU7T2JUB.js → chunk-TP4KXU3E.js} +4 -2
  21. package/dist/cli.js +11 -5
  22. package/dist/index.js +4 -4
  23. package/dist/{intelligence-service-KRQNQNEB.js → intelligence-service-ULDOVV5Y.js} +2 -2
  24. package/dist/mcp.js +2 -2
  25. package/package.json +8 -8
@@ -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-BYM32RDv.js";import"./index-BliSumN-.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-BliSumN-.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">
@@ -584,7 +584,7 @@ import {
584
584
  wordpressSchemaDeployResultDtoSchema,
585
585
  wordpressSchemaStatusResultDtoSchema,
586
586
  wordpressStatusDtoSchema
587
- } from "./chunk-GU7T2JUB.js";
587
+ } from "./chunk-TP4KXU3E.js";
588
588
 
589
589
  // src/intelligence-service.ts
590
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";
@@ -1343,6 +1343,14 @@ var siteCrawlSnapshots = sqliteTable("site_crawl_snapshots", {
1343
1343
  deadLinkState: text("dead_link_state").notNull().default("disabled"),
1344
1344
  deadLinksChecked: integer("dead_links_checked").notNull().default(0),
1345
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),
1346
1354
  /**
1347
1355
  * Whether nav/header/footer link detection ran for this scan, and if not,
1348
1356
  * why. NULL means the scan predates detection, which reads report as
@@ -3074,6 +3082,33 @@ CREATE TABLE IF NOT EXISTS _migrations (
3074
3082
  applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
3075
3083
  );
3076
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
+ }
3077
3112
  function relinkOrphanedSnapshotQueryIds(tx) {
3078
3113
  const orphans = tx.all(sql2`
3079
3114
  SELECT qs.id AS snapId, qs.query_text AS text, r.project_id AS projectId
@@ -6432,6 +6467,36 @@ var MIGRATION_VERSIONS = [
6432
6467
  `CREATE INDEX IF NOT EXISTS idx_ai_user_fetch_verification_manifests_project_ts
6433
6468
  ON ai_user_fetch_verification_manifests_hourly(project_id, ts_hour)`
6434
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
+ }
6435
6500
  }
6436
6501
  ];
6437
6502
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -60336,7 +60401,7 @@ async function technicalAeoRoutes(app, opts) {
60336
60401
  return emptyCrawlSummary(project.name, legacyAuditAvailable);
60337
60402
  }
60338
60403
  const snapshot = target.snapshot;
60339
- 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" };
60340
60405
  return {
60341
60406
  project: project.name,
60342
60407
  hasCrawlData: true,
@@ -61503,13 +61568,23 @@ async function technicalAeoRoutes(app, opts) {
61503
61568
  return { project: project.name, runId: snapshot.runId, state: "unavailable", legacyAuditAvailable };
61504
61569
  }
61505
61570
  if (!snapshot.attemptId) {
61506
- 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: [] };
61507
61572
  }
61508
61573
  const where = and45(
61509
61574
  eq57(siteCrawlFindings.projectId, project.id),
61510
61575
  eq57(siteCrawlFindings.runId, snapshot.runId),
61511
61576
  eq57(siteCrawlFindings.attemptId, snapshot.attemptId),
61512
- 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')`)
61513
61588
  );
61514
61589
  const total = app.db.select({ value: count() }).from(siteCrawlFindings).where(where).get()?.value ?? 0;
61515
61590
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
@@ -61533,6 +61608,7 @@ async function technicalAeoRoutes(app, opts) {
61533
61608
  checkDeadLinks: true,
61534
61609
  checked: snapshot.deadLinksChecked,
61535
61610
  found: snapshot.deadLinksFound,
61611
+ unverified: snapshot.deadLinksUnverified,
61536
61612
  total,
61537
61613
  nextCursor: nextOffset < total ? encodeCursor2(nextOffset) : null,
61538
61614
  deadLinks
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-QFMHIUV7.js";
13
+ } from "./chunk-P5LR3NDQ.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -139,7 +139,7 @@ import {
139
139
  siteCrawlSnapshots,
140
140
  toAlertView,
141
141
  usageCounters
142
- } from "./chunk-KU45I42F.js";
142
+ } from "./chunk-7M3QVRJD.js";
143
143
  import {
144
144
  AGENT_MEMORY_VALUE_MAX_BYTES,
145
145
  AGENT_PROVIDER_IDS,
@@ -254,7 +254,7 @@ import {
254
254
  validationError,
255
255
  winnabilityClassLabel,
256
256
  withRetry
257
- } from "./chunk-GU7T2JUB.js";
257
+ } from "./chunk-TP4KXU3E.js";
258
258
 
259
259
  // src/telemetry.ts
260
260
  import crypto from "crypto";
@@ -8085,15 +8085,31 @@ function observedErrorCount(pages) {
8085
8085
  for (const page of pages) if (page.state === "fetch-error") total++;
8086
8086
  return total;
8087
8087
  }
8088
+ function isUnverifiablePage(page) {
8089
+ return page.state === "fetch-error" && page.statusCode === null;
8090
+ }
8088
8091
  function deadLinkCheckedCount(edges, pages) {
8089
8092
  const attemptedUrls = /* @__PURE__ */ new Set();
8090
8093
  for (const page of pages) {
8091
8094
  if (page.state === "discovered" || page.state === "robots-blocked") continue;
8095
+ if (isUnverifiablePage(page)) continue;
8092
8096
  attemptedUrls.add(page.requestedUrl);
8093
8097
  if (page.finalUrl) attemptedUrls.add(page.finalUrl);
8094
8098
  }
8095
8099
  return new Set([...edges].filter((edge) => edge.type === "anchor" && edge.classification === "internal" && attemptedUrls.has(edge.to)).map((edge) => edge.to)).size;
8096
8100
  }
8101
+ var RATE_LIMITED_STATUS = 429;
8102
+ function partitionDeadLinks(deadLinks) {
8103
+ const reported = deadLinks.findings;
8104
+ const engineUnverified = deadLinks.unverified ?? [];
8105
+ const answeredWithError = (finding) => typeof finding.statusCode === "number" && finding.statusCode >= 400;
8106
+ const dead = reported.filter((finding) => answeredWithError(finding) && finding.statusCode !== RATE_LIMITED_STATUS);
8107
+ const unverified = [
8108
+ ...reported.filter((finding) => !answeredWithError(finding) || finding.statusCode === RATE_LIMITED_STATUS),
8109
+ ...engineUnverified
8110
+ ];
8111
+ return { dead, unverified };
8112
+ }
8097
8113
  async function executeSiteAudit(db, runId, projectId, opts = {}) {
8098
8114
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
8099
8115
  const claim = db.update(runs).set({ status: "running", startedAt }).where(and12(eq15(runs.id, runId), eq15(runs.projectId, projectId), eq15(runs.status, "queued"))).run();
@@ -8435,7 +8451,9 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
8435
8451
  const errorCount = observedErrorCount(observedPages.values());
8436
8452
  const terminalStatus = crawlSummary.complete ? "completed" : "partial";
8437
8453
  const deadLinksChecked = opts.checkDeadLinks ? deadLinkCheckedCount(observedEdges.values(), observedPages.values()) : 0;
8438
- const deadLinksFound = report.deadLinks.findings.length;
8454
+ const { dead: deadLinkFindings, unverified: unverifiedLinks } = partitionDeadLinks(report.deadLinks);
8455
+ const deadLinksFound = deadLinkFindings.length;
8456
+ const deadLinksUnverified = new Set(unverifiedLinks.map((finding) => finding.to)).size;
8439
8457
  const legacyIssues = factors.map((factor) => toLegacyIssue(factor, crawlSummary.auditRollup.auditedPages)).filter((issue) => issue !== null);
8440
8458
  try {
8441
8459
  persistSiteCrawlGraphLayout(db, { projectId, runId, attemptId }, graphLayout, finishedAt);
@@ -8536,13 +8554,14 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
8536
8554
  deadLinkState: report.deadLinks.state,
8537
8555
  deadLinksChecked,
8538
8556
  deadLinksFound,
8557
+ deadLinksUnverified,
8539
8558
  templateDetection: templateLinks.detection,
8540
8559
  linkPlacementRulesetVersion: placementRulesetVersion,
8541
8560
  createdAt: finishedAt,
8542
8561
  updatedAt: finishedAt
8543
8562
  }).run();
8544
8563
  if (opts.checkDeadLinks) {
8545
- for (const finding of report.deadLinks.findings) {
8564
+ for (const finding of deadLinkFindings) {
8546
8565
  tx.insert(siteCrawlFindings).values({
8547
8566
  id: crypto17.randomUUID(),
8548
8567
  projectId,
@@ -9075,7 +9094,7 @@ function readStoredGroundingSources(rawResponse) {
9075
9094
  return result;
9076
9095
  }
9077
9096
  async function backfillInsightsCommand(project, opts) {
9078
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-KRQNQNEB.js");
9097
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-ULDOVV5Y.js");
9079
9098
  const config = loadConfig();
9080
9099
  const db = createClient(config.database);
9081
9100
  migrate(db);
@@ -82,7 +82,7 @@ import {
82
82
  trafficConnectWordpressRequestSchema,
83
83
  trafficEventKindSchema,
84
84
  trafficSeriesGranularitySchema
85
- } from "./chunk-GU7T2JUB.js";
85
+ } from "./chunk-TP4KXU3E.js";
86
86
 
87
87
  // src/config.ts
88
88
  import fs from "fs";
@@ -8717,8 +8717,8 @@ var siteCrawlCountsSchema = z25.object({
8717
8717
  var siteCrawlDeadLinksSummarySchema = z25.discriminatedUnion("state", [
8718
8718
  z25.object({ state: z25.literal("unavailable") }),
8719
8719
  z25.object({ state: z25.literal("disabled") }),
8720
- z25.object({ state: z25.literal("complete"), checked: z25.number().int().nonnegative(), found: z25.number().int().nonnegative() }),
8721
- z25.object({ state: z25.literal("partial"), checked: z25.number().int().nonnegative(), found: z25.number().int().nonnegative() })
8720
+ z25.object({ state: z25.literal("complete"), checked: z25.number().int().nonnegative(), found: z25.number().int().nonnegative(), unverified: z25.number().int().nonnegative() }),
8721
+ z25.object({ state: z25.literal("partial"), checked: z25.number().int().nonnegative(), found: z25.number().int().nonnegative(), unverified: z25.number().int().nonnegative() })
8722
8722
  ]);
8723
8723
  var siteCrawlSummarySchema = z25.object({
8724
8724
  project: z25.string(),
@@ -9400,6 +9400,7 @@ var siteCrawlDeadLinksResponseSchema = z25.discriminatedUnion("state", [
9400
9400
  checkDeadLinks: z25.literal(true),
9401
9401
  checked: z25.number().int().nonnegative(),
9402
9402
  found: z25.number().int().nonnegative(),
9403
+ unverified: z25.number().int().nonnegative(),
9403
9404
  total: z25.number().int().nonnegative(),
9404
9405
  nextCursor: z25.string().nullable(),
9405
9406
  deadLinks: z25.array(siteCrawlDeadLinkSchema).default([])
@@ -9411,6 +9412,7 @@ var siteCrawlDeadLinksResponseSchema = z25.discriminatedUnion("state", [
9411
9412
  checkDeadLinks: z25.literal(true),
9412
9413
  checked: z25.number().int().nonnegative(),
9413
9414
  found: z25.number().int().nonnegative(),
9415
+ unverified: z25.number().int().nonnegative(),
9414
9416
  total: z25.number().int().nonnegative(),
9415
9417
  nextCursor: z25.string().nullable(),
9416
9418
  deadLinks: z25.array(siteCrawlDeadLinkSchema).default([])
package/dist/cli.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  showFirstRunNotice,
32
32
  trackCliCommandFinished,
33
33
  trackEvent
34
- } from "./chunk-IUWFALUQ.js";
34
+ } from "./chunk-NFG2BPQP.js";
35
35
  import {
36
36
  CliError,
37
37
  EXIT_SYSTEM_ERROR,
@@ -51,7 +51,7 @@ import {
51
51
  saveConfigPatch,
52
52
  systemError,
53
53
  usageError
54
- } from "./chunk-QFMHIUV7.js";
54
+ } from "./chunk-P5LR3NDQ.js";
55
55
  import {
56
56
  CLOUDFLARE_WORKER_BINDINGS,
57
57
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -65,7 +65,7 @@ import {
65
65
  projects,
66
66
  queries,
67
67
  renderReportHtml
68
- } from "./chunk-KU45I42F.js";
68
+ } from "./chunk-7M3QVRJD.js";
69
69
  import {
70
70
  AdsDeliverySnapshotStatuses,
71
71
  AdsHistoricalCampaignRollupStatuses,
@@ -130,7 +130,7 @@ import {
130
130
  providerQuotaPolicySchema,
131
131
  resolveProviderInput,
132
132
  winnabilityClassSchema
133
- } from "./chunk-GU7T2JUB.js";
133
+ } from "./chunk-TP4KXU3E.js";
134
134
 
135
135
  // src/cli.ts
136
136
  import { pathToFileURL } from "url";
@@ -14948,13 +14948,19 @@ async function technicalAeoDeadLinks(project, opts) {
14948
14948
  }
14949
14949
  const lines = [`Dead links: ${res.found} found from ${res.checked} checked (${res.state})`];
14950
14950
  for (const finding of res.deadLinks) lines.push(`${finding.sourceUrl ?? "-"} \u2192 ${finding.targetUrl ?? "-"}`);
14951
+ if (res.unverified > 0) lines.push(unverifiedNote(res.unverified));
14951
14952
  if (res.nextCursor) lines.push(`Next cursor: ${res.nextCursor}`);
14952
14953
  console.log(lines.join("\n"));
14953
14954
  }
14955
+ function unverifiedNote(unverified) {
14956
+ return `${unverified} link${unverified === 1 ? "" : "s"} could not be checked (the crawler never got a response). Not counted as broken; re-run to retry.`;
14957
+ }
14954
14958
  function formatDeadLinkState(value) {
14955
14959
  if (value.state === "disabled") return "disabled (opt in with --check-dead-links)";
14956
14960
  if (value.state === "unavailable") return "unavailable";
14957
- return `${value.found ?? 0} found / ${value.checked ?? 0} checked (${value.state})`;
14961
+ const unverified = value.unverified ?? 0;
14962
+ const suffix = unverified > 0 ? `, ${unverified} unchecked` : "";
14963
+ return `${value.found ?? 0} found / ${value.checked ?? 0} checked${suffix} (${value.state})`;
14958
14964
  }
14959
14965
  async function technicalAeoRun(project, opts) {
14960
14966
  const client = getClient27();
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-IUWFALUQ.js";
3
+ } from "./chunk-NFG2BPQP.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-QFMHIUV7.js";
7
- import "./chunk-KU45I42F.js";
8
- import "./chunk-GU7T2JUB.js";
6
+ } from "./chunk-P5LR3NDQ.js";
7
+ import "./chunk-7M3QVRJD.js";
8
+ import "./chunk-TP4KXU3E.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-KU45I42F.js";
4
- import "./chunk-GU7T2JUB.js";
3
+ } from "./chunk-7M3QVRJD.js";
4
+ import "./chunk-TP4KXU3E.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-QFMHIUV7.js";
6
+ } from "./chunk-P5LR3NDQ.js";
7
7
  import {
8
8
  isReadOnlyKey
9
- } from "./chunk-GU7T2JUB.js";
9
+ } from "./chunk-TP4KXU3E.js";
10
10
 
11
11
  // src/mcp/cli.ts
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.166.0",
3
+ "version": "4.167.1",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -70,26 +70,26 @@
70
70
  "@types/node-cron": "^3.0.11",
71
71
  "tsup": "^8.5.1",
72
72
  "tsx": "^4.19.0",
73
+ "@ainyc/canonry-api-client": "0.0.0",
73
74
  "@ainyc/canonry-api-routes": "0.0.0",
74
75
  "@ainyc/canonry-config": "0.0.0",
75
76
  "@ainyc/canonry-contracts": "0.0.0",
76
- "@ainyc/canonry-api-client": "0.0.0",
77
77
  "@ainyc/canonry-db": "0.0.0",
78
- "@ainyc/canonry-integration-bing": "0.0.0",
79
78
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
80
79
  "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
81
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
82
- "@ainyc/canonry-integration-google": "0.0.0",
83
- "@ainyc/canonry-integration-google-business-profile": "0.0.0",
84
80
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
81
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
85
82
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
83
+ "@ainyc/canonry-integration-bing": "0.0.0",
84
+ "@ainyc/canonry-integration-google": "0.0.0",
86
85
  "@ainyc/canonry-integration-google-places": "0.0.0",
86
+ "@ainyc/canonry-integration-traffic": "0.0.0",
87
87
  "@ainyc/canonry-integration-wordpress": "0.0.0",
88
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
88
89
  "@ainyc/canonry-intelligence": "0.0.0",
89
- "@ainyc/canonry-integration-traffic": "0.0.0",
90
90
  "@ainyc/canonry-provider-cdp": "0.0.0",
91
- "@ainyc/canonry-provider-gemini": "0.0.0",
92
91
  "@ainyc/canonry-provider-claude": "0.0.0",
92
+ "@ainyc/canonry-provider-gemini": "0.0.0",
93
93
  "@ainyc/canonry-provider-local": "0.0.0",
94
94
  "@ainyc/canonry-provider-openai": "0.0.0",
95
95
  "@ainyc/canonry-provider-perplexity": "0.0.0"