@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.
- package/README.md +31 -19
- package/assets/assets/{AuditHistoryPanel-CZByTZjH.js → AuditHistoryPanel-Dvz6QFtW.js} +1 -1
- package/assets/assets/{BacklinksPage-DPsSKu1M.js → BacklinksPage-BHOtOIr5.js} +1 -1
- package/assets/assets/{HistoryPage-9acsanzg.js → HistoryPage-Bc5Nx6dX.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-gIOHHJ4x.js → MeasurementPropertyPage-D4FIFiWv.js} +1 -1
- package/assets/assets/{ProjectPage-CTic0OFR.js → ProjectPage-CT5ZD7_A.js} +1 -1
- package/assets/assets/{RunRow-cuE1cBCg.js → RunRow-CB2oG45_.js} +1 -1
- package/assets/assets/{RunsPage-Bt6T2Kz2.js → RunsPage-OLbocIOY.js} +1 -1
- package/assets/assets/{SettingsPage-ChyewFuC.js → SettingsPage-DVdPBrap.js} +1 -1
- package/assets/assets/{SiteHealthSection-BYM32RDv.js → SiteHealthSection-B1bjMCqY.js} +3 -3
- package/assets/assets/{TrafficPage-DMpuFJua.js → TrafficPage-BUWXjHhw.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-D4G26uyv.js → TrafficSourceDetailPage-DwoFzTu7.js} +1 -1
- package/assets/assets/{extract-error-message-D7fD7zwP.js → extract-error-message-CNH2-nPH.js} +1 -1
- package/assets/assets/{index-BliSumN-.js → index-DdXb59Rg.js} +3 -3
- package/assets/assets/{react-sigma_core.esm.min-CmW2JaYh.js → react-sigma_core.esm.min-DLRh4X_G.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-KU45I42F.js → chunk-7M3QVRJD.js} +80 -4
- package/dist/{chunk-IUWFALUQ.js → chunk-NFG2BPQP.js} +25 -6
- package/dist/{chunk-QFMHIUV7.js → chunk-P5LR3NDQ.js} +1 -1
- package/dist/{chunk-GU7T2JUB.js → chunk-TP4KXU3E.js} +4 -2
- package/dist/cli.js +11 -5
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-KRQNQNEB.js → intelligence-service-ULDOVV5Y.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -8
package/assets/assets/{react-sigma_core.esm.min-CmW2JaYh.js → react-sigma_core.esm.min-DLRh4X_G.js}
RENAMED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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-
|
|
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);
|
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
3
|
+
} from "./chunk-NFG2BPQP.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-P5LR3NDQ.js";
|
|
7
|
+
import "./chunk-7M3QVRJD.js";
|
|
8
|
+
import "./chunk-TP4KXU3E.js";
|
|
9
9
|
export {
|
|
10
10
|
createServer,
|
|
11
11
|
loadConfig
|
package/dist/mcp.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
PACKAGE_VERSION,
|
|
4
4
|
canonryMcpTools,
|
|
5
5
|
createApiClient
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-P5LR3NDQ.js";
|
|
7
7
|
import {
|
|
8
8
|
isReadOnlyKey
|
|
9
|
-
} from "./chunk-
|
|
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.
|
|
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"
|