@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.
- package/assets/assets/{AuditHistoryPanel-AtgoG2AH.js → AuditHistoryPanel-BTtqk1mK.js} +1 -1
- package/assets/assets/{BacklinksPage-DrOSF7Mu.js → BacklinksPage-wqRYkJFG.js} +1 -1
- package/assets/assets/ChartPrimitives-BoYe2M-i.js +1 -0
- package/assets/assets/{HistoryPage-CGQXleJc.js → HistoryPage-Dj1FU5WV.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-C7D2OgIQ.js → MeasurementPropertyPage-CPCYzznG.js} +1 -1
- package/assets/assets/ProjectPage-7vg1El9D.js +9 -0
- package/assets/assets/{RunRow-BdzKKODo.js → RunRow-CTgtgq5x.js} +1 -1
- package/assets/assets/{RunsPage-BU96L07S.js → RunsPage-qS7etC5f.js} +1 -1
- package/assets/assets/{SettingsPage-LEca0kDH.js → SettingsPage-BlUWPiA7.js} +1 -1
- package/assets/assets/{SiteHealthSection-CKM4DUfE.js → SiteHealthSection-tUFchSy-.js} +3 -3
- package/assets/assets/{TrafficPage-tU9wl1F_.js → TrafficPage-BSWUsXM1.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-DZTpnU9f.js → TrafficSourceDetailPage-DOTtU4SK.js} +1 -1
- package/assets/assets/{extract-error-message-BAyN-ftk.js → extract-error-message-DOWCsWL0.js} +1 -1
- package/assets/assets/{index-Wqv7kvs-.js → index-BYGqta_M.js} +20 -20
- package/assets/assets/{index-CACRaOmZ.css → index-CHluSPlN.css} +1 -1
- package/assets/assets/{react-sigma_core.esm.min-DlZOAc_w.js → react-sigma_core.esm.min-C4kgfZfm.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-HQAVEK42.js → chunk-2TLRE2L3.js} +1 -1
- package/dist/{chunk-X7CYY5Q7.js → chunk-C6ZZH3XI.js} +206 -38
- package/dist/{chunk-XGESWHFP.js → chunk-FZAAGXTK.js} +35 -18
- package/dist/{chunk-7M7T5QXS.js → chunk-NG7OR6GO.js} +3354 -3216
- package/dist/cli.js +42 -15
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-6XQMM466.js → intelligence-service-ANRFEQWX.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +12 -12
- package/assets/assets/ChartPrimitives-DXVLCdBi.js +0 -1
- package/assets/assets/ProjectPage-Lp1VjNvL.js +0 -12
package/assets/assets/{react-sigma_core.esm.min-DlZOAc_w.js → react-sigma_core.esm.min-C4kgfZfm.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-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-
|
|
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-
|
|
22
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CHluSPlN.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
25
25
|
<div id="root"></div>
|
|
@@ -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-
|
|
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,
|
|
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,
|
|
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,
|
|
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 >
|
|
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,
|
|
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,
|
|
19447
|
+
return Math.min(parsed, max2);
|
|
19419
19448
|
}
|
|
19420
|
-
function parseOptionalPositiveInt(value,
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
|
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 /
|
|
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
|
|
22656
|
+
const max2 = Math.max(...buckets.map((b) => b.count), 1);
|
|
22588
22657
|
const rows = buckets.map((b) => {
|
|
22589
|
-
const pct = b.count /
|
|
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
|
|
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 /
|
|
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(
|
|
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
|
|
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,
|
|
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,
|
|
35019
|
-
return value.length <=
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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(
|
|
59010
|
+
return Math.min(max2, Math.floor(n));
|
|
58845
59011
|
}
|
|
58846
|
-
function parseBoundedLimit(value, fallback,
|
|
58847
|
-
const parsed = parsePositiveInt(value, fallback,
|
|
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,
|