@canonry/canonry 4.160.0 → 4.162.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 (28) hide show
  1. package/assets/assets/{AuditHistoryPanel-AtgoG2AH.js → AuditHistoryPanel-BTtqk1mK.js} +1 -1
  2. package/assets/assets/{BacklinksPage-DrOSF7Mu.js → BacklinksPage-wqRYkJFG.js} +1 -1
  3. package/assets/assets/ChartPrimitives-BoYe2M-i.js +1 -0
  4. package/assets/assets/{HistoryPage-CGQXleJc.js → HistoryPage-Dj1FU5WV.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-C7D2OgIQ.js → MeasurementPropertyPage-CPCYzznG.js} +1 -1
  6. package/assets/assets/ProjectPage-7vg1El9D.js +9 -0
  7. package/assets/assets/{RunRow-BdzKKODo.js → RunRow-CTgtgq5x.js} +1 -1
  8. package/assets/assets/{RunsPage-BU96L07S.js → RunsPage-qS7etC5f.js} +1 -1
  9. package/assets/assets/{SettingsPage-LEca0kDH.js → SettingsPage-BlUWPiA7.js} +1 -1
  10. package/assets/assets/{SiteHealthSection-CKM4DUfE.js → SiteHealthSection-tUFchSy-.js} +3 -3
  11. package/assets/assets/{TrafficPage-tU9wl1F_.js → TrafficPage-BSWUsXM1.js} +1 -1
  12. package/assets/assets/{TrafficSourceDetailPage-DZTpnU9f.js → TrafficSourceDetailPage-DOTtU4SK.js} +1 -1
  13. package/assets/assets/{extract-error-message-BAyN-ftk.js → extract-error-message-DOWCsWL0.js} +1 -1
  14. package/assets/assets/{index-Wqv7kvs-.js → index-BYGqta_M.js} +20 -20
  15. package/assets/assets/{index-CACRaOmZ.css → index-CHluSPlN.css} +1 -1
  16. package/assets/assets/{react-sigma_core.esm.min-DlZOAc_w.js → react-sigma_core.esm.min-C4kgfZfm.js} +1 -1
  17. package/assets/index.html +2 -2
  18. package/dist/{chunk-HQAVEK42.js → chunk-2TLRE2L3.js} +1 -1
  19. package/dist/{chunk-X7CYY5Q7.js → chunk-C6ZZH3XI.js} +206 -38
  20. package/dist/{chunk-XGESWHFP.js → chunk-FZAAGXTK.js} +35 -18
  21. package/dist/{chunk-7M7T5QXS.js → chunk-NG7OR6GO.js} +3354 -3216
  22. package/dist/cli.js +42 -15
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-6XQMM466.js → intelligence-service-ANRFEQWX.js} +2 -2
  25. package/dist/mcp.js +2 -2
  26. package/package.json +12 -12
  27. package/assets/assets/ChartPrimitives-DXVLCdBi.js +0 -1
  28. package/assets/assets/ProjectPage-Lp1VjNvL.js +0 -12
@@ -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-CKM4DUfE.js";import"./index-Wqv7kvs-.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-DXVLCdBi.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-tUFchSy-.js";import"./index-BYGqta_M.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,14 +12,14 @@
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-Wqv7kvs-.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-BYGqta_M.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">
19
19
  <link rel="modulepreload" crossorigin href="./assets/vendor-lucide-nYKv-BB7.js">
20
20
  <link rel="modulepreload" crossorigin href="./assets/vendor-markdown-Bn7fRwrx.js">
21
21
  <link rel="modulepreload" crossorigin href="./assets/vendor-yaml-GWJz5H2G.js">
22
- <link rel="stylesheet" crossorigin href="./assets/index-CACRaOmZ.css">
22
+ <link rel="stylesheet" crossorigin href="./assets/index-CHluSPlN.css">
23
23
  </head>
24
24
  <body>
25
25
  <div id="root"></div>
@@ -81,7 +81,7 @@ import {
81
81
  trafficConnectWordpressRequestSchema,
82
82
  trafficEventKindSchema,
83
83
  trafficSeriesGranularitySchema
84
- } from "./chunk-7M7T5QXS.js";
84
+ } from "./chunk-NG7OR6GO.js";
85
85
 
86
86
  // src/config.ts
87
87
  import fs from "fs";
@@ -183,6 +183,7 @@ import {
183
183
  buildHarvestAnchorTerms,
184
184
  buildMeasurementExecutionIdentity,
185
185
  buildMeasurementRunManifestV1,
186
+ calendarDateRange,
186
187
  calendarMonthBounds,
187
188
  canonicalMeasurementExecutionIdentityJson,
188
189
  canonicalMeasurementPlanJson,
@@ -324,6 +325,7 @@ import {
324
325
  keywordDtoSchema,
325
326
  keywordGenerateRequestSchema,
326
327
  latestProjectRunDtoSchema,
328
+ linearTrend,
327
329
  locationContextSchema,
328
330
  loginRequestSchema,
329
331
  mapWithConcurrency,
@@ -499,6 +501,7 @@ import {
499
501
  segmentCrawlerHits,
500
502
  serializeRunError,
501
503
  settingsDtoSchema,
504
+ shiftIsoCalendarDate,
502
505
  siteAuditLivePageHealthSchema,
503
506
  siteAuditPageFactorSchema,
504
507
  siteAuditPagesResponseSchema,
@@ -577,7 +580,7 @@ import {
577
580
  wordpressSchemaDeployResultDtoSchema,
578
581
  wordpressSchemaStatusResultDtoSchema,
579
582
  wordpressStatusDtoSchema
580
- } from "./chunk-7M7T5QXS.js";
583
+ } from "./chunk-NG7OR6GO.js";
581
584
 
582
585
  // src/intelligence-service.ts
583
586
  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";
@@ -641,6 +644,7 @@ __export(schema_exports, {
641
644
  googleConnections: () => googleConnections,
642
645
  gscCoverageSnapshots: () => gscCoverageSnapshots,
643
646
  gscDailyTotals: () => gscDailyTotals,
647
+ gscDataWatermarks: () => gscDataWatermarks,
644
648
  gscQueryDailyTotals: () => gscQueryDailyTotals,
645
649
  gscSearchData: () => gscSearchData,
646
650
  gscUrlInspections: () => gscUrlInspections,
@@ -1141,6 +1145,12 @@ var gscSearchData = sqliteTable("gsc_search_data", {
1141
1145
  index("idx_gsc_search_query").on(table.query),
1142
1146
  index("idx_gsc_search_run").on(table.syncRunId)
1143
1147
  ]);
1148
+ var gscDataWatermarks = sqliteTable("gsc_data_watermarks", {
1149
+ projectId: text("project_id").primaryKey().references(() => projects.id, { onDelete: "cascade" }),
1150
+ dataThroughDate: text("data_through_date").notNull(),
1151
+ syncedThroughDate: text("synced_through_date"),
1152
+ updatedAt: text("updated_at").notNull()
1153
+ });
1144
1154
  var gscDailyTotals = sqliteTable("gsc_daily_totals", {
1145
1155
  id: text("id").primaryKey(),
1146
1156
  projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
@@ -6177,6 +6187,25 @@ var MIGRATION_VERSIONS = [
6177
6187
  `ALTER TABLE traffic_sources ADD COLUMN queue_backlog_count INTEGER`,
6178
6188
  `ALTER TABLE traffic_sources ADD COLUMN queue_backlog_observed_at TEXT`
6179
6189
  ]
6190
+ },
6191
+ {
6192
+ version: 137,
6193
+ name: "gsc-data-watermark",
6194
+ // The furthest GSC reporting date this project has EVER observed.
6195
+ //
6196
+ // `MAX(date)` over the stored rows is not a frontier: Search Analytics
6197
+ // returns no row for a day with no data, so a quiet tail makes the observed
6198
+ // max walk BACKWARD and drags every anchored window back with it. This
6199
+ // column is monotonic — a sync may only advance it — so a zero-traffic
6200
+ // stretch can never move the frontier the wrong way.
6201
+ statements: [
6202
+ `CREATE TABLE IF NOT EXISTS gsc_data_watermarks (
6203
+ project_id TEXT PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,
6204
+ data_through_date TEXT NOT NULL,
6205
+ synced_through_date TEXT,
6206
+ updated_at TEXT NOT NULL
6207
+ )`
6208
+ ]
6180
6209
  }
6181
6210
  ];
6182
6211
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -12194,13 +12223,13 @@ function parseRunTriggerRequest(value) {
12194
12223
  }))
12195
12224
  });
12196
12225
  }
12197
- function parseListLimit(raw, defaultValue, max) {
12226
+ function parseListLimit(raw, defaultValue, max2) {
12198
12227
  if (raw === void 0) return defaultValue;
12199
12228
  const parsed = Number(raw);
12200
12229
  if (!Number.isFinite(parsed) || parsed < 1 || !Number.isInteger(parsed)) {
12201
12230
  throw validationError('"limit" must be a positive integer');
12202
12231
  }
12203
- return Math.min(parsed, max);
12232
+ return Math.min(parsed, max2);
12204
12233
  }
12205
12234
  function parseListKind(raw) {
12206
12235
  if (raw === void 0 || raw === "") return null;
@@ -18576,7 +18605,7 @@ function validateCron(expr) {
18576
18605
  }
18577
18606
  return true;
18578
18607
  }
18579
- function validateCronField(field, min, max) {
18608
+ function validateCronField(field, min, max2) {
18580
18609
  if (field === "*") return true;
18581
18610
  const segments = field.split(",");
18582
18611
  for (const segment of segments) {
@@ -18592,7 +18621,7 @@ function validateCronField(field, min, max) {
18592
18621
  if (rangeParts.length > 2) return false;
18593
18622
  for (const part of rangeParts) {
18594
18623
  const num = parseInt(part, 10);
18595
- if (isNaN(num) || num < min || num > max) return false;
18624
+ if (isNaN(num) || num < min || num > max2) return false;
18596
18625
  }
18597
18626
  }
18598
18627
  return true;
@@ -19412,16 +19441,16 @@ function addAuditHistoryFilters(filters, query) {
19412
19441
  if (query.actor) filters.push(eq22(auditLog.actor, query.actor));
19413
19442
  if (query.entityType) filters.push(eq22(auditLog.entityType, query.entityType));
19414
19443
  }
19415
- function parseBoundedInt(value, fallback, max) {
19444
+ function parseBoundedInt(value, fallback, max2) {
19416
19445
  const parsed = Number.parseInt(value ?? "", 10);
19417
19446
  if (!Number.isFinite(parsed) || parsed < 0) return fallback;
19418
- return Math.min(parsed, max);
19447
+ return Math.min(parsed, max2);
19419
19448
  }
19420
- function parseOptionalPositiveInt(value, max) {
19449
+ function parseOptionalPositiveInt(value, max2) {
19421
19450
  if (value == null) return void 0;
19422
19451
  const parsed = Number.parseInt(value, 10);
19423
19452
  if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
19424
- return Math.min(parsed, max);
19453
+ return Math.min(parsed, max2);
19425
19454
  }
19426
19455
 
19427
19456
  // ../api-routes/src/analytics.ts
@@ -21168,7 +21197,47 @@ function winnabilityClassRank(winnabilityClass) {
21168
21197
  }
21169
21198
 
21170
21199
  // ../api-routes/src/gsc-totals.ts
21171
- import { and as and21, asc as asc4, eq as eq27, sql as sql8 } from "drizzle-orm";
21200
+ import { and as and21, asc as asc4, eq as eq27, sql as sql8, max } from "drizzle-orm";
21201
+ var WINDOW_DAYS = { "7d": 7, "30d": 30, "90d": 90 };
21202
+ function resolveGscWindowRange(window, latestDataDate, today) {
21203
+ if (window === "all") {
21204
+ return {
21205
+ startDate: null,
21206
+ endDate: latestDataDate,
21207
+ latestDataDate,
21208
+ daysSinceLatestData: gscDaysSinceLatestData(latestDataDate, today)
21209
+ };
21210
+ }
21211
+ return resolveGscWindowDays(WINDOW_DAYS[window], latestDataDate, today);
21212
+ }
21213
+ function resolveGscWindowDays(days, latestDataDate, today) {
21214
+ if (latestDataDate === null) {
21215
+ return {
21216
+ startDate: shiftIsoCalendarDate(today, -days),
21217
+ endDate: null,
21218
+ latestDataDate: null,
21219
+ daysSinceLatestData: null
21220
+ };
21221
+ }
21222
+ return {
21223
+ startDate: shiftIsoCalendarDate(latestDataDate, -(days - 1)),
21224
+ endDate: latestDataDate,
21225
+ latestDataDate,
21226
+ daysSinceLatestData: gscDaysSinceLatestData(latestDataDate, today)
21227
+ };
21228
+ }
21229
+ function gscDaysSinceLatestData(latestDataDate, today) {
21230
+ if (latestDataDate === null) return null;
21231
+ return Math.max(0, Math.round(
21232
+ (Date.parse(`${today}T00:00:00Z`) - Date.parse(`${latestDataDate}T00:00:00Z`)) / 864e5
21233
+ ));
21234
+ }
21235
+ function readLatestGscDataDate(db, projectId) {
21236
+ const watermark = db.select({ through: gscDataWatermarks.dataThroughDate }).from(gscDataWatermarks).where(eq27(gscDataWatermarks.projectId, projectId)).get()?.through ?? null;
21237
+ const property = db.select({ latest: max(gscDailyTotals.date) }).from(gscDailyTotals).where(eq27(gscDailyTotals.projectId, projectId)).get()?.latest ?? null;
21238
+ const dimensioned = db.select({ latest: max(gscSearchData.date) }).from(gscSearchData).where(eq27(gscSearchData.projectId, projectId)).get()?.latest ?? null;
21239
+ return [watermark, property, dimensioned].filter((d) => d !== null).reduce((maxDate, d) => maxDate === null || d > maxDate ? d : maxDate, null);
21240
+ }
21172
21241
  function readGscDailyTotals(db, projectId, startDate, endDate) {
21173
21242
  const rows = db.select({
21174
21243
  date: gscDailyTotals.date,
@@ -22427,7 +22496,7 @@ function renderWhatsChanged(report, audience) {
22427
22496
  }
22428
22497
  function renderProviderBars(rates) {
22429
22498
  if (rates.length === 0) return "";
22430
- const max = Math.max(...rates.map((r) => r.citationRate), 100);
22499
+ const max2 = Math.max(...rates.map((r) => r.citationRate), 100);
22431
22500
  const width = 600;
22432
22501
  const height = Math.max(rates.length * 32 + 24, 80);
22433
22502
  const labelWidth = 80;
@@ -22436,7 +22505,7 @@ function renderProviderBars(rates) {
22436
22505
  const bars = rates.map((r, i) => {
22437
22506
  const y = i * 32 + padding;
22438
22507
  const barHeight = 22;
22439
- const w = max > 0 ? r.citationRate / max * barWidth : 0;
22508
+ const w = max2 > 0 ? r.citationRate / max2 * barWidth : 0;
22440
22509
  const color = COLORS.series[i % COLORS.series.length];
22441
22510
  return `
22442
22511
  <text x="${labelWidth - 8}" y="${y + 16}" fill="${COLORS.textMuted}" font-size="11" text-anchor="end">${escapeHtml(r.provider)}</text>
@@ -22486,14 +22555,14 @@ function renderCitationScorecard(report) {
22486
22555
  }
22487
22556
  function renderLandscapeBars(data, heading, ariaLabel) {
22488
22557
  if (data.length <= 1) return "";
22489
- const max = Math.max(...data.map((d) => d.count), 1);
22558
+ const max2 = Math.max(...data.map((d) => d.count), 1);
22490
22559
  const width = 600;
22491
22560
  const height = data.length * 28 + 16;
22492
22561
  const labelWidth = 160;
22493
22562
  const bars = data.map((d, i) => {
22494
22563
  const y = i * 28 + 8;
22495
22564
  const barHeight = 18;
22496
- const w = d.count / max * (width - labelWidth - 60);
22565
+ const w = d.count / max2 * (width - labelWidth - 60);
22497
22566
  const color = d.isProject ? COLORS.accent : COLORS.series[(i + 1) % COLORS.series.length];
22498
22567
  return `
22499
22568
  <text x="${labelWidth - 8}" y="${y + 13}" fill="${COLORS.textMuted}" font-size="11" text-anchor="end">${escapeHtml(d.label)}</text>
@@ -22584,9 +22653,9 @@ function renderCategoryBars(buckets) {
22584
22653
  if (buckets.length === 0) return "";
22585
22654
  const total = buckets.reduce((s, b) => s + b.count, 0);
22586
22655
  if (total === 0) return "";
22587
- const max = Math.max(...buckets.map((b) => b.count), 1);
22656
+ const max2 = Math.max(...buckets.map((b) => b.count), 1);
22588
22657
  const rows = buckets.map((b) => {
22589
- const pct = b.count / max * 100;
22658
+ const pct = b.count / max2 * 100;
22590
22659
  const tone = SOURCE_CATEGORY_TONE[b.category] ?? "neutral";
22591
22660
  const color = tone === "negative" ? COLORS.negative : tone === "caution" ? COLORS.caution : COLORS.accent;
22592
22661
  return `
@@ -22662,11 +22731,11 @@ function renderLineChart(points, color, title, height = 200) {
22662
22731
  const padY = 24;
22663
22732
  const usableW = width - padX * 2;
22664
22733
  const usableH = height - padY * 2;
22665
- const max = Math.max(...points.map((p) => p.y), 1);
22734
+ const max2 = Math.max(...points.map((p) => p.y), 1);
22666
22735
  const stepX = points.length > 1 ? usableW / (points.length - 1) : 0;
22667
22736
  const xy = points.map((p, i) => ({
22668
22737
  x: padX + i * stepX,
22669
- y: padY + usableH - p.y / max * usableH,
22738
+ y: padY + usableH - p.y / max2 * usableH,
22670
22739
  raw: p
22671
22740
  }));
22672
22741
  const path8 = xy.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ");
@@ -22679,7 +22748,7 @@ function renderLineChart(points, color, title, height = 200) {
22679
22748
  <h3>${escapeHtml(title)}</h3>
22680
22749
  <svg viewBox="0 0 ${width} ${height}" width="100%" preserveAspectRatio="xMinYMin meet" role="img" aria-label="${escapeHtml(title)} line chart">
22681
22750
  <line x1="${padX}" y1="${padY + usableH}" x2="${padX + usableW}" y2="${padY + usableH}" stroke="${COLORS.border}" stroke-width="1" />
22682
- <text x="${padX - 6}" y="${(padY + 4).toFixed(1)}" fill="${COLORS.textFaint}" font-size="9" text-anchor="end">${formatNumber(max)}</text>
22751
+ <text x="${padX - 6}" y="${(padY + 4).toFixed(1)}" fill="${COLORS.textFaint}" font-size="9" text-anchor="end">${formatNumber(max2)}</text>
22683
22752
  <text x="${padX - 6}" y="${(padY + usableH).toFixed(1)}" fill="${COLORS.textFaint}" font-size="9" text-anchor="end">0</text>
22684
22753
  <path d="${path8}" stroke="${color}" stroke-width="2" fill="none" />
22685
22754
  ${dots}
@@ -27371,7 +27440,12 @@ function summarizeTransitionsFromSnapshots(latest, previous, since) {
27371
27440
  return { since, gained, lost, emerging };
27372
27441
  }
27373
27442
  function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
27374
- const cutoff = new Date(Date.now() - 28 * 24 * 60 * 60 * 1e3).toISOString().slice(0, 10);
27443
+ const suggestionWindow = resolveGscWindowDays(
27444
+ 28,
27445
+ readLatestGscDataDate(app.db, projectId),
27446
+ (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
27447
+ );
27448
+ const cutoff = suggestionWindow.startDate ?? "";
27375
27449
  const dimensionedRows = app.db.select({
27376
27450
  date: gscSearchData.date,
27377
27451
  query: gscSearchData.query,
@@ -27384,11 +27458,14 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
27384
27458
  }).from(gscSearchData).where(and28(
27385
27459
  eq34(gscSearchData.projectId, projectId),
27386
27460
  sql12`${gscSearchData.date} >= ${cutoff}`,
27461
+ // Same upper bound as the accurate source below. The merge picks a source
27462
+ // PER DAY, so an asymmetric range would let a dimensioned-only day past
27463
+ // the window's end into a basket the other source cannot balance.
27464
+ sql12`${gscSearchData.date} <= ${suggestionWindow.endDate ?? "9999-12-31"}`,
27387
27465
  sql12`${gscSearchData.impressions} > 0`
27388
27466
  )).groupBy(gscSearchData.date, gscSearchData.query).all();
27389
- const todayIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
27390
27467
  const merged = mergeGscQueryTotalsWithFallback(
27391
- readGscQueryDailyRows(app.db, projectId, cutoff, todayIso),
27468
+ readGscQueryDailyRows(app.db, projectId, cutoff, suggestionWindow.endDate ?? "9999-12-31"),
27392
27469
  dimensionedRows.map((r) => ({
27393
27470
  date: r.date,
27394
27471
  query: r.query,
@@ -30850,6 +30927,12 @@ var routeCatalog = [
30850
30927
  nameParameter,
30851
30928
  { name: "startDate", in: "query", description: "Filter by start date.", schema: stringSchema },
30852
30929
  { name: "endDate", in: "query", description: "Filter by end date.", schema: stringSchema },
30930
+ {
30931
+ name: "days",
30932
+ in: "query",
30933
+ description: "Relative span in days, resolved server-side against the last published GSC date. Prefer this over client-computed start/end dates: those bypass the published-day anchoring and are pinned to the caller's clock rather than Google's Pacific calendar.",
30934
+ schema: stringSchema
30935
+ },
30853
30936
  { name: "query", in: "query", description: "Filter by search query.", schema: stringSchema },
30854
30937
  { name: "page", in: "query", description: "Filter by page URL.", schema: stringSchema },
30855
30938
  {
@@ -35015,8 +35098,8 @@ var SEVERITY_ICONS = {
35015
35098
  [AlertSeverities.success]: "\u2705",
35016
35099
  [AlertSeverities.info]: "\u{1F4CA}"
35017
35100
  };
35018
- function clamp(value, max) {
35019
- return value.length <= max ? value : `${value.slice(0, max - 1)}\u2026`;
35101
+ function clamp(value, max2) {
35102
+ return value.length <= max2 ? value : `${value.slice(0, max2 - 1)}\u2026`;
35020
35103
  }
35021
35104
  function absoluteLink(url) {
35022
35105
  if (!url) return void 0;
@@ -35405,6 +35488,7 @@ var INDEXING_SCOPE = "https://www.googleapis.com/auth/indexing";
35405
35488
  var GSC_API_BASE = "https://www.googleapis.com/webmasters/v3";
35406
35489
  var URL_INSPECTION_API = "https://searchconsole.googleapis.com/v1/urlInspection/index:inspect";
35407
35490
  var GSC_MAX_ROWS_PER_REQUEST = 25e3;
35491
+ var GSC_REPORTING_TIME_ZONE = "America/Los_Angeles";
35408
35492
  var GSC_DATA_LAG_DAYS = 3;
35409
35493
  var INDEXING_API_BASE = "https://indexing.googleapis.com/v3";
35410
35494
  var INDEXING_API_DAILY_LIMIT = 200;
@@ -36448,11 +36532,11 @@ async function fetchDailyTotals(accessToken, propertyId, days) {
36448
36532
  ga4Log("info", "fetch-daily-totals.done", { propertyId, days: syncDays, rows: rows.length });
36449
36533
  return rows;
36450
36534
  }
36451
- var WINDOW_DAYS = { "7d": 7, "30d": 30, "90d": 90 };
36535
+ var WINDOW_DAYS2 = { "7d": 7, "30d": 30, "90d": 90 };
36452
36536
  async function fetchWindowSummary(accessToken, propertyId, windowKey) {
36453
36537
  validateAccessToken2(accessToken);
36454
36538
  validatePropertyId(propertyId);
36455
- const days = WINDOW_DAYS[windowKey];
36539
+ const days = WINDOW_DAYS2[windowKey];
36456
36540
  if (!days) {
36457
36541
  throw new GA4ApiError(`Unsupported windowKey "${windowKey}" \u2014 must be 7d, 30d, or 90d`, 400);
36458
36542
  }
@@ -37027,6 +37111,26 @@ function stableStringify3(value) {
37027
37111
  }
37028
37112
 
37029
37113
  // ../api-routes/src/google.ts
37114
+ function resolveReportedWindow(resolved, startDate, endDate) {
37115
+ const start = startDate ?? resolved.startDate;
37116
+ const end = endDate ?? resolved.endDate;
37117
+ if (start === null || end === null || start <= end) {
37118
+ return { ...resolved, startDate: start, endDate: end };
37119
+ }
37120
+ return {
37121
+ ...resolved,
37122
+ startDate: startDate ? start : null,
37123
+ endDate: endDate ? end : null
37124
+ };
37125
+ }
37126
+ function assertForwardRange(startDate, endDate) {
37127
+ if (startDate && endDate && startDate > endDate) {
37128
+ throw validationError(`startDate "${startDate}" is after endDate "${endDate}".`);
37129
+ }
37130
+ }
37131
+ function gscToday() {
37132
+ return formatIsoDateInTimeZone((/* @__PURE__ */ new Date()).toISOString(), GSC_REPORTING_TIME_ZONE);
37133
+ }
37030
37134
  var GOOGLE_OAUTH_COMPLETE_MESSAGE = "canonry:google-oauth-complete";
37031
37135
  function googleOAuthSuccessHtml(type) {
37032
37136
  const message = JSON.stringify({
@@ -37457,11 +37561,19 @@ async function googleRoutes(app, opts) {
37457
37561
  );
37458
37562
  }
37459
37563
  const orderBy = parsedOrderBy.data;
37460
- const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
37564
+ assertForwardRange(startDate, endDate);
37565
+ const daysParam = request.query.days === void 0 ? null : Number(request.query.days);
37566
+ if (daysParam !== null && (!Number.isInteger(daysParam) || daysParam < 1)) {
37567
+ throw validationError('"days" must be a positive integer');
37568
+ }
37569
+ const latestDataDate = readLatestGscDataDate(app.db, project.id);
37570
+ const resolvedWindow = daysParam === null ? resolveGscWindowRange(parseWindow(request.query.window), latestDataDate, gscToday()) : resolveGscWindowDays(daysParam, latestDataDate, gscToday());
37571
+ const cutoffDate = startDate ? null : resolvedWindow.startDate;
37572
+ const effectiveEndDate = endDate ?? (daysParam === null ? void 0 : resolvedWindow.endDate ?? void 0);
37461
37573
  const conditions = [eq39(gscSearchData.projectId, project.id)];
37462
37574
  if (startDate) conditions.push(sql13`${gscSearchData.date} >= ${startDate}`);
37463
37575
  else if (cutoffDate) conditions.push(sql13`${gscSearchData.date} >= ${cutoffDate}`);
37464
- if (endDate) conditions.push(sql13`${gscSearchData.date} <= ${endDate}`);
37576
+ if (effectiveEndDate) conditions.push(sql13`${gscSearchData.date} <= ${effectiveEndDate}`);
37465
37577
  if (query) conditions.push(sql13`${gscSearchData.query} LIKE ${"%" + escapeLikePattern(query) + "%"} ESCAPE '\\'`);
37466
37578
  if (page) conditions.push(sql13`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page) + "%"} ESCAPE '\\'`);
37467
37579
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
@@ -37498,9 +37610,15 @@ async function googleRoutes(app, opts) {
37498
37610
  app.get("/projects/:name/google/gsc/performance/daily", async (request) => {
37499
37611
  const project = resolveProject(app.db, request.params.name);
37500
37612
  const { startDate, endDate } = request.query;
37501
- const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
37613
+ assertForwardRange(startDate, endDate);
37614
+ const resolvedWindow = resolveGscWindowRange(
37615
+ parseWindow(request.query.window),
37616
+ readLatestGscDataDate(app.db, project.id),
37617
+ gscToday()
37618
+ );
37619
+ const cutoffDate = startDate ? null : resolvedWindow.startDate;
37502
37620
  const windowStart = startDate ?? cutoffDate ?? "";
37503
- const windowEnd = endDate ?? "9999-12-31";
37621
+ const windowEnd = endDate ?? resolvedWindow.endDate ?? "9999-12-31";
37504
37622
  const dailyTotals = readGscDailyTotals(app.db, project.id, windowStart, windowEnd);
37505
37623
  const conditions = [eq39(gscSearchData.projectId, project.id)];
37506
37624
  if (startDate) conditions.push(sql13`${gscSearchData.date} >= ${startDate}`);
@@ -37511,6 +37629,7 @@ async function googleRoutes(app, opts) {
37511
37629
  clicks: sql13`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
37512
37630
  impressions: sql13`COALESCE(SUM(${gscSearchData.impressions}), 0)`
37513
37631
  }).from(gscSearchData).where(and30(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
37632
+ const propertyDates = new Set(dailyTotals.map((d) => d.date));
37514
37633
  const daily = mergeGscDailyTotalsWithFallback(
37515
37634
  dailyTotals,
37516
37635
  dimensionedRows.map((r) => ({
@@ -37523,24 +37642,71 @@ async function googleRoutes(app, opts) {
37523
37642
  date: d.date,
37524
37643
  clicks: d.clicks,
37525
37644
  impressions: d.impressions,
37526
- ctr: d.impressions > 0 ? d.clicks / d.impressions : 0
37645
+ ctr: d.impressions > 0 ? d.clicks / d.impressions : 0,
37646
+ position: propertyDates.has(d.date) ? d.position : null
37527
37647
  }));
37528
37648
  const totalClicks = daily.reduce((sum, d) => sum + d.clicks, 0);
37529
37649
  const totalImpressions = daily.reduce((sum, d) => sum + d.impressions, 0);
37650
+ let positionWeight = 0;
37651
+ let positionWeighted = 0;
37652
+ for (const d of daily) {
37653
+ if (d.position === null || d.impressions <= 0) continue;
37654
+ positionWeight += d.impressions;
37655
+ positionWeighted += d.position * d.impressions;
37656
+ }
37657
+ const byDate = new Map(daily.map((d) => [d.date, d]));
37658
+ const denseDates = daily.length === 0 ? [] : calendarDateRange(daily[0].date, daily[daily.length - 1].date);
37659
+ const densify = (pick) => denseDates.map((date) => {
37660
+ const row = byDate.get(date);
37661
+ return row ? pick(row) : null;
37662
+ });
37530
37663
  return {
37531
37664
  totals: {
37532
37665
  clicks: totalClicks,
37533
37666
  impressions: totalImpressions,
37534
37667
  ctr: totalImpressions > 0 ? totalClicks / totalImpressions : 0,
37668
+ position: positionWeight > 0 ? positionWeighted / positionWeight : null,
37669
+ /**
37670
+ * How many days actually carried a property-level position. Below
37671
+ * `days`, the position figure describes a SUBSET of the window, and a
37672
+ * surface must say so rather than present it as the window's average.
37673
+ */
37674
+ positionDays: daily.filter((d) => d.position !== null).length,
37535
37675
  days: daily.length
37536
37676
  },
37537
- daily
37677
+ daily,
37678
+ // The period actually returned. An explicit start/end wins over the
37679
+ // label, so echo what was used rather than what the window would have
37680
+ // chosen — a caller must be able to label the data it got.
37681
+ //
37682
+ // Mixing one explicit bound with the computed opposite one can invert the
37683
+ // range (an explicit `startDate` of 2030-01-01 against a computed
37684
+ // `endDate` of 2026-01-06), which would describe a period that cannot
37685
+ // contain the rows beside it. When only one side is given, the other is
37686
+ // dropped rather than reported reversed: an absent bound is honest about
37687
+ // being unspecified, a reversed pair is not.
37688
+ window: resolveReportedWindow(resolvedWindow, startDate, endDate),
37689
+ // Fitted server-side so the dashboard, the CLI, and the report all draw
37690
+ // the SAME line (UI/CLI parity — a chart-only regression is invisible to
37691
+ // an agent), and fitted over the CALENDAR, not over the rows.
37692
+ trends: {
37693
+ clicks: linearTrend(densify((d) => d.clicks)),
37694
+ impressions: linearTrend(densify((d) => d.impressions)),
37695
+ ctr: linearTrend(densify((d) => d.ctr)),
37696
+ position: linearTrend(densify((d) => d.position))
37697
+ }
37538
37698
  };
37539
37699
  });
37540
37700
  app.get("/projects/:name/google/gsc/top-pages", async (request) => {
37541
37701
  const project = resolveProject(app.db, request.params.name);
37542
37702
  const { startDate, endDate, limit } = request.query;
37543
- const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
37703
+ assertForwardRange(startDate, endDate);
37704
+ const resolvedWindow = resolveGscWindowRange(
37705
+ parseWindow(request.query.window),
37706
+ readLatestGscDataDate(app.db, project.id),
37707
+ gscToday()
37708
+ );
37709
+ const cutoffDate = startDate ? null : resolvedWindow.startDate;
37544
37710
  const conditions = [eq39(gscSearchData.projectId, project.id)];
37545
37711
  if (startDate) conditions.push(sql13`${gscSearchData.date} >= ${startDate}`);
37546
37712
  else if (cutoffDate) conditions.push(sql13`${gscSearchData.date} >= ${cutoffDate}`);
@@ -37552,7 +37718,7 @@ async function googleRoutes(app, opts) {
37552
37718
  impressions: sql13`COALESCE(SUM(${gscSearchData.impressions}), 0)`
37553
37719
  }).from(gscSearchData).where(and30(...conditions)).groupBy(gscSearchData.page).orderBy(desc17(sql13`SUM(${gscSearchData.clicks})`), desc17(sql13`SUM(${gscSearchData.impressions})`)).limit(limitVal).all();
37554
37720
  const windowStart = startDate ?? cutoffDate ?? "";
37555
- const windowEnd = endDate ?? "9999-12-31";
37721
+ const windowEnd = endDate ?? resolvedWindow.endDate ?? "9999-12-31";
37556
37722
  const dailyTotals = readGscDailyTotals(app.db, project.id, windowStart, windowEnd);
37557
37723
  const totalClicks = dailyTotals.reduce((sum, d) => sum + d.clicks, 0);
37558
37724
  const totalImpressions = dailyTotals.reduce((sum, d) => sum + d.impressions, 0);
@@ -58838,13 +59004,13 @@ function emptyScore(projectName) {
58838
59004
  prioritizedFixes: []
58839
59005
  };
58840
59006
  }
58841
- function parsePositiveInt(value, fallback, max) {
59007
+ function parsePositiveInt(value, fallback, max2) {
58842
59008
  const n = typeof value === "string" ? Number.parseInt(value, 10) : typeof value === "number" ? value : NaN;
58843
59009
  if (!Number.isFinite(n) || n < 0) return fallback;
58844
- return Math.min(max, Math.floor(n));
59010
+ return Math.min(max2, Math.floor(n));
58845
59011
  }
58846
- function parseBoundedLimit(value, fallback, max) {
58847
- const parsed = parsePositiveInt(value, fallback, max);
59012
+ function parseBoundedLimit(value, fallback, max2) {
59013
+ const parsed = parsePositiveInt(value, fallback, max2);
58848
59014
  return Math.max(1, parsed);
58849
59015
  }
58850
59016
  function parseSiteHealthState(value) {
@@ -62025,6 +62191,7 @@ export {
62025
62191
  doctorHealthState,
62026
62192
  notifications,
62027
62193
  gscSearchData,
62194
+ gscDataWatermarks,
62028
62195
  gscDailyTotals,
62029
62196
  gscQueryDailyTotals,
62030
62197
  gscUrlInspections,
@@ -62101,6 +62268,7 @@ export {
62101
62268
  renderReportHtml,
62102
62269
  toAlertView,
62103
62270
  resolveDestination,
62271
+ GSC_REPORTING_TIME_ZONE,
62104
62272
  GSC_DATA_LAG_DAYS,
62105
62273
  refreshAccessToken,
62106
62274
  fetchSearchAnalytics,