@canonry/canonry 7.93.0 → 7.95.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 (142) hide show
  1. package/assets/agent-workspace/skills/aero/references/ai-traffic-tracking.md +7 -1
  2. package/assets/agent-workspace/skills/canonry/references/ai-traffic-tracking.md +7 -1
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +6 -4
  4. package/assets/assets/{AgentSetupPage-D53_2n_4.js → AgentSetupPage-BV0TvyWO.js} +4 -4
  5. package/assets/assets/{AuditHistoryPanel-BFRuRv_c.js → AuditHistoryPanel-CTVhEVjr.js} +1 -1
  6. package/assets/assets/{BacklinksPage-DpSbhjVf.js → BacklinksPage-CE1Bfpf6.js} +1 -1
  7. package/assets/assets/{HistoryPage-1z0GDe6W.js → HistoryPage-BhqA-Udl.js} +1 -1
  8. package/assets/assets/MeasurementPropertyPage-BiJ06H7w.js +4 -0
  9. package/assets/assets/ProjectPage-BqS0rfDT.js +11 -0
  10. package/assets/assets/{RunsPage-DpsqauyB.js → RunsPage-BII3_JsE.js} +1 -1
  11. package/assets/assets/{SettingsPage-ChIbt7x6.js → SettingsPage-DS4ivpsX.js} +1 -1
  12. package/assets/assets/SiteHealthSection-IywsGGIA.js +2 -0
  13. package/assets/assets/ToneBadge-CTZ_JZLD.js +60 -0
  14. package/assets/assets/{TrafficPage-yoq0n5vi.js → TrafficPage-Bg9JrnxC.js} +1 -1
  15. package/assets/assets/{TrafficSourceDetailPage-eCf7k39J.js → TrafficSourceDetailPage-BuD8BlaT.js} +1 -1
  16. package/assets/assets/{account-context-vYgW6LGe.js → account-context-ylqeghOr.js} +1 -1
  17. package/assets/assets/{agent-trust-copy-CJ7pZlY5.js → agent-trust-copy-DxpavxIA.js} +1 -1
  18. package/assets/assets/{card-CArOADSj.js → card-rZ7BA4sl.js} +1 -1
  19. package/assets/assets/{format-helpers-kCHfk00b.js → format-helpers-PdOXn5Yf.js} +1 -1
  20. package/assets/assets/index-CU7tAhKY.js +60 -0
  21. package/assets/assets/{query-client-DWFpwopf.js → query-client-Cq4kP41m.js} +1 -1
  22. package/assets/assets/react-query-DVctucqC.js +1 -0
  23. package/assets/assets/{react-sigma_core.esm.min-CaW_CBlP.js → react-sigma_core.esm.min-DYhAEr2t.js} +1 -1
  24. package/assets/assets/{server-traffic-B1Fb2oFZ.js → server-traffic-Owp6jMwT.js} +1 -1
  25. package/assets/assets/{service-Cu6wI40I.js → service-y3nQY3Ue.js} +1 -1
  26. package/assets/index.html +7 -7
  27. package/dist/{ads-X3VXFPB7.js → ads-GN7QSEUV.js} +3 -3
  28. package/dist/{agent-VVPPGM2P.js → agent-J46OIQHH.js} +7 -7
  29. package/dist/{agent-ask-FJVCKO3R.js → agent-ask-755DZ4YB.js} +3 -3
  30. package/dist/{agent-conversations-5YXYDZEK.js → agent-conversations-QO7C6F6L.js} +3 -3
  31. package/dist/{agent-journeys-DBMYKLJX.js → agent-journeys-SVI6LWKM.js} +3 -3
  32. package/dist/{agent-memory-7HOF4LNF.js → agent-memory-V3ZJZT5W.js} +3 -3
  33. package/dist/{agent-providers-AO25NSCX.js → agent-providers-QIHZ222U.js} +3 -3
  34. package/dist/{agent-transcript-IRZ2QDU7.js → agent-transcript-6TS3ZOCA.js} +3 -3
  35. package/dist/{analytics-RH5NXDQX.js → analytics-LFT5OAOR.js} +3 -3
  36. package/dist/{apply-XNTW4GYA.js → apply-3XXMVLBK.js} +3 -3
  37. package/dist/{backfill-O6BON2NA.js → backfill-F62Y47XV.js} +10 -10
  38. package/dist/{backlinks-VHK5TJU4.js → backlinks-AOA5KHHB.js} +3 -3
  39. package/dist/{bing-A4SQ5MIN.js → bing-75F4W4E2.js} +3 -3
  40. package/dist/{bootstrap-I2NLTHBM.js → bootstrap-CDGVAICZ.js} +10 -10
  41. package/dist/{cdp-LAD7DM6U.js → cdp-YBCWRWY4.js} +7 -7
  42. package/dist/{chunk-U3KBGIJ6.js → chunk-46YXUJSW.js} +2 -2
  43. package/dist/{chunk-5LLEZ6YL.js → chunk-5TPLZD4I.js} +1 -1
  44. package/dist/{chunk-4CPCYR5M.js → chunk-6RDBULDN.js} +1 -1
  45. package/dist/{chunk-ON4G7RCY.js → chunk-6S6XEEGX.js} +35 -22
  46. package/dist/{chunk-4UA2Y24K.js → chunk-73AUPWSG.js} +6 -6
  47. package/dist/{chunk-LWVLQCQH.js → chunk-7PUUMJXY.js} +2973 -2639
  48. package/dist/{chunk-QXT6QVBE.js → chunk-A6O6X6FU.js} +1 -1
  49. package/dist/{chunk-7RWVMCMK.js → chunk-BUZBBVDZ.js} +1 -1
  50. package/dist/{chunk-SOJIEKH6.js → chunk-C6UU2PDJ.js} +21 -21
  51. package/dist/{chunk-GO5OUMY5.js → chunk-CVCUADCM.js} +669 -635
  52. package/dist/{chunk-J6QOUWDQ.js → chunk-EWSVH5TU.js} +1 -1
  53. package/dist/{chunk-VYZIXIDN.js → chunk-FESPLVBO.js} +6 -6
  54. package/dist/{chunk-CWEEHODU.js → chunk-FWY5HUSB.js} +2 -2
  55. package/dist/{chunk-SNQVHD2O.js → chunk-GUVN5OSX.js} +3 -3
  56. package/dist/{chunk-YUHP5IUB.js → chunk-IATTIKXF.js} +3 -3
  57. package/dist/{chunk-HE4ZCVSF.js → chunk-J3NS6NGM.js} +146 -3
  58. package/dist/{chunk-CM3BXAMM.js → chunk-JVOOKVCW.js} +2 -2
  59. package/dist/{chunk-3SVI6OY3.js → chunk-KK7NWOGH.js} +7 -7
  60. package/dist/{chunk-67WZEUEE.js → chunk-KMT7I5KZ.js} +5 -5
  61. package/dist/{chunk-N6CIAFEO.js → chunk-L7W72K4X.js} +367 -367
  62. package/dist/{chunk-OLEMJTEJ.js → chunk-LHU3KIKG.js} +2 -2
  63. package/dist/{chunk-GS3KXIFK.js → chunk-MKLAX5KY.js} +2 -1
  64. package/dist/{chunk-VNPYFXO5.js → chunk-NJG6WFRK.js} +1 -1
  65. package/dist/{chunk-7BEQY4YS.js → chunk-NMUL63XG.js} +2 -2
  66. package/dist/{chunk-QBMMHMDZ.js → chunk-NVWTGVZ4.js} +3 -3
  67. package/dist/{chunk-WCUXKDJZ.js → chunk-PHOMW3R4.js} +4 -4
  68. package/dist/{chunk-5X6CWQ6P.js → chunk-Q6WHXY7D.js} +1 -1
  69. package/dist/{chunk-VFYZVQVX.js → chunk-RPPB4H4D.js} +1 -1
  70. package/dist/{chunk-BOUEM6D6.js → chunk-T3FYWUQL.js} +2 -2
  71. package/dist/{chunk-IFVVXGLK.js → chunk-TG5D4E2Y.js} +1 -1
  72. package/dist/{chunk-2ORBI7PS.js → chunk-TS5Q63NE.js} +3 -3
  73. package/dist/{chunk-U3DFWCTU.js → chunk-TXW2FPNJ.js} +4 -4
  74. package/dist/{chunk-UPUJSQ2U.js → chunk-VOP74COR.js} +2 -2
  75. package/dist/{chunk-2OJJPSFS.js → chunk-WK6IPTTM.js} +4 -4
  76. package/dist/{chunk-AAUJZ43Q.js → chunk-YTRXALSU.js} +1 -1
  77. package/dist/{citations-D2ZLKEAZ.js → citations-SQYZTJQ6.js} +3 -3
  78. package/dist/{cli-catalog-ZJX65B35.js → cli-catalog-N3JLEU77.js} +3 -3
  79. package/dist/{cli-commands-BSX2GGJL.js → cli-commands-QOJJMX2X.js} +7 -7
  80. package/dist/cli.js +13 -13
  81. package/dist/{competitor-GMBE6HDJ.js → competitor-C5JAHHR7.js} +3 -3
  82. package/dist/{content-V7VKNFRA.js → content-RVPODQI6.js} +3 -3
  83. package/dist/{dashboard-DOIM7F3F.js → dashboard-3NAY2SX6.js} +6 -6
  84. package/dist/{demo-TKNEM6WC.js → demo-OSYK452E.js} +1 -1
  85. package/dist/{demo-server-K6LAEKGV.js → demo-server-GO4V5UXK.js} +14 -14
  86. package/dist/{discover-TALBBFX7.js → discover-U2DNK2TO.js} +3 -3
  87. package/dist/{discover-eval-2R3F3C7O.js → discover-eval-ESLS56AH.js} +3 -3
  88. package/dist/{doctor-UW7QBZR3.js → doctor-SBF54WXE.js} +3 -3
  89. package/dist/{evidence-CMXDXINS.js → evidence-WHDCM7Z5.js} +3 -3
  90. package/dist/{export-cmd-HO6K6ZDO.js → export-cmd-7CHS6K3S.js} +3 -3
  91. package/dist/{ga-VSS5Z44P.js → ga-XRASDAVX.js} +4 -4
  92. package/dist/{gbp-RYMAOVON.js → gbp-FEOFMTBH.js} +3 -3
  93. package/dist/{google-RZQSM3VZ.js → google-MWJ4HAUB.js} +4 -4
  94. package/dist/{google-marketing-ULMGHDFF.js → google-marketing-GELTQ32D.js} +1 -1
  95. package/dist/{health-cmd-5AJLSCWH.js → health-cmd-IO7YMTRQ.js} +3 -3
  96. package/dist/{history-ZEWDWGKK.js → history-I74D3NTT.js} +3 -3
  97. package/dist/index.d.ts +261 -64
  98. package/dist/index.js +25 -25
  99. package/dist/{init-NAMFIDDT.js → init-ZLJMCJ3Q.js} +12 -12
  100. package/dist/{insights-F747HGKV.js → insights-EAMVEB5Y.js} +3 -3
  101. package/dist/intelligence-service-GSQ7YAYU.js +16 -0
  102. package/dist/{journeys-IN6PKYC6.js → journeys-E373TZZS.js} +3 -3
  103. package/dist/{keys-JPOCEVCO.js → keys-27JRRWKZ.js} +3 -3
  104. package/dist/{keyword-P6EYHLKS.js → keyword-45H2TBRG.js} +3 -3
  105. package/dist/{logs-3WC6OWDD.js → logs-K4K4OJDY.js} +3 -3
  106. package/dist/{mcp-LU6BKIGI.js → mcp-734WXNIA.js} +4 -4
  107. package/dist/mcp.js +9 -9
  108. package/dist/{measurement-plan-G4E42X4M.js → measurement-plan-XZXK3YLO.js} +5 -5
  109. package/dist/{notify-ADPO6H5M.js → notify-ZGLVTSVC.js} +3 -3
  110. package/dist/{organic-evidence-6C4ADEAQ.js → organic-evidence-CRTYFYAN.js} +3 -3
  111. package/dist/{overview-NLKLGTIL.js → overview-5URBJKI4.js} +4 -4
  112. package/dist/{project-4QVFGUPT.js → project-R5W4Y4L4.js} +3 -3
  113. package/dist/{query-6JVOPG7E.js → query-LPGNI3ZR.js} +3 -3
  114. package/dist/{research-NG2HHDTB.js → research-GTVKPIO2.js} +3 -3
  115. package/dist/{results-export-RUBBBAKB.js → results-export-UNQQHELR.js} +3 -3
  116. package/dist/{run-4ZM7F4T4.js → run-DLAFXVF4.js} +4 -4
  117. package/dist/{schedule-NYJD7QNS.js → schedule-7I5VIJFS.js} +3 -3
  118. package/dist/{search-Z5TMFDQD.js → search-MUS7FSOJ.js} +3 -3
  119. package/dist/{serve-ZHUBM33Q.js → serve-5TV26VAH.js} +26 -26
  120. package/dist/{session-VNIHR75J.js → session-FWALDL3F.js} +13 -13
  121. package/dist/{settings-2YWI2XEK.js → settings-EB433IDI.js} +3 -3
  122. package/dist/{sitemaps-PPRANFJS.js → sitemaps-5EVGGGQ5.js} +3 -3
  123. package/dist/{skills-WKAHQ42S.js → skills-2HS4MYNT.js} +2 -2
  124. package/dist/{snapshot-J2RMG6IC.js → snapshot-4DMW7AK2.js} +3 -3
  125. package/dist/{src-ZNFSBJSC.js → src-OYMKXD6L.js} +4 -2
  126. package/dist/{status-S7WMZ437.js → status-2CRLY5B6.js} +4 -4
  127. package/dist/{technical-aeo-NKCIAWHJ.js → technical-aeo-HIQJCEML.js} +3 -3
  128. package/dist/{telemetry-DT4BLD5L.js → telemetry-H3SGPKZC.js} +4 -4
  129. package/dist/{tools-22WRA2TI.js → tools-PHYD6WYF.js} +11 -11
  130. package/dist/{traffic-HTBFYDKA.js → traffic-47L2EV6Q.js} +8 -5
  131. package/dist/{trends-SHT3BBXE.js → trends-CCWE7MF6.js} +3 -3
  132. package/dist/{users-BV6QFK4Q.js → users-BGIGSKB2.js} +3 -3
  133. package/dist/{visibility-stats-TGCKVC7I.js → visibility-stats-XVF64BMX.js} +3 -3
  134. package/dist/{wordpress-LYXQ3AWR.js → wordpress-DQ6DAQPS.js} +3 -3
  135. package/package.json +1 -1
  136. package/assets/assets/MeasurementPropertyPage-Dzjx179C.js +0 -4
  137. package/assets/assets/ProjectPage-BgPs3YQG.js +0 -11
  138. package/assets/assets/SiteHealthSection-B-79VmOp.js +0 -2
  139. package/assets/assets/ToneBadge-Cx8uzsd6.js +0 -60
  140. package/assets/assets/index-9HU3ZsRh.js +0 -60
  141. package/assets/assets/react-query-DdeD3n1W.js +0 -1
  142. package/dist/intelligence-service-7FVMY2WA.js +0 -16
@@ -3704,8 +3704,9 @@ function percentOf(part, whole) {
3704
3704
  var WINDOW_SETS = {
3705
3705
  metrics: ["7d", "30d", "90d", "all"],
3706
3706
  gaMeasurementAnalysis: ["30d", "60d", "90d"],
3707
- gaOverview: ["7d", "30d", "90d"],
3708
- gaSearchLanding: ["7d", "28d", "90d"],
3707
+ gaOverview: ["7d", "30d", "90d", "180d"],
3708
+ gaTraffic: ["7d", "30d", "90d", "180d", "all"],
3709
+ gaSearchLanding: ["7d", "28d", "90d", "180d"],
3709
3710
  googleAdsMetrics: ["7d", "14d", "30d"],
3710
3711
  trafficAnalytics: ["7d", "14d", "30d", "90d"],
3711
3712
  organicEvidence: ["60d", "90d"]
@@ -11671,8 +11672,8 @@ function parseBoundaryDate(value, field) {
11671
11672
  if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== trimmed) throw invalid();
11672
11673
  return trimmed;
11673
11674
  }
11674
- function resolveDateRange(input) {
11675
- const window = parseWindow(input.window);
11675
+ function resolveDateRange(input, windows = WINDOW_SETS.metrics) {
11676
+ const window = parseWindowIn(input.window, windows, { fallback: "all" });
11676
11677
  const startDate = parseBoundaryDate(input.startDate, "startDate");
11677
11678
  const endDate = parseBoundaryDate(input.endDate, "endDate");
11678
11679
  if (startDate && endDate && startDate > endDate) {
@@ -17165,7 +17166,7 @@ var resultsClearResponseSchema = z47.object({
17165
17166
  });
17166
17167
 
17167
17168
  // ../contracts/src/ga.ts
17168
- import { z as z51 } from "zod";
17169
+ import { z as z52 } from "zod";
17169
17170
 
17170
17171
  // ../contracts/src/traffic-class.ts
17171
17172
  import { z as z48 } from "zod";
@@ -17370,29 +17371,131 @@ function buildStackedDailySeries(points, options) {
17370
17371
  return { series, otherCount: folded.length, days };
17371
17372
  }
17372
17373
 
17374
+ // ../contracts/src/statistics.ts
17375
+ import { z as z51 } from "zod";
17376
+ function round(value, dp = 4) {
17377
+ const f = 10 ** dp;
17378
+ return (Math.round(value * f) + 0) / f;
17379
+ }
17380
+ function roundSignificant(value, sig = 6) {
17381
+ if (!Number.isFinite(value) || value === 0) return 0;
17382
+ const magnitude = Math.ceil(Math.log10(Math.abs(value)));
17383
+ const factor = 10 ** (sig - magnitude);
17384
+ return (Math.round(value * factor) + 0) / factor;
17385
+ }
17386
+ function shareOf(part, total) {
17387
+ if (!Number.isFinite(part) || !Number.isFinite(total)) return null;
17388
+ if (part <= 0) return 0;
17389
+ return total > 0 ? part / total : null;
17390
+ }
17391
+ function breakdownShares(parts) {
17392
+ const total = parts.reduce((sum, part) => sum + Math.max(0, part), 0);
17393
+ return parts.map((part) => shareOf(part, total) ?? 0);
17394
+ }
17395
+ function median(values) {
17396
+ const sorted = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
17397
+ if (sorted.length === 0) return null;
17398
+ const middle = Math.floor(sorted.length / 2);
17399
+ return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
17400
+ }
17401
+ function wilsonInterval(successes, n, z97 = 1.96) {
17402
+ if (!Number.isFinite(n) || n <= 0) return null;
17403
+ const s = Math.max(0, Math.min(successes, n));
17404
+ const p = s / n;
17405
+ const z210 = z97 * z97;
17406
+ const denom = 1 + z210 / n;
17407
+ const center = (p + z210 / (2 * n)) / denom;
17408
+ const margin = z97 / denom * Math.sqrt(p * (1 - p) / n + z210 / (4 * n * n));
17409
+ return {
17410
+ low: round(Math.max(0, center - margin)),
17411
+ high: round(Math.min(1, center + margin))
17412
+ };
17413
+ }
17414
+ var linearTrendSchema = z51.object({
17415
+ /** Change in the fitted value per one-index step. */
17416
+ slope: z51.number(),
17417
+ /** Fitted value at index 0. */
17418
+ intercept: z51.number(),
17419
+ /** Coefficient of determination in [0, 1]. `1` for a series with no variance. */
17420
+ r2: z51.number(),
17421
+ /** Fitted value at the first index. */
17422
+ start: z51.number(),
17423
+ /** Fitted value at the last index. */
17424
+ end: z51.number(),
17425
+ /** Count of finite observations the fit used (not the series length). */
17426
+ n: z51.number(),
17427
+ /**
17428
+ * Index of the first observation the fit used, and of the last.
17429
+ *
17430
+ * `start` / `end` are evaluated AT these indices, not at the series' own ends.
17431
+ * A caller drawing the line must span exactly this range: extending it across
17432
+ * leading or trailing indices the metric has no data for draws a fitted claim
17433
+ * over dates that were never measured.
17434
+ */
17435
+ startIndex: z51.number(),
17436
+ endIndex: z51.number()
17437
+ });
17438
+ function linearTrend(values) {
17439
+ const points = [];
17440
+ for (const [index, value] of values.entries()) {
17441
+ if (typeof value === "number" && Number.isFinite(value)) points.push({ x: index, y: value });
17442
+ }
17443
+ const n = points.length;
17444
+ if (n < 2) return null;
17445
+ const meanX = points.reduce((sum, p) => sum + p.x, 0) / n;
17446
+ const meanY = points.reduce((sum, p) => sum + p.y, 0) / n;
17447
+ let sxx = 0;
17448
+ let sxy = 0;
17449
+ for (const p of points) {
17450
+ sxx += (p.x - meanX) ** 2;
17451
+ sxy += (p.x - meanX) * (p.y - meanY);
17452
+ }
17453
+ const slope = sxy / sxx;
17454
+ const intercept = meanY - slope * meanX;
17455
+ let ssRes = 0;
17456
+ let ssTot = 0;
17457
+ for (const p of points) {
17458
+ ssRes += (p.y - (slope * p.x + intercept)) ** 2;
17459
+ ssTot += (p.y - meanY) ** 2;
17460
+ }
17461
+ const r2 = ssTot === 0 ? 1 : Math.min(1, Math.max(0, 1 - ssRes / ssTot));
17462
+ const firstX = points[0].x;
17463
+ const lastX = points[n - 1].x;
17464
+ return {
17465
+ slope: roundSignificant(slope),
17466
+ intercept: roundSignificant(intercept),
17467
+ r2: round(r2),
17468
+ start: roundSignificant(slope * firstX + intercept),
17469
+ end: roundSignificant(slope * lastX + intercept),
17470
+ n,
17471
+ startIndex: firstX,
17472
+ endIndex: lastX
17473
+ };
17474
+ }
17475
+
17373
17476
  // ../contracts/src/ga.ts
17374
- var ga4ConnectionDtoSchema = z51.object({
17375
- id: z51.string(),
17376
- projectId: z51.string(),
17377
- propertyId: z51.string(),
17378
- clientEmail: z51.string(),
17379
- connected: z51.boolean(),
17380
- createdAt: z51.string(),
17381
- updatedAt: z51.string()
17382
- });
17383
- var ga4TrafficSnapshotDtoSchema = z51.object({
17384
- date: z51.string(),
17385
- landingPage: z51.string(),
17386
- sessions: z51.number(),
17387
- organicSessions: z51.number(),
17388
- users: z51.number()
17389
- });
17390
- var ga4SourceDimensionSchema = z51.enum(["session", "first_user", "manual_utm"]);
17391
- var ga4AiReferralDtoSchema = z51.object({
17392
- source: z51.string(),
17393
- medium: z51.string(),
17477
+ var ga4ConnectionDtoSchema = z52.object({
17478
+ id: z52.string(),
17479
+ projectId: z52.string(),
17480
+ propertyId: z52.string(),
17481
+ clientEmail: z52.string(),
17482
+ connected: z52.boolean(),
17483
+ createdAt: z52.string(),
17484
+ updatedAt: z52.string()
17485
+ });
17486
+ var ga4TrafficSnapshotDtoSchema = z52.object({
17487
+ date: z52.string(),
17488
+ landingPage: z52.string(),
17489
+ sessions: z52.number(),
17490
+ organicSessions: z52.number(),
17491
+ users: z52.number()
17492
+ });
17493
+ var ga4SourceDimensionSchema = z52.enum(["session", "first_user", "manual_utm"]);
17494
+ var ga4AiReferralDtoSchema = z52.object({
17495
+ source: z52.string(),
17496
+ medium: z52.string(),
17394
17497
  trafficClass: aiReferralTrafficClassSchema,
17395
- sessions: z51.number(),
17498
+ sessions: z52.number(),
17396
17499
  /**
17397
17500
  * @deprecated Never emitted since 4.135.0. Removed in the next major.
17398
17501
  *
@@ -17407,7 +17510,7 @@ var ga4AiReferralDtoSchema = z51.object({
17407
17510
  * number. Sessions are unaffected: GA4 attributes exactly one landing page
17408
17511
  * per session, so the landing-page rows partition the day and do sum.
17409
17512
  */
17410
- users: z51.number().optional(),
17513
+ users: z52.number().optional(),
17411
17514
  /**
17412
17515
  * The winning attribution dimension for this (source, medium) tuple — the
17413
17516
  * one with the highest session count. GA4 emits one row per dimension
@@ -17425,33 +17528,33 @@ var ga4AiReferralDtoSchema = z51.object({
17425
17528
  */
17426
17529
  share: fraction()
17427
17530
  });
17428
- var ga4AiReferralLandingPageDtoSchema = z51.object({
17429
- source: z51.string(),
17430
- medium: z51.string(),
17531
+ var ga4AiReferralLandingPageDtoSchema = z52.object({
17532
+ source: z52.string(),
17533
+ medium: z52.string(),
17431
17534
  trafficClass: aiReferralTrafficClassSchema,
17432
17535
  /**
17433
17536
  * The winning attribution dimension for this (source, medium, landingPage)
17434
17537
  * tuple — the one with the highest session count.
17435
17538
  */
17436
17539
  sourceDimension: ga4SourceDimensionSchema,
17437
- landingPage: z51.string(),
17438
- sessions: z51.number(),
17540
+ landingPage: z52.string(),
17541
+ sessions: z52.number(),
17439
17542
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
17440
- users: z51.number().optional()
17543
+ users: z52.number().optional()
17441
17544
  });
17442
- var ga4SocialReferralDtoSchema = z51.object({
17443
- source: z51.string(),
17444
- medium: z51.string(),
17445
- sessions: z51.number(),
17545
+ var ga4SocialReferralDtoSchema = z52.object({
17546
+ source: z52.string(),
17547
+ medium: z52.string(),
17548
+ sessions: z52.number(),
17446
17549
  /**
17447
17550
  * @deprecated Never emitted by `/ga/traffic`. ga_social_referrals stores
17448
17551
  * users as GA's COUNT DISTINCT at (date, source, medium, channel group), so
17449
17552
  * summing it across the window counts a returning visitor once per day.
17450
17553
  * Optional so existing consumers keep parsing.
17451
17554
  */
17452
- users: z51.number().optional(),
17555
+ users: z52.number().optional(),
17453
17556
  /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
17454
- channelGroup: z51.string(),
17557
+ channelGroup: z52.string(),
17455
17558
  /**
17456
17559
  * This row's sessions as a 0..1 share of the window's social sessions
17457
17560
  * (`socialSessions`, which is the sum of these rows), so the rows' shares add
@@ -17459,51 +17562,51 @@ var ga4SocialReferralDtoSchema = z51.object({
17459
17562
  */
17460
17563
  share: fraction()
17461
17564
  });
17462
- var ga4SocialPlatformDtoSchema = z51.object({
17565
+ var ga4SocialPlatformDtoSchema = z52.object({
17463
17566
  /** Platform id from `socialPlatformForReferralSource`; null for a source no platform matches. */
17464
17567
  platform: socialPlatformSchema.nullable(),
17465
17568
  /** The platform's name, or the raw source when `platform` is null. */
17466
- label: z51.string(),
17467
- sessions: z51.number(),
17569
+ label: z52.string(),
17570
+ sessions: z52.number(),
17468
17571
  /** Sessions outside GA4's Paid Social channel group. */
17469
- organicSessions: z51.number(),
17572
+ organicSessions: z52.number(),
17470
17573
  /** Sessions in GA4's Paid Social channel group. */
17471
- paidSessions: z51.number(),
17574
+ paidSessions: z52.number(),
17472
17575
  /** 0..1 share of `socialSessions`; the groups' shares add up to 1. */
17473
17576
  share: fraction(),
17474
17577
  /** The grouped `socialReferrals` rows, sessions descending. */
17475
- sources: z51.array(ga4SocialReferralDtoSchema)
17578
+ sources: z52.array(ga4SocialReferralDtoSchema)
17476
17579
  });
17477
- var ga4AiEngineReferralDtoSchema = z51.object({
17580
+ var ga4AiEngineReferralDtoSchema = z52.object({
17478
17581
  /** Engine id from `aiEngineForReferralSource`; null for the unattributed group. */
17479
17582
  engine: aiReferralEngineSchema.nullable(),
17480
17583
  /** The engine's name, or `Unattributed AI`. */
17481
- label: z51.string(),
17482
- sessions: z51.number(),
17483
- organicSessions: z51.number(),
17484
- paidSessions: z51.number(),
17584
+ label: z52.string(),
17585
+ sessions: z52.number(),
17586
+ organicSessions: z52.number(),
17587
+ paidSessions: z52.number(),
17485
17588
  /** 0..1 share of the `aiReferrals` rows' sessions, the same base as each row's `share`. */
17486
17589
  share: fraction(),
17487
17590
  /** The grouped `aiReferrals` rows, sessions descending. */
17488
- sources: z51.array(ga4AiReferralDtoSchema)
17591
+ sources: z52.array(ga4AiReferralDtoSchema)
17489
17592
  });
17490
- var ga4ChannelBucketDtoSchema = z51.object({
17491
- sessions: z51.number(),
17593
+ var ga4ChannelBucketDtoSchema = z52.object({
17594
+ sessions: z52.number(),
17492
17595
  sharePct: percent(),
17493
- sharePctDisplay: z51.string()
17596
+ sharePctDisplay: z52.string()
17494
17597
  });
17495
- var ga4ChannelBreakdownDtoSchema = z51.object({
17598
+ var ga4ChannelBreakdownDtoSchema = z52.object({
17496
17599
  organic: ga4ChannelBucketDtoSchema,
17497
17600
  social: ga4ChannelBucketDtoSchema,
17498
17601
  direct: ga4ChannelBucketDtoSchema,
17499
17602
  ai: ga4ChannelBucketDtoSchema,
17500
17603
  other: ga4ChannelBucketDtoSchema
17501
17604
  });
17502
- var ga4TrafficSummaryDtoSchema = z51.object({
17503
- totalSessions: z51.number(),
17504
- totalOrganicSessions: z51.number(),
17605
+ var ga4TrafficSummaryDtoSchema = z52.object({
17606
+ totalSessions: z52.number(),
17607
+ totalOrganicSessions: z52.number(),
17505
17608
  /** Direct-channel sessions (sessions with no source — bookmarks, typed URLs, AI-driven traffic with stripped referrer). 0 for legacy rows from before the column was added. */
17506
- totalDirectSessions: z51.number(),
17609
+ totalDirectSessions: z52.number(),
17507
17610
  /**
17508
17611
  * Null when the range has no un-dimensioned aggregate behind it.
17509
17612
  *
@@ -17512,14 +17615,14 @@ var ga4TrafficSummaryDtoSchema = z51.object({
17512
17615
  * windows have a precomputed deduplicated row; an explicit calendar range does
17513
17616
  * not, and reports the figure as unavailable rather than as an inflated sum.
17514
17617
  */
17515
- totalUsers: z51.number().nullable(),
17516
- topPages: z51.array(z51.object({
17517
- landingPage: z51.string(),
17518
- sessions: z51.number(),
17519
- organicSessions: z51.number(),
17618
+ totalUsers: z52.number().nullable(),
17619
+ topPages: z52.array(z52.object({
17620
+ landingPage: z52.string(),
17621
+ sessions: z52.number(),
17622
+ organicSessions: z52.number(),
17520
17623
  /** Per-page Direct-channel sessions. 0 for legacy rows. */
17521
- directSessions: z51.number(),
17522
- users: z51.number(),
17624
+ directSessions: z52.number(),
17625
+ users: z52.number(),
17523
17626
  /**
17524
17627
  * Organic search sessions as a 0..1 share of this page's sessions. 0 when
17525
17628
  * the page has no organic sessions; null when organic sessions exist with
@@ -17527,49 +17630,49 @@ var ga4TrafficSummaryDtoSchema = z51.object({
17527
17630
  */
17528
17631
  organicShare: fraction().nullable()
17529
17632
  })),
17530
- aiReferrals: z51.array(ga4AiReferralDtoSchema),
17531
- aiReferralLandingPages: z51.array(ga4AiReferralLandingPageDtoSchema),
17633
+ aiReferrals: z52.array(ga4AiReferralDtoSchema),
17634
+ aiReferralLandingPages: z52.array(ga4AiReferralLandingPageDtoSchema),
17532
17635
  /** Deduped AI session total: MAX(sessions) per date+source across attribution dimensions, then summed. Cross-cutting: can overlap with Direct/Organic/Social via firstUserSource. */
17533
- aiSessionsDeduped: z51.number(),
17636
+ aiSessionsDeduped: z52.number(),
17534
17637
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
17535
- aiUsersDeduped: z51.number().optional(),
17638
+ aiUsersDeduped: z52.number().optional(),
17536
17639
  /** Deduped AI sessions whose attribution carries paid intent. */
17537
- paidAiSessionsDeduped: z51.number(),
17640
+ paidAiSessionsDeduped: z52.number(),
17538
17641
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
17539
- paidAiUsersDeduped: z51.number().optional(),
17642
+ paidAiUsersDeduped: z52.number().optional(),
17540
17643
  /** Deduped AI sessions without paid intent evidence. */
17541
- organicAiSessionsDeduped: z51.number(),
17644
+ organicAiSessionsDeduped: z52.number(),
17542
17645
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
17543
- organicAiUsersDeduped: z51.number().optional(),
17646
+ organicAiUsersDeduped: z52.number().optional(),
17544
17647
  /** AI sessions whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals; `channelBreakdown` removes those overlaps for display. */
17545
- aiSessionsBySession: z51.number(),
17648
+ aiSessionsBySession: z52.number(),
17546
17649
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
17547
- aiUsersBySession: z51.number().optional(),
17650
+ aiUsersBySession: z52.number().optional(),
17548
17651
  /** Session-source-only paid AI sessions. */
17549
- paidAiSessionsBySession: z51.number(),
17652
+ paidAiSessionsBySession: z52.number(),
17550
17653
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
17551
- paidAiUsersBySession: z51.number().optional(),
17654
+ paidAiUsersBySession: z52.number().optional(),
17552
17655
  /** Session-source-only organic/non-paid AI sessions. */
17553
- organicAiSessionsBySession: z51.number(),
17656
+ organicAiSessionsBySession: z52.number(),
17554
17657
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
17555
- organicAiUsersBySession: z51.number().optional(),
17658
+ organicAiUsersBySession: z52.number().optional(),
17556
17659
  /**
17557
17660
  * `aiReferrals` grouped by engine, sessions descending, with the unattributed
17558
17661
  * group last. The groups' sessions add up to the rows' sessions.
17559
17662
  */
17560
- aiEngines: z51.array(ga4AiEngineReferralDtoSchema),
17561
- socialReferrals: z51.array(ga4SocialReferralDtoSchema),
17663
+ aiEngines: z52.array(ga4AiEngineReferralDtoSchema),
17664
+ socialReferrals: z52.array(ga4SocialReferralDtoSchema),
17562
17665
  /** `socialReferrals` grouped by platform, sessions descending; the groups' sessions add up to `socialSessions`. */
17563
- socialPlatforms: z51.array(ga4SocialPlatformDtoSchema),
17666
+ socialPlatforms: z52.array(ga4SocialPlatformDtoSchema),
17564
17667
  /**
17565
17668
  * Daily social sessions by platform over the same window as every other
17566
17669
  * figure here: the five largest platforms, then the rest as `other`.
17567
17670
  */
17568
17671
  socialPlatformDaily: stackedDailySeriesSchema,
17569
17672
  /** Total social sessions (session-scoped, no cross-dimension dedup needed). */
17570
- socialSessions: z51.number(),
17673
+ socialSessions: z52.number(),
17571
17674
  /** Social sessions in GA4's Paid Social channel group. */
17572
- paidSocialSessions: z51.number(),
17675
+ paidSocialSessions: z52.number(),
17573
17676
  /** `paidSocialSessions` as a 0..1 share of `socialSessions`; 0 with no social sessions. */
17574
17677
  paidSocialShare: fraction(),
17575
17678
  /** Total social users (session-scoped, no cross-dimension dedup needed). */
@@ -17578,7 +17681,7 @@ var ga4TrafficSummaryDtoSchema = z51.object({
17578
17681
  * COUNT DISTINCT at (date, source, medium, channel group), so summing it
17579
17682
  * across the window counts a returning visitor once per day.
17580
17683
  */
17581
- socialUsers: z51.number().optional(),
17684
+ socialUsers: z52.number().optional(),
17582
17685
  /** Five disjoint buckets used for the channel breakdown. Known AI session-source matches are removed from their native GA4 bucket before shares are computed. */
17583
17686
  channelBreakdown: ga4ChannelBreakdownDtoSchema,
17584
17687
  /** Organic sessions as a percentage of total sessions (0–100, at wire precision). */
@@ -17600,55 +17703,56 @@ var ga4TrafficSummaryDtoSchema = z51.object({
17600
17703
  /** Social sessions as a percentage of total sessions (0–100, at wire precision). */
17601
17704
  socialSharePct: percent(),
17602
17705
  /** Display string for organicSharePct: the unrounded share through formatPercent ('12.5%', '<0.1%' for a non-zero share below one decimal), '0%' with no sessions, or '—' when sessions exist but total is unknown (partial sync). */
17603
- organicSharePctDisplay: z51.string(),
17706
+ organicSharePctDisplay: z52.string(),
17604
17707
  /** Display string for aiSharePct: the unrounded share through formatPercent ('12.5%', '<0.1%' for a non-zero share below one decimal), '0%' with no sessions, or '—' when sessions exist but total is unknown (partial sync). */
17605
- aiSharePctDisplay: z51.string(),
17708
+ aiSharePctDisplay: z52.string(),
17606
17709
  /** Display string for aiSharePctBySession: the unrounded share through formatPercent ('12.5%', '<0.1%' for a non-zero share below one decimal), '0%' with no sessions, or '—' when sessions exist but total is unknown (partial sync). */
17607
- aiSharePctBySessionDisplay: z51.string(),
17710
+ aiSharePctBySessionDisplay: z52.string(),
17608
17711
  /** Display string for paidAiSharePct. */
17609
- paidAiSharePctDisplay: z51.string(),
17712
+ paidAiSharePctDisplay: z52.string(),
17610
17713
  /** Display string for paidAiSharePctBySession. */
17611
- paidAiSharePctBySessionDisplay: z51.string(),
17714
+ paidAiSharePctBySessionDisplay: z52.string(),
17612
17715
  /** Display string for organicAiSharePct. */
17613
- organicAiSharePctDisplay: z51.string(),
17716
+ organicAiSharePctDisplay: z52.string(),
17614
17717
  /** Display string for organicAiSharePctBySession. */
17615
- organicAiSharePctBySessionDisplay: z51.string(),
17718
+ organicAiSharePctBySessionDisplay: z52.string(),
17616
17719
  /** Display string for directSharePct: the unrounded share through formatPercent ('12.5%', '<0.1%' for a non-zero share below one decimal), '0%' with no sessions, or '—' when sessions exist but total is unknown (partial sync). */
17617
- directSharePctDisplay: z51.string(),
17720
+ directSharePctDisplay: z52.string(),
17618
17721
  /** Display string for socialSharePct: the unrounded share through formatPercent ('12.5%', '<0.1%' for a non-zero share below one decimal), '0%' with no sessions, or '—' when sessions exist but total is unknown (partial sync). */
17619
- socialSharePctDisplay: z51.string(),
17722
+ socialSharePctDisplay: z52.string(),
17620
17723
  /** Sessions not covered by Organic, Social, Direct, or AI (session) channels — e.g. Referral, Email, Paid Search, Display. Always non-negative; clamped to 0 when the four disjoint channels sum above total (rounding edge). */
17621
- otherSessions: z51.number(),
17724
+ otherSessions: z52.number(),
17622
17725
  /** Other sessions as a percentage of total sessions (0–100, at wire precision). */
17623
17726
  otherSharePct: percent(),
17624
17727
  /** Display string for otherSharePct: the unrounded share through formatPercent ('12.5%', '<0.1%' for a non-zero share below one decimal), '0%' with no sessions, or '—' when sessions exist but total is unknown (partial sync). */
17625
- otherSharePctDisplay: z51.string(),
17626
- lastSyncedAt: z51.string().nullable()
17627
- });
17628
- var ga4StatusDtoSchema = z51.object({
17629
- connected: z51.boolean(),
17630
- propertyId: z51.string().nullable(),
17631
- clientEmail: z51.string().nullable(),
17632
- authMethod: z51.enum(["service-account", "oauth"]).nullable(),
17633
- lastSyncedAt: z51.string().nullable(),
17634
- createdAt: z51.string().nullable().optional(),
17635
- updatedAt: z51.string().nullable().optional()
17636
- });
17637
- var ga4PropertySummaryDtoSchema = z51.object({
17638
- propertyId: z51.string(),
17639
- displayName: z51.string(),
17728
+ otherSharePctDisplay: z52.string(),
17729
+ lastSyncedAt: z52.string().nullable()
17730
+ });
17731
+ var ga4StatusDtoSchema = z52.object({
17732
+ connected: z52.boolean(),
17733
+ propertyId: z52.string().nullable(),
17734
+ clientEmail: z52.string().nullable(),
17735
+ authMethod: z52.enum(["service-account", "oauth"]).nullable(),
17736
+ lastSyncedAt: z52.string().nullable(),
17737
+ createdAt: z52.string().nullable().optional(),
17738
+ updatedAt: z52.string().nullable().optional()
17739
+ });
17740
+ var ga4PropertySummaryDtoSchema = z52.object({
17741
+ propertyId: z52.string(),
17742
+ displayName: z52.string(),
17640
17743
  /** GA4 account that owns the property. Two accounts can hold like-named properties. */
17641
- accountName: z51.string()
17744
+ accountName: z52.string()
17642
17745
  });
17643
- var ga4PropertiesDtoSchema = z51.object({
17644
- properties: z51.array(ga4PropertySummaryDtoSchema)
17746
+ var ga4PropertiesDtoSchema = z52.object({
17747
+ properties: z52.array(ga4PropertySummaryDtoSchema)
17645
17748
  });
17646
- var gaSearchLandingWindowSchema = z51.enum(WINDOW_SETS.gaSearchLanding);
17749
+ var gaSearchLandingWindowSchema = z52.enum(WINDOW_SETS.gaSearchLanding);
17647
17750
  var GA_SEARCH_LANDING_WINDOWS = gaSearchLandingWindowSchema.options;
17648
17751
  var GA_SEARCH_LANDING_WINDOW_DAYS = {
17649
17752
  "7d": 7,
17650
17753
  "28d": 28,
17651
- "90d": 90
17754
+ "90d": 90,
17755
+ "180d": 180
17652
17756
  };
17653
17757
  var GA_SEARCH_LANDING_DEFAULT_WINDOW = "28d";
17654
17758
  var GA_SEARCH_LANDING_DEFAULT_LIMIT = 50;
@@ -17657,124 +17761,124 @@ function gaSearchLandingPageLabel(landingPage) {
17657
17761
  return landingPage === "" ? GA_SEARCH_LANDING_EMPTY_PAGE_LABEL : landingPage;
17658
17762
  }
17659
17763
  var GA_SEARCH_LANDING_MAX_LIMIT = 1e3;
17660
- var gaSearchLandingStatusSchema = z51.enum(["never-synced", "ready", "unavailable", "error"]);
17661
- var gaSearchLandingMetricsSchema = z51.object({
17662
- organicGoogleSearchClicks: z51.number().int().nonnegative(),
17663
- organicGoogleSearchImpressions: z51.number().int().nonnegative(),
17764
+ var gaSearchLandingStatusSchema = z52.enum(["never-synced", "ready", "unavailable", "error"]);
17765
+ var gaSearchLandingMetricsSchema = z52.object({
17766
+ organicGoogleSearchClicks: z52.number().int().nonnegative(),
17767
+ organicGoogleSearchImpressions: z52.number().int().nonnegative(),
17664
17768
  /** GA4's own click-through rate (0..1). Null when there were no impressions. */
17665
- organicGoogleSearchClickThroughRate: fraction(z51.number().min(0).max(1)).nullable(),
17769
+ organicGoogleSearchClickThroughRate: fraction(z52.number().min(0).max(1)).nullable(),
17666
17770
  /** GA4's own average position. Null when there were no impressions. */
17667
- organicGoogleSearchAveragePosition: z51.number().nonnegative().nullable(),
17771
+ organicGoogleSearchAveragePosition: z52.number().nonnegative().nullable(),
17668
17772
  /**
17669
17773
  * GA4's plain `activeUsers` from the same request, which is what GA4's own
17670
17774
  * report shows in this column (verified against the GA4 UI). Not limited to
17671
17775
  * Google organic sessions.
17672
17776
  */
17673
- activeUsers: z51.number().int().nonnegative()
17777
+ activeUsers: z52.number().int().nonnegative()
17674
17778
  });
17675
17779
  var gaSearchLandingPageRowSchema = gaSearchLandingMetricsSchema.extend({
17676
17780
  /** GA4's `landingPagePlusQueryString`, exactly as reported (never normalized). */
17677
- landingPage: z51.string()
17781
+ landingPage: z52.string()
17678
17782
  });
17679
- var gaSearchConsoleLinkStateSchema = z51.enum(["has-data", "not-linked", "no-search-data", "unknown"]);
17680
- var gaSearchLandingPagesResponseSchema = z51.object({
17681
- source: z51.literal("ga4-search-console-link"),
17783
+ var gaSearchConsoleLinkStateSchema = z52.enum(["has-data", "not-linked", "no-search-data", "unknown"]);
17784
+ var gaSearchLandingPagesResponseSchema = z52.object({
17785
+ source: z52.literal("ga4-search-console-link"),
17682
17786
  status: gaSearchLandingStatusSchema,
17683
17787
  /** The last failed attempt's message (Google's own text for `unavailable`). */
17684
- error: z51.string().nullable(),
17788
+ error: z52.string().nullable(),
17685
17789
  /** When the stored snapshot was written. Null when none exists. */
17686
- syncedAt: z51.string().datetime().nullable(),
17790
+ syncedAt: z52.string().datetime().nullable(),
17687
17791
  /** When a sync last tried to refresh the snapshot, successful or not. */
17688
- attemptedAt: z51.string().datetime().nullable(),
17792
+ attemptedAt: z52.string().datetime().nullable(),
17689
17793
  window: gaSearchLandingWindowSchema,
17690
17794
  /** Inclusive first day of the stored window, in the property's time zone. */
17691
- windowStart: z51.iso.date().nullable(),
17795
+ windowStart: z52.iso.date().nullable(),
17692
17796
  /** Inclusive last day of the stored window (yesterday at sync time). */
17693
- windowEnd: z51.iso.date().nullable(),
17694
- windowDays: z51.number().int().positive().nullable(),
17797
+ windowEnd: z52.iso.date().nullable(),
17798
+ windowDays: z52.number().int().positive().nullable(),
17695
17799
  /** GA4 property reporting time zone the window dates are in. */
17696
- timeZone: z51.string().nullable(),
17697
- subjectToThresholding: z51.boolean(),
17698
- dataLossFromOtherRow: z51.boolean(),
17800
+ timeZone: z52.string().nullable(),
17801
+ subjectToThresholding: z52.boolean(),
17802
+ dataLossFromOtherRow: z52.boolean(),
17699
17803
  /** GA4's own TOTAL row for the window. Never a sum of `rows`. Null when no snapshot exists. */
17700
17804
  total: gaSearchLandingMetricsSchema.nullable(),
17701
17805
  /** Rows GA4 reported for the window. Can exceed `totalRows` when `rowsCapped`. */
17702
- reportRowCount: z51.number().int().nonnegative().nullable(),
17806
+ reportRowCount: z52.number().int().nonnegative().nullable(),
17703
17807
  /** True when GA4 reported more rows than were stored (the per-window row cap). */
17704
- rowsCapped: z51.boolean(),
17808
+ rowsCapped: z52.boolean(),
17705
17809
  /** Stored rows for the window, the population `limit` / `offset` page over. */
17706
- totalRows: z51.number().int().nonnegative(),
17707
- limit: z51.number().int().positive(),
17708
- offset: z51.number().int().nonnegative(),
17810
+ totalRows: z52.number().int().nonnegative(),
17811
+ limit: z52.number().int().positive(),
17812
+ offset: z52.number().int().nonnegative(),
17709
17813
  /** Ordered by clicks, then impressions (both descending), then landing page. */
17710
- rows: z51.array(gaSearchLandingPageRowSchema),
17814
+ rows: z52.array(gaSearchLandingPageRowSchema),
17711
17815
  /**
17712
17816
  * Whether the window's GA4 report has Search Console data, compared with
17713
17817
  * Canonry's own Search Console data for the same days. Null when no window
17714
17818
  * is stored.
17715
17819
  */
17716
- searchConsoleLink: z51.object({
17820
+ searchConsoleLink: z52.object({
17717
17821
  state: gaSearchConsoleLinkStateSchema,
17718
17822
  /** Search Console impressions Canonry stores for the window's days; null when it has none for them. */
17719
- searchConsoleImpressions: z51.number().int().nonnegative().nullable()
17823
+ searchConsoleImpressions: z52.number().int().nonnegative().nullable()
17720
17824
  }).nullable()
17721
17825
  });
17722
17826
  function gaSearchLandingHasSearchData(total) {
17723
17827
  return total.organicGoogleSearchClicks > 0 || total.organicGoogleSearchImpressions > 0;
17724
17828
  }
17725
- var gaSearchLandingSyncResultSchema = z51.object({
17726
- status: z51.enum(["ready", "unavailable", "error"]),
17829
+ var gaSearchLandingSyncResultSchema = z52.object({
17830
+ status: z52.enum(["ready", "unavailable", "error"]),
17727
17831
  /** Windows written by this sync, empty unless `ready`. */
17728
- windows: z51.array(z51.object({
17832
+ windows: z52.array(z52.object({
17729
17833
  window: gaSearchLandingWindowSchema,
17730
- rowCount: z51.number().int().nonnegative(),
17731
- reportRowCount: z51.number().int().nonnegative(),
17732
- rowsCapped: z51.boolean()
17834
+ rowCount: z52.number().int().nonnegative(),
17835
+ reportRowCount: z52.number().int().nonnegative(),
17836
+ rowsCapped: z52.boolean()
17733
17837
  })),
17734
17838
  /** Landing-page rows written across every window. */
17735
- rowCount: z51.number().int().nonnegative(),
17736
- error: z51.string().optional()
17737
- });
17738
- var gaSyncOnlySchema = z51.enum(["traffic", "ai", "social", "search-landing"]);
17739
- var ga4SyncResponseDtoSchema = z51.object({
17740
- synced: z51.boolean(),
17741
- rowCount: z51.number().int().nonnegative(),
17742
- aiReferralCount: z51.number().int().nonnegative(),
17743
- socialReferralCount: z51.number().int().nonnegative(),
17744
- /**
17745
- * The window that was ACTUALLY fetched and written, in days — the request's
17746
- * `days` bounded to GA4's supported sync range (1–90). Read this, not
17839
+ rowCount: z52.number().int().nonnegative(),
17840
+ error: z52.string().optional()
17841
+ });
17842
+ var gaSyncOnlySchema = z52.enum(["traffic", "ai", "social", "search-landing"]);
17843
+ var ga4SyncResponseDtoSchema = z52.object({
17844
+ synced: z52.boolean(),
17845
+ rowCount: z52.number().int().nonnegative(),
17846
+ aiReferralCount: z52.number().int().nonnegative(),
17847
+ socialReferralCount: z52.number().int().nonnegative(),
17848
+ /**
17849
+ * The requested coverage window, in days — reused mature days plus fetched gaps. The request's
17850
+ * `days` is bounded to GA4's supported sync range (1–180). Read this, not
17747
17851
  * `requestedDays`, when reporting or reasoning about coverage: a
17748
- * `--days 500` sync writes 90 days, and this field says 90.
17852
+ * `--days 500` sync covers 180 days, and this field says 180.
17749
17853
  */
17750
- days: z51.number().int().nonnegative(),
17854
+ days: z52.number().int().nonnegative(),
17751
17855
  /** The window the caller asked for, before bounding. Equals `days` unless `clamped`. */
17752
- requestedDays: z51.number().int().nonnegative(),
17856
+ requestedDays: z52.number().int().nonnegative(),
17753
17857
  /**
17754
17858
  * True when `requestedDays` fell outside GA4's supported range and `days`
17755
17859
  * differs from it — i.e. the synced history is not the range requested.
17756
17860
  */
17757
- clamped: z51.boolean(),
17758
- syncedAt: z51.string(),
17759
- measurement: z51.object({
17760
- acquisition: z51.object({ days: z51.number().int().nonnegative(), status: z51.enum(["ready", "error"]), rowCount: z51.number().int().nonnegative(), error: z51.string().optional() }),
17761
- leads: z51.object({
17762
- days: z51.number().int().nonnegative(),
17763
- status: z51.enum(["ready", "error", "not-configured"]),
17764
- rowCount: z51.number().int().nonnegative(),
17765
- attributionScope: z51.enum(["landing-page", "channel"]).optional(),
17766
- error: z51.string().optional(),
17861
+ clamped: z52.boolean(),
17862
+ syncedAt: z52.string(),
17863
+ measurement: z52.object({
17864
+ acquisition: z52.object({ days: z52.number().int().nonnegative(), status: z52.enum(["ready", "error"]), rowCount: z52.number().int().nonnegative(), error: z52.string().optional() }),
17865
+ leads: z52.object({
17866
+ days: z52.number().int().nonnegative(),
17867
+ status: z52.enum(["ready", "error", "not-configured"]),
17868
+ rowCount: z52.number().int().nonnegative(),
17869
+ attributionScope: z52.enum(["landing-page", "channel"]).optional(),
17870
+ error: z52.string().optional(),
17767
17871
  /** The GA4 events this sync counted: the configured lead events plus the property's key events. */
17768
- eventNames: z51.array(z51.string()).optional(),
17872
+ eventNames: z52.array(z52.string()).optional(),
17769
17873
  /**
17770
17874
  * The property's key-event read. `read` lists them; `error` keeps the
17771
17875
  * previous successful list and names the failure; `disabled` means the
17772
17876
  * project counts only its configured lead events.
17773
17877
  */
17774
- keyEvents: z51.object({
17775
- status: z51.enum(["read", "error", "disabled"]),
17776
- names: z51.array(z51.string()),
17777
- error: z51.string().optional()
17878
+ keyEvents: z52.object({
17879
+ status: z52.enum(["read", "error", "disabled"]),
17880
+ names: z52.array(z52.string()),
17881
+ error: z52.string().optional()
17778
17882
  }).optional()
17779
17883
  }),
17780
17884
  /**
@@ -17792,18 +17896,18 @@ var ga4SyncResponseDtoSchema = z51.object({
17792
17896
  * or `unavailable` attempt is left out and reported in
17793
17897
  * `measurement.searchLandingPages`.
17794
17898
  */
17795
- syncedComponents: z51.array(z51.string()).optional()
17899
+ syncedComponents: z52.array(z52.string()).optional()
17796
17900
  });
17797
- var gaMoverChangeBasisSchema = z51.enum(["new", "small-base", "percent"]);
17901
+ var gaMoverChangeBasisSchema = z52.enum(["new", "small-base", "percent"]);
17798
17902
  var GaMoverChangeBases = gaMoverChangeBasisSchema.enum;
17799
- var gaSourceMoverSchema = z51.object({
17800
- source: z51.string(),
17903
+ var gaSourceMoverSchema = z52.object({
17904
+ source: z52.string(),
17801
17905
  /** Sessions from this source in the last 7 days. */
17802
- sessions7d: z51.number(),
17906
+ sessions7d: z52.number(),
17803
17907
  /** Sessions from this source in the 7 days before that. */
17804
- sessionsPrev7d: z51.number(),
17908
+ sessionsPrev7d: z52.number(),
17805
17909
  /** Signed session change, `sessions7d - sessionsPrev7d`. The mover is the source whose change is largest in size. */
17806
- changeSessions: z51.number(),
17910
+ changeSessions: z52.number(),
17807
17911
  /**
17808
17912
  * Signed whole-percent change against the prior 7 days (`150` = +150%, `-100`
17809
17913
  * = the source stopped sending sessions). Null when the prior 7 days had no
@@ -17812,29 +17916,29 @@ var gaSourceMoverSchema = z51.object({
17812
17916
  changePct: percent().nullable(),
17813
17917
  changeBasis: gaMoverChangeBasisSchema
17814
17918
  });
17815
- var gaSocialReferralTrendResponseSchema = z51.object({
17816
- socialSessions7d: z51.number(),
17817
- socialSessionsPrev7d: z51.number(),
17919
+ var gaSocialReferralTrendResponseSchema = z52.object({
17920
+ socialSessions7d: z52.number(),
17921
+ socialSessionsPrev7d: z52.number(),
17818
17922
  /** Signed whole-percent change of the last 7 days against the 7 before. Null when the prior 7 days had no sessions. */
17819
17923
  trend7dPct: percent().nullable(),
17820
- socialSessions30d: z51.number(),
17821
- socialSessionsPrev30d: z51.number(),
17924
+ socialSessions30d: z52.number(),
17925
+ socialSessionsPrev30d: z52.number(),
17822
17926
  /** Signed whole-percent change of the last 30 days against the 30 before. Null when the prior 30 days had no sessions. */
17823
17927
  trend30dPct: percent().nullable(),
17824
17928
  /** Social source with the largest session change in the last 7 days against the 7 before. Null when no source moved. */
17825
17929
  biggestMover: gaSourceMoverSchema.nullable()
17826
17930
  });
17827
- var gaChannelTrendSchema = z51.object({
17828
- sessions7d: z51.number(),
17829
- sessionsPrev7d: z51.number(),
17931
+ var gaChannelTrendSchema = z52.object({
17932
+ sessions7d: z52.number(),
17933
+ sessionsPrev7d: z52.number(),
17830
17934
  /** Signed whole-percent change of the last 7 days against the 7 before. Null when the prior 7 days had no sessions. */
17831
17935
  trend7dPct: percent().nullable(),
17832
- sessions30d: z51.number(),
17833
- sessionsPrev30d: z51.number(),
17936
+ sessions30d: z52.number(),
17937
+ sessionsPrev30d: z52.number(),
17834
17938
  /** Signed whole-percent change of the last 30 days against the 30 before. Null when the prior 30 days had no sessions. */
17835
17939
  trend30dPct: percent().nullable()
17836
17940
  });
17837
- var gaAttributionTrendResponseSchema = z51.object({
17941
+ var gaAttributionTrendResponseSchema = z52.object({
17838
17942
  organic: gaChannelTrendSchema,
17839
17943
  /** AI session trend, scoped to sessionSource-only matches so it lines up with the disjoint AI cell in the channel breakdown. */
17840
17944
  ai: gaChannelTrendSchema,
@@ -17846,23 +17950,23 @@ var gaAttributionTrendResponseSchema = z51.object({
17846
17950
  /** Social source with the largest session change in the last 7 days against the 7 before. Null when no source moved. */
17847
17951
  socialBiggestMover: gaSourceMoverSchema.nullable()
17848
17952
  });
17849
- var gaOverviewValuesSchema = z51.object({
17850
- activeUsers: z51.number().int().nonnegative().nullable(),
17851
- newUsers: z51.number().int().nonnegative().nullable(),
17852
- totalUsers: z51.number().int().nonnegative().nullable(),
17853
- sessions: z51.number().int().nonnegative().nullable(),
17854
- organicSessions: z51.number().int().nonnegative().nullable(),
17855
- views: z51.number().int().nonnegative().nullable(),
17856
- eventCount: z51.number().int().nonnegative().nullable(),
17953
+ var gaOverviewValuesSchema = z52.object({
17954
+ activeUsers: z52.number().int().nonnegative().nullable(),
17955
+ newUsers: z52.number().int().nonnegative().nullable(),
17956
+ totalUsers: z52.number().int().nonnegative().nullable(),
17957
+ sessions: z52.number().int().nonnegative().nullable(),
17958
+ organicSessions: z52.number().int().nonnegative().nullable(),
17959
+ views: z52.number().int().nonnegative().nullable(),
17960
+ eventCount: z52.number().int().nonnegative().nullable(),
17857
17961
  /** GA4 counts a key event once per session when the property says so, so this can be a decimal. */
17858
- keyEvents: z51.number().nonnegative().nullable(),
17962
+ keyEvents: z52.number().nonnegative().nullable(),
17859
17963
  engagementRate: fraction().nullable(),
17860
17964
  bounceRate: fraction().nullable(),
17861
- averageEngagementTimeSeconds: z51.number().nonnegative().nullable()
17965
+ averageEngagementTimeSeconds: z52.number().nonnegative().nullable()
17862
17966
  });
17863
17967
  var gaOverviewMetricKeySchema = gaOverviewValuesSchema.keyof();
17864
17968
  var GaOverviewMetricKeys = gaOverviewMetricKeySchema.enum;
17865
- var gaOverviewChangeSchema = z51.object({
17969
+ var gaOverviewChangeSchema = z52.object({
17866
17970
  activeUsers: percent().nullable(),
17867
17971
  newUsers: percent().nullable(),
17868
17972
  totalUsers: percent().nullable(),
@@ -17875,12 +17979,12 @@ var gaOverviewChangeSchema = z51.object({
17875
17979
  bounceRate: percent().nullable(),
17876
17980
  averageEngagementTimeSeconds: percent().nullable()
17877
17981
  });
17878
- var GA_OVERVIEW_WINDOW_DAYS = { "7d": 7, "30d": 30, "90d": 90 };
17982
+ var GA_OVERVIEW_WINDOW_DAYS = { "7d": 7, "30d": 30, "90d": 90, "180d": 180 };
17879
17983
  var GA_OVERVIEW_WINDOWS = WINDOW_SETS.gaOverview;
17880
17984
  function isGaOverviewWindow(value) {
17881
17985
  return GA_OVERVIEW_WINDOWS.some((window) => window === value);
17882
17986
  }
17883
- var GA_OVERVIEW_HISTORY_DAYS = 2 * GA_OVERVIEW_WINDOW_DAYS["90d"];
17987
+ var GA_OVERVIEW_HISTORY_DAYS = 2 * GA_OVERVIEW_WINDOW_DAYS["180d"];
17884
17988
  function gaOverviewComparisonDaysAgo(window) {
17885
17989
  const days = GA_OVERVIEW_WINDOW_DAYS[window];
17886
17990
  return {
@@ -17897,20 +18001,20 @@ function gaOverviewComparisonRanges(window, now = /* @__PURE__ */ new Date(), ti
17897
18001
  };
17898
18002
  }
17899
18003
  var GaOverviewPeriods = { current: "current", previous: "previous" };
17900
- var gaOverviewPeriodSchema = z51.object({
18004
+ var gaOverviewPeriodSchema = z52.object({
17901
18005
  /** Inclusive. Null when the window has never been synced with the overview metrics. */
17902
- startDate: z51.string().nullable(),
17903
- endDate: z51.string().nullable()
18006
+ startDate: z52.string().nullable(),
18007
+ endDate: z52.string().nullable()
17904
18008
  });
17905
- var gaOverviewDaySchema = z51.object({
17906
- date: z51.string(),
18009
+ var gaOverviewDaySchema = z52.object({
18010
+ date: z52.string(),
17907
18011
  /** The day of the previous period in the same position: the window's first day lines up with its first. */
17908
- previousDate: z51.string(),
18012
+ previousDate: z52.string(),
17909
18013
  values: gaOverviewValuesSchema,
17910
18014
  previousValues: gaOverviewValuesSchema
17911
18015
  });
17912
- var gaOverviewResponseSchema = z51.object({
17913
- window: metricsWindowSchema,
18016
+ var gaOverviewResponseSchema = z52.object({
18017
+ window: z52.enum(WINDOW_SETS.gaTraffic),
17914
18018
  current: gaOverviewPeriodSchema,
17915
18019
  /** The period just before `current`, of the same length. */
17916
18020
  previous: gaOverviewPeriodSchema,
@@ -17923,9 +18027,9 @@ var gaOverviewResponseSchema = z51.object({
17923
18027
  */
17924
18028
  changePct: gaOverviewChangeSchema,
17925
18029
  /** One entry per day of `current`, oldest first. */
17926
- days: z51.array(gaOverviewDaySchema),
18030
+ days: z52.array(gaOverviewDaySchema),
17927
18031
  /** When GA4 last reported this window's totals. Null when it never has. */
17928
- syncedAt: z51.string().nullable()
18032
+ syncedAt: z52.string().nullable()
17929
18033
  });
17930
18034
  var GaOverviewUnits = { count: "count", fraction: "fraction", seconds: "seconds" };
17931
18035
  var GaOverviewBetter = { higher: "higher", lower: "lower" };
@@ -17964,128 +18068,141 @@ var gaTrafficResponseSchema = ga4TrafficSummaryDtoSchema.extend({
17964
18068
  * Read every share against this window. The share fields divide a channel
17965
18069
  * count by `totalSessions`; both come from these dates and only these dates.
17966
18070
  */
17967
- windowStart: z51.string().nullable(),
18071
+ windowStart: z52.string().nullable(),
17968
18072
  /** Inclusive end (YYYY-MM-DD) of the measured window. `null` when open-ended. */
17969
- windowEnd: z51.string().nullable(),
18073
+ windowEnd: z52.string().nullable(),
17970
18074
  /**
17971
18075
  * Calendar days the measured window covers, counting both ends. `null` when
17972
18076
  * either bound is open — an unknown span is reported as unknown rather than
17973
18077
  * guessed.
17974
18078
  */
17975
- windowDays: z51.number().nullable(),
18079
+ windowDays: z52.number().nullable(),
17976
18080
  /** Alias of `windowStart`, retained for callers that predate it. */
17977
- periodStart: z51.string().nullable(),
18081
+ periodStart: z52.string().nullable(),
17978
18082
  /** Alias of `windowEnd`, retained for callers that predate it. */
17979
- periodEnd: z51.string().nullable()
18083
+ periodEnd: z52.string().nullable()
17980
18084
  });
17981
- var ga4AiReferralHistoryEntrySchema = z51.object({
17982
- date: z51.string(),
17983
- source: z51.string(),
17984
- medium: z51.string(),
18085
+ var ga4AiReferralHistoryEntrySchema = z52.object({
18086
+ date: z52.string(),
18087
+ source: z52.string(),
18088
+ medium: z52.string(),
17985
18089
  trafficClass: aiReferralTrafficClassSchema,
17986
- landingPage: z51.string(),
17987
- sessions: z51.number(),
18090
+ landingPage: z52.string(),
18091
+ sessions: z52.number(),
17988
18092
  /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.180.6. */
17989
- users: z51.number().optional(),
18093
+ users: z52.number().optional(),
17990
18094
  /** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
17991
18095
  sourceDimension: ga4SourceDimensionSchema
17992
18096
  });
17993
- var ga4AiReferralDailySourceSchema = z51.object({
17994
- source: z51.string(),
17995
- sessions: z51.number(),
17996
- paidSessions: z51.number(),
17997
- organicSessions: z51.number()
17998
- });
17999
- var ga4AiReferralDailyEntrySchema = z51.object({
18000
- date: z51.string(),
18001
- sessions: z51.number(),
18002
- paidSessions: z51.number(),
18003
- organicSessions: z51.number(),
18097
+ var ga4AiReferralDailySourceSchema = z52.object({
18098
+ source: z52.string(),
18099
+ sessions: z52.number(),
18100
+ paidSessions: z52.number(),
18101
+ organicSessions: z52.number()
18102
+ });
18103
+ var ga4AiReferralDailyEntrySchema = z52.object({
18104
+ date: z52.string(),
18105
+ sessions: z52.number(),
18106
+ paidSessions: z52.number(),
18107
+ organicSessions: z52.number(),
18004
18108
  /** Per-source split of the day. `sessions` is the sum of these. */
18005
- bySource: z51.array(ga4AiReferralDailySourceSchema)
18109
+ bySource: z52.array(ga4AiReferralDailySourceSchema)
18006
18110
  });
18007
- var ga4AiReferralDailyDtoSchema = z51.object({
18111
+ var ga4AiReferralDailyTrendSchema = linearTrendSchema.extend({
18112
+ startDate: z52.string(),
18113
+ endDate: z52.string()
18114
+ });
18115
+ var ga4AiReferralDailyDtoSchema = z52.object({
18008
18116
  /** Ascending by date. Dates with no AI referrals are absent, not zero-filled. */
18009
- days: z51.array(ga4AiReferralDailyEntrySchema),
18117
+ days: z52.array(ga4AiReferralDailyEntrySchema),
18010
18118
  /** Sources present in the window, ordered by total sessions descending. */
18011
- sources: z51.array(z51.string()),
18012
- totalSessions: z51.number(),
18013
- totalPaidSessions: z51.number(),
18014
- totalOrganicSessions: z51.number(),
18119
+ sources: z52.array(z52.string()),
18120
+ totalSessions: z52.number(),
18121
+ totalPaidSessions: z52.number(),
18122
+ totalOrganicSessions: z52.number(),
18015
18123
  /**
18016
18124
  * The same days by AI engine: the five largest engines (the unattributed
18017
18125
  * group counts as one), then the rest as `other`. Folded from the same
18018
18126
  * winner set as `days`, so each day's values add up to that day's sessions.
18019
18127
  */
18020
- byEngine: stackedDailySeriesSchema
18021
- });
18022
- var ga4SocialReferralHistoryEntrySchema = z51.object({
18023
- date: z51.string(),
18024
- source: z51.string(),
18025
- medium: z51.string(),
18026
- sessions: z51.number(),
18027
- users: z51.number(),
18128
+ byEngine: stackedDailySeriesSchema,
18129
+ /**
18130
+ * Fitted over the window's complete GA4 days, server side so every consumer
18131
+ * draws the same line. A day counts once GA4 reported the site's daily total
18132
+ * for it, so a date with no AI row is a measured zero; days before the
18133
+ * project's first stored AI row, and the last two UTC days (still filling),
18134
+ * are left out. Null with fewer than two such days. Optional so a newer
18135
+ * client reads an older API.
18136
+ */
18137
+ trend: ga4AiReferralDailyTrendSchema.nullable().optional()
18138
+ });
18139
+ var ga4SocialReferralHistoryEntrySchema = z52.object({
18140
+ date: z52.string(),
18141
+ source: z52.string(),
18142
+ medium: z52.string(),
18143
+ sessions: z52.number(),
18144
+ users: z52.number(),
18028
18145
  /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
18029
- channelGroup: z51.string()
18146
+ channelGroup: z52.string()
18030
18147
  });
18031
- var ga4SessionHistoryEntrySchema = z51.object({
18032
- date: z51.string(),
18033
- sessions: z51.number(),
18034
- organicSessions: z51.number(),
18148
+ var ga4SessionHistoryEntrySchema = z52.object({
18149
+ date: z52.string(),
18150
+ sessions: z52.number(),
18151
+ organicSessions: z52.number(),
18035
18152
  /**
18036
18153
  * Unique visitors for the day. Deduplicated by GA when `usersSource` is
18037
18154
  * `deduplicated`; a landing-page sum (which overcounts multi-page visitors)
18038
18155
  * for days synced before per-day totals were captured.
18039
18156
  */
18040
- users: z51.number(),
18157
+ users: z52.number(),
18041
18158
  /**
18042
18159
  * How `users` was derived. `deduplicated` matches the GA UI's active users;
18043
18160
  * `landing-page-sum` is the legacy overcount kept so historical days still
18044
18161
  * render. Never compare the two across a series without saying which is which.
18045
18162
  */
18046
- usersSource: z51.enum(["deduplicated", "landing-page-sum"])
18163
+ usersSource: z52.enum(["deduplicated", "landing-page-sum"])
18047
18164
  });
18048
- var gaAcquisitionDimensionSchema = z51.enum(["date", "channelGroup", "source", "medium", "hostName", "landingPage"]);
18165
+ var gaAcquisitionDimensionSchema = z52.enum(["date", "channelGroup", "source", "medium", "hostName", "landingPage"]);
18049
18166
  var GaAcquisitionDimensions = gaAcquisitionDimensionSchema.enum;
18050
- var gaAcquisitionQuerySchema = z51.object({
18167
+ var gaAcquisitionQuerySchema = z52.object({
18051
18168
  window: metricsWindowSchema.optional(),
18052
- startDate: z51.iso.date().optional(),
18053
- endDate: z51.iso.date().optional(),
18054
- channel: z51.string().optional(),
18055
- source: z51.string().optional(),
18056
- medium: z51.string().optional(),
18057
- hostName: z51.string().optional(),
18058
- landingPage: z51.string().optional(),
18059
- pathPrefix: z51.string().optional(),
18060
- groupBy: z51.string().refine((value) => value.split(",").every((key) => gaAcquisitionDimensionSchema.safeParse(key.trim()).success), `groupBy must contain: ${gaAcquisitionDimensionSchema.options.join(", ")}`).optional(),
18061
- limit: z51.number().int().min(1).max(1e3).optional(),
18062
- offset: z51.number().int().nonnegative().optional()
18063
- }).strict();
18064
- var gaAcquisitionRowSchema = z51.object({
18065
- date: z51.string().optional(),
18066
- channelGroup: z51.string().optional(),
18067
- source: z51.string().optional(),
18068
- medium: z51.string().optional(),
18069
- hostName: z51.string().optional(),
18070
- landingPage: z51.string().optional(),
18071
- sessions: z51.number().int().nonnegative()
18072
- });
18073
- var gaAcquisitionResponseSchema = z51.object({
18074
- source: z51.literal("ga4-acquisition"),
18169
+ startDate: z52.iso.date().optional(),
18170
+ endDate: z52.iso.date().optional(),
18171
+ channel: z52.string().optional(),
18172
+ source: z52.string().optional(),
18173
+ medium: z52.string().optional(),
18174
+ hostName: z52.string().optional(),
18175
+ landingPage: z52.string().optional(),
18176
+ pathPrefix: z52.string().optional(),
18177
+ groupBy: z52.string().refine((value) => value.split(",").every((key) => gaAcquisitionDimensionSchema.safeParse(key.trim()).success), `groupBy must contain: ${gaAcquisitionDimensionSchema.options.join(", ")}`).optional(),
18178
+ limit: z52.number().int().min(1).max(1e3).optional(),
18179
+ offset: z52.number().int().nonnegative().optional()
18180
+ }).strict();
18181
+ var gaAcquisitionRowSchema = z52.object({
18182
+ date: z52.string().optional(),
18183
+ channelGroup: z52.string().optional(),
18184
+ source: z52.string().optional(),
18185
+ medium: z52.string().optional(),
18186
+ hostName: z52.string().optional(),
18187
+ landingPage: z52.string().optional(),
18188
+ sessions: z52.number().int().nonnegative()
18189
+ });
18190
+ var gaAcquisitionResponseSchema = z52.object({
18191
+ source: z52.literal("ga4-acquisition"),
18075
18192
  status: gaMeasurementComponentStatusSchema,
18076
- error: z51.string().nullable(),
18077
- syncedAt: z51.string().nullable(),
18078
- scope: z51.object({ project: z51.string(), attribution: z51.literal("project-only"), unavailableDimensions: z51.array(z51.enum(["property", "target", "market"])) }),
18079
- window: z51.object({ startDate: z51.string().nullable(), endDate: z51.string().nullable(), window: metricsWindowSchema }),
18080
- coverage: z51.object({ earliestDate: z51.string().nullable(), latestDate: z51.string().nullable() }),
18081
- filters: z51.object({ channel: z51.string().nullable(), source: z51.string().nullable(), medium: z51.string().nullable(), hostName: z51.string().nullable(), landingPage: z51.string().nullable(), pathPrefix: z51.string().nullable() }),
18082
- groupBy: z51.array(gaAcquisitionDimensionSchema),
18083
- channelGroups: z51.array(z51.string()),
18084
- totalSessions: z51.number().int().nonnegative(),
18085
- totalRows: z51.number().int().nonnegative(),
18086
- limit: z51.number().int(),
18087
- offset: z51.number().int(),
18088
- rows: z51.array(gaAcquisitionRowSchema)
18193
+ error: z52.string().nullable(),
18194
+ syncedAt: z52.string().nullable(),
18195
+ scope: z52.object({ project: z52.string(), attribution: z52.literal("project-only"), unavailableDimensions: z52.array(z52.enum(["property", "target", "market"])) }),
18196
+ window: z52.object({ startDate: z52.string().nullable(), endDate: z52.string().nullable(), window: metricsWindowSchema }),
18197
+ coverage: z52.object({ earliestDate: z52.string().nullable(), latestDate: z52.string().nullable() }),
18198
+ filters: z52.object({ channel: z52.string().nullable(), source: z52.string().nullable(), medium: z52.string().nullable(), hostName: z52.string().nullable(), landingPage: z52.string().nullable(), pathPrefix: z52.string().nullable() }),
18199
+ groupBy: z52.array(gaAcquisitionDimensionSchema),
18200
+ channelGroups: z52.array(z52.string()),
18201
+ totalSessions: z52.number().int().nonnegative(),
18202
+ totalRows: z52.number().int().nonnegative(),
18203
+ limit: z52.number().int(),
18204
+ offset: z52.number().int(),
18205
+ rows: z52.array(gaAcquisitionRowSchema)
18089
18206
  });
18090
18207
  function formatGaOverviewValue(key, value, options = {}) {
18091
18208
  if (value === null) return "\u2014";
@@ -18168,8 +18285,8 @@ function collectBrandAliasesFromDomain(domain) {
18168
18285
  }
18169
18286
 
18170
18287
  // ../contracts/src/backlinks.ts
18171
- import { z as z52 } from "zod";
18172
- var backlinkSourceSchema = z52.enum(["commoncrawl", "bing-webmaster"]);
18288
+ import { z as z53 } from "zod";
18289
+ var backlinkSourceSchema = z53.enum(["commoncrawl", "bing-webmaster"]);
18173
18290
  var BacklinkSources = backlinkSourceSchema.enum;
18174
18291
  function computeBacklinkSummaryMetrics(rows) {
18175
18292
  if (rows.length === 0) {
@@ -18185,108 +18302,108 @@ function computeBacklinkSummaryMetrics(rows) {
18185
18302
  top10HostsShare: share.toFixed(6)
18186
18303
  };
18187
18304
  }
18188
- var ccReleaseSyncStatusSchema = z52.enum(["queued", "downloading", "querying", "ready", "failed"]);
18305
+ var ccReleaseSyncStatusSchema = z53.enum(["queued", "downloading", "querying", "ready", "failed"]);
18189
18306
  var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
18190
- var ccReleaseSyncDtoSchema = z52.object({
18191
- id: z52.string(),
18192
- release: z52.string(),
18307
+ var ccReleaseSyncDtoSchema = z53.object({
18308
+ id: z53.string(),
18309
+ release: z53.string(),
18193
18310
  status: ccReleaseSyncStatusSchema,
18194
- phaseDetail: z52.string().nullable().optional(),
18195
- vertexPath: z52.string().nullable().optional(),
18196
- edgesPath: z52.string().nullable().optional(),
18197
- vertexSha256: z52.string().nullable().optional(),
18198
- edgesSha256: z52.string().nullable().optional(),
18199
- vertexBytes: z52.number().int().nullable().optional(),
18200
- edgesBytes: z52.number().int().nullable().optional(),
18201
- projectsProcessed: z52.number().int().nullable().optional(),
18202
- domainsDiscovered: z52.number().int().nullable().optional(),
18203
- downloadStartedAt: z52.string().nullable().optional(),
18204
- downloadFinishedAt: z52.string().nullable().optional(),
18205
- queryStartedAt: z52.string().nullable().optional(),
18206
- queryFinishedAt: z52.string().nullable().optional(),
18207
- error: z52.string().nullable().optional(),
18208
- createdAt: z52.string(),
18209
- updatedAt: z52.string()
18311
+ phaseDetail: z53.string().nullable().optional(),
18312
+ vertexPath: z53.string().nullable().optional(),
18313
+ edgesPath: z53.string().nullable().optional(),
18314
+ vertexSha256: z53.string().nullable().optional(),
18315
+ edgesSha256: z53.string().nullable().optional(),
18316
+ vertexBytes: z53.number().int().nullable().optional(),
18317
+ edgesBytes: z53.number().int().nullable().optional(),
18318
+ projectsProcessed: z53.number().int().nullable().optional(),
18319
+ domainsDiscovered: z53.number().int().nullable().optional(),
18320
+ downloadStartedAt: z53.string().nullable().optional(),
18321
+ downloadFinishedAt: z53.string().nullable().optional(),
18322
+ queryStartedAt: z53.string().nullable().optional(),
18323
+ queryFinishedAt: z53.string().nullable().optional(),
18324
+ error: z53.string().nullable().optional(),
18325
+ createdAt: z53.string(),
18326
+ updatedAt: z53.string()
18210
18327
  });
18211
- var backlinkDomainDtoSchema = z52.object({
18212
- linkingDomain: z52.string(),
18213
- numHosts: z52.number().int(),
18328
+ var backlinkDomainDtoSchema = z53.object({
18329
+ linkingDomain: z53.string(),
18330
+ numHosts: z53.number().int(),
18214
18331
  source: backlinkSourceSchema
18215
18332
  });
18216
- var backlinkSummaryDtoSchema = z52.object({
18217
- projectId: z52.string(),
18218
- release: z52.string(),
18219
- targetDomain: z52.string(),
18220
- totalLinkingDomains: z52.number().int(),
18221
- totalHosts: z52.number().int(),
18222
- top10HostsShare: z52.string(),
18223
- queriedAt: z52.string(),
18333
+ var backlinkSummaryDtoSchema = z53.object({
18334
+ projectId: z53.string(),
18335
+ release: z53.string(),
18336
+ targetDomain: z53.string(),
18337
+ totalLinkingDomains: z53.number().int(),
18338
+ totalHosts: z53.number().int(),
18339
+ top10HostsShare: z53.string(),
18340
+ queriedAt: z53.string(),
18224
18341
  source: backlinkSourceSchema,
18225
18342
  // Populated when the response is filtered (e.g. ?excludeCrawlers=1).
18226
18343
  // Counts the rows omitted from totalLinkingDomains/totalHosts so callers
18227
18344
  // can show "N hidden" hints without re-deriving them.
18228
- excludedLinkingDomains: z52.number().int().optional(),
18229
- excludedHosts: z52.number().int().optional()
18345
+ excludedLinkingDomains: z53.number().int().optional(),
18346
+ excludedHosts: z53.number().int().optional()
18230
18347
  });
18231
- var backlinkListResponseSchema = z52.object({
18348
+ var backlinkListResponseSchema = z53.object({
18232
18349
  source: backlinkSourceSchema,
18233
18350
  summary: backlinkSummaryDtoSchema.nullable(),
18234
- total: z52.number().int(),
18235
- rows: z52.array(backlinkDomainDtoSchema)
18236
- });
18237
- var backlinkHistoryEntrySchema = z52.object({
18238
- release: z52.string(),
18239
- totalLinkingDomains: z52.number().int(),
18240
- totalHosts: z52.number().int(),
18241
- top10HostsShare: z52.string(),
18242
- queriedAt: z52.string(),
18351
+ total: z53.number().int(),
18352
+ rows: z53.array(backlinkDomainDtoSchema)
18353
+ });
18354
+ var backlinkHistoryEntrySchema = z53.object({
18355
+ release: z53.string(),
18356
+ totalLinkingDomains: z53.number().int(),
18357
+ totalHosts: z53.number().int(),
18358
+ top10HostsShare: z53.string(),
18359
+ queriedAt: z53.string(),
18243
18360
  source: backlinkSourceSchema
18244
18361
  });
18245
- var backlinkSourceAvailabilityDtoSchema = z52.object({
18362
+ var backlinkSourceAvailabilityDtoSchema = z53.object({
18246
18363
  source: backlinkSourceSchema,
18247
- connected: z52.boolean(),
18248
- hasData: z52.boolean(),
18249
- latestRelease: z52.string().nullable(),
18250
- totalLinkingDomains: z52.number().int(),
18251
- lastSyncedAt: z52.string().nullable()
18252
- });
18253
- var backlinkSourcesResponseSchema = z52.object({
18254
- projectId: z52.string(),
18255
- targetDomain: z52.string(),
18256
- sources: z52.array(backlinkSourceAvailabilityDtoSchema),
18257
- anyConnected: z52.boolean(),
18258
- anyData: z52.boolean()
18259
- });
18260
- var backlinksInstallStatusDtoSchema = z52.object({
18261
- duckdbInstalled: z52.boolean(),
18262
- duckdbVersion: z52.string().nullable().optional(),
18263
- duckdbSpec: z52.string(),
18264
- pluginDir: z52.string()
18265
- });
18266
- var backlinksInstallResultDtoSchema = z52.object({
18267
- installed: z52.boolean(),
18268
- version: z52.string(),
18269
- path: z52.string(),
18270
- alreadyPresent: z52.boolean()
18271
- });
18272
- var ccAvailableReleaseSchema = z52.object({
18273
- release: z52.string(),
18274
- vertexUrl: z52.string(),
18275
- edgesUrl: z52.string(),
18276
- vertexBytes: z52.number().int().nullable(),
18277
- edgesBytes: z52.number().int().nullable(),
18278
- lastModified: z52.string().nullable()
18279
- });
18280
- var ccCachedReleaseSchema = z52.object({
18281
- release: z52.string(),
18364
+ connected: z53.boolean(),
18365
+ hasData: z53.boolean(),
18366
+ latestRelease: z53.string().nullable(),
18367
+ totalLinkingDomains: z53.number().int(),
18368
+ lastSyncedAt: z53.string().nullable()
18369
+ });
18370
+ var backlinkSourcesResponseSchema = z53.object({
18371
+ projectId: z53.string(),
18372
+ targetDomain: z53.string(),
18373
+ sources: z53.array(backlinkSourceAvailabilityDtoSchema),
18374
+ anyConnected: z53.boolean(),
18375
+ anyData: z53.boolean()
18376
+ });
18377
+ var backlinksInstallStatusDtoSchema = z53.object({
18378
+ duckdbInstalled: z53.boolean(),
18379
+ duckdbVersion: z53.string().nullable().optional(),
18380
+ duckdbSpec: z53.string(),
18381
+ pluginDir: z53.string()
18382
+ });
18383
+ var backlinksInstallResultDtoSchema = z53.object({
18384
+ installed: z53.boolean(),
18385
+ version: z53.string(),
18386
+ path: z53.string(),
18387
+ alreadyPresent: z53.boolean()
18388
+ });
18389
+ var ccAvailableReleaseSchema = z53.object({
18390
+ release: z53.string(),
18391
+ vertexUrl: z53.string(),
18392
+ edgesUrl: z53.string(),
18393
+ vertexBytes: z53.number().int().nullable(),
18394
+ edgesBytes: z53.number().int().nullable(),
18395
+ lastModified: z53.string().nullable()
18396
+ });
18397
+ var ccCachedReleaseSchema = z53.object({
18398
+ release: z53.string(),
18282
18399
  syncStatus: ccReleaseSyncStatusSchema.nullable(),
18283
- bytes: z52.number().int(),
18284
- lastUsedAt: z52.string().nullable()
18400
+ bytes: z53.number().int(),
18401
+ lastUsedAt: z53.string().nullable()
18285
18402
  });
18286
18403
 
18287
18404
  // ../contracts/src/content.ts
18288
- import { z as z53 } from "zod";
18289
- var contentActionSchema = z53.enum(["create", "expand", "refresh", "add-schema"]);
18405
+ import { z as z54 } from "zod";
18406
+ var contentActionSchema = z54.enum(["create", "expand", "refresh", "add-schema"]);
18290
18407
  var ContentActions = contentActionSchema.enum;
18291
18408
  function contentActionLabel(action) {
18292
18409
  switch (action) {
@@ -18300,9 +18417,9 @@ function contentActionLabel(action) {
18300
18417
  return "Add schema";
18301
18418
  }
18302
18419
  }
18303
- var demandSourceSchema = z53.enum(["gsc", "competitor-evidence", "both"]);
18420
+ var demandSourceSchema = z54.enum(["gsc", "competitor-evidence", "both"]);
18304
18421
  var DemandSources = demandSourceSchema.enum;
18305
- var actionConfidenceSchema = z53.enum(["high", "medium", "low"]);
18422
+ var actionConfidenceSchema = z54.enum(["high", "medium", "low"]);
18306
18423
  var ActionConfidences = actionConfidenceSchema.enum;
18307
18424
  function actionConfidenceLabel(confidence) {
18308
18425
  switch (confidence) {
@@ -18314,7 +18431,7 @@ function actionConfidenceLabel(confidence) {
18314
18431
  return "Low";
18315
18432
  }
18316
18433
  }
18317
- var pageTypeSchema = z53.enum([
18434
+ var pageTypeSchema = z54.enum([
18318
18435
  "blog-post",
18319
18436
  "comparison",
18320
18437
  "listicle",
@@ -18323,7 +18440,7 @@ var pageTypeSchema = z53.enum([
18323
18440
  "glossary"
18324
18441
  ]);
18325
18442
  var PageTypes = pageTypeSchema.enum;
18326
- var contentActionStateSchema = z53.enum([
18443
+ var contentActionStateSchema = z54.enum([
18327
18444
  "proposed",
18328
18445
  "briefed",
18329
18446
  "payload-generated",
@@ -18333,7 +18450,7 @@ var contentActionStateSchema = z53.enum([
18333
18450
  "dismissed"
18334
18451
  ]);
18335
18452
  var ContentActionStates = contentActionStateSchema.enum;
18336
- var winnabilityClassSchema = z53.enum(["ownable", "ceded"]);
18453
+ var winnabilityClassSchema = z54.enum(["ownable", "ceded"]);
18337
18454
  var WinnabilityClasses = winnabilityClassSchema.enum;
18338
18455
  function winnabilityClassLabel(winnabilityClass) {
18339
18456
  switch (winnabilityClass) {
@@ -18368,40 +18485,40 @@ function deriveWinnabilityClass(citedSurfaceDomains, surfaceClasses, threshold =
18368
18485
  winnability
18369
18486
  };
18370
18487
  }
18371
- var ourBestPageSchema = z53.object({
18372
- url: z53.string(),
18373
- gscImpressions: z53.number().nonnegative(),
18374
- gscClicks: z53.number().nonnegative(),
18488
+ var ourBestPageSchema = z54.object({
18489
+ url: z54.string(),
18490
+ gscImpressions: z54.number().nonnegative(),
18491
+ gscClicks: z54.number().nonnegative(),
18375
18492
  // Null when the page came from the inventory fallback (no GSC ranking data).
18376
- gscAvgPosition: z53.number().nonnegative().nullable(),
18377
- organicSessions: z53.number().nonnegative()
18378
- });
18379
- var winningCompetitorSchema = z53.object({
18380
- domain: z53.string(),
18381
- url: z53.string(),
18382
- title: z53.string(),
18383
- citationCount: z53.number().int().nonnegative()
18384
- });
18385
- var scoreBreakdownSchema = z53.object({
18386
- demand: z53.number(),
18387
- competitor: z53.number(),
18388
- absence: z53.number(),
18389
- gapSeverity: z53.number()
18390
- });
18391
- var existingActionRefSchema = z53.object({
18392
- actionId: z53.string(),
18493
+ gscAvgPosition: z54.number().nonnegative().nullable(),
18494
+ organicSessions: z54.number().nonnegative()
18495
+ });
18496
+ var winningCompetitorSchema = z54.object({
18497
+ domain: z54.string(),
18498
+ url: z54.string(),
18499
+ title: z54.string(),
18500
+ citationCount: z54.number().int().nonnegative()
18501
+ });
18502
+ var scoreBreakdownSchema = z54.object({
18503
+ demand: z54.number(),
18504
+ competitor: z54.number(),
18505
+ absence: z54.number(),
18506
+ gapSeverity: z54.number()
18507
+ });
18508
+ var existingActionRefSchema = z54.object({
18509
+ actionId: z54.string(),
18393
18510
  state: contentActionStateSchema,
18394
- lastUpdated: z53.string()
18511
+ lastUpdated: z54.string()
18395
18512
  });
18396
- var contentTargetRowDtoSchema = z53.object({
18397
- targetRef: z53.string(),
18398
- query: z53.string(),
18513
+ var contentTargetRowDtoSchema = z54.object({
18514
+ targetRef: z54.string(),
18515
+ query: z54.string(),
18399
18516
  action: contentActionSchema,
18400
18517
  ourBestPage: ourBestPageSchema.nullable(),
18401
18518
  winningCompetitor: winningCompetitorSchema.nullable(),
18402
- score: z53.number(),
18519
+ score: z54.number(),
18403
18520
  scoreBreakdown: scoreBreakdownSchema,
18404
- drivers: z53.array(z53.string()),
18521
+ drivers: z54.array(z54.string()),
18405
18522
  demandSource: demandSourceSchema,
18406
18523
  actionConfidence: actionConfidenceSchema,
18407
18524
  existingAction: existingActionRefSchema.nullable(),
@@ -18416,134 +18533,134 @@ var contentTargetRowDtoSchema = z53.object({
18416
18533
  * `[0, 1]`. `null` when the gate failed open (no classification coverage for
18417
18534
  * the cited surface) — distinct from a computed `1.0`.
18418
18535
  */
18419
- winnability: fraction(z53.number().min(0).max(1)).nullable()
18420
- });
18421
- var contentTargetsResponseDtoSchema = z53.object({
18422
- targets: z53.array(contentTargetRowDtoSchema),
18423
- contextMetrics: z53.object({
18424
- totalAiReferralSessions: z53.number().int().nonnegative(),
18425
- latestRunId: z53.string(),
18426
- runTimestamp: z53.string()
18536
+ winnability: fraction(z54.number().min(0).max(1)).nullable()
18537
+ });
18538
+ var contentTargetsResponseDtoSchema = z54.object({
18539
+ targets: z54.array(contentTargetRowDtoSchema),
18540
+ contextMetrics: z54.object({
18541
+ totalAiReferralSessions: z54.number().int().nonnegative(),
18542
+ latestRunId: z54.string(),
18543
+ runTimestamp: z54.string()
18427
18544
  })
18428
18545
  });
18429
- var contentTargetDismissalDtoSchema = z53.object({
18430
- targetRef: z53.string(),
18431
- addressedUrl: z53.string().nullable(),
18432
- note: z53.string().nullable(),
18433
- dismissedAt: z53.string()
18546
+ var contentTargetDismissalDtoSchema = z54.object({
18547
+ targetRef: z54.string(),
18548
+ addressedUrl: z54.string().nullable(),
18549
+ note: z54.string().nullable(),
18550
+ dismissedAt: z54.string()
18434
18551
  });
18435
- var contentTargetDismissalsResponseDtoSchema = z53.object({
18436
- dismissals: z53.array(contentTargetDismissalDtoSchema)
18552
+ var contentTargetDismissalsResponseDtoSchema = z54.object({
18553
+ dismissals: z54.array(contentTargetDismissalDtoSchema)
18437
18554
  });
18438
- var contentTargetDismissRequestSchema = z53.object({
18439
- targetRef: z53.string().min(1),
18555
+ var contentTargetDismissRequestSchema = z54.object({
18556
+ targetRef: z54.string().min(1),
18440
18557
  /** URL of the page the user wrote that addresses this recommendation. Stored verbatim for the audit trail; not currently used to suppress the slug-token matcher. */
18441
- addressedUrl: z53.string().url().optional(),
18558
+ addressedUrl: z54.string().url().optional(),
18442
18559
  /** Free-form note (e.g. "covered in our Q1 content sprint"). 500 char cap is the API surface limit; the DB column is unbounded. */
18443
- note: z53.string().max(500).optional()
18560
+ note: z54.string().max(500).optional()
18444
18561
  });
18445
- var recommendationExplanationDtoSchema = z53.object({
18446
- targetRef: z53.string(),
18562
+ var recommendationExplanationDtoSchema = z54.object({
18563
+ targetRef: z54.string(),
18447
18564
  /** Version of the prompt template used. Bumping the version invalidates the cache forward without touching the table. */
18448
- promptVersion: z53.string(),
18565
+ promptVersion: z54.string(),
18449
18566
  /** Provider that produced the explanation (e.g. "claude", "gemini"). */
18450
- provider: z53.string(),
18567
+ provider: z54.string(),
18451
18568
  /** Model id within that provider (e.g. "claude-sonnet-4-6"). */
18452
- model: z53.string(),
18569
+ model: z54.string(),
18453
18570
  /** Markdown-formatted rationale + recommended next steps. */
18454
- responseText: z53.string(),
18571
+ responseText: z54.string(),
18455
18572
  /** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
18456
- costMillicents: z53.number().int().nonnegative(),
18457
- generatedAt: z53.string()
18573
+ costMillicents: z54.number().int().nonnegative(),
18574
+ generatedAt: z54.string()
18458
18575
  });
18459
- var recommendationExplainRequestSchema = z53.object({
18576
+ var recommendationExplainRequestSchema = z54.object({
18460
18577
  /**
18461
18578
  * Optional provider override (e.g. "claude" to force Claude even if
18462
18579
  * the project's default is Gemini). Falls through to project default
18463
18580
  * → auto-detect when omitted.
18464
18581
  */
18465
- provider: z53.string().optional(),
18582
+ provider: z54.string().optional(),
18466
18583
  /**
18467
18584
  * Optional model override within the chosen provider. Falls through to
18468
18585
  * the `analyze`-tier default model when omitted.
18469
18586
  */
18470
- model: z53.string().optional(),
18587
+ model: z54.string().optional(),
18471
18588
  /**
18472
18589
  * Force a fresh LLM call even if a cached explanation exists for the
18473
18590
  * current prompt version. Use sparingly — defeats the cache.
18474
18591
  */
18475
- forceRefresh: z53.boolean().optional()
18592
+ forceRefresh: z54.boolean().optional()
18476
18593
  });
18477
- var contentBriefDtoSchema = z53.object({
18594
+ var contentBriefDtoSchema = z54.object({
18478
18595
  /** The query the brief is for (echoed from the recommendation). */
18479
- targetQuery: z53.string().trim().min(1),
18596
+ targetQuery: z54.string().trim().min(1),
18480
18597
  /** Always `ownable` in practice — the gate rejects `ceded` before synthesis. */
18481
18598
  winnabilityClass: winnabilityClassSchema,
18482
18599
  /** The differentiated content angle to take. */
18483
- angle: z53.string().trim().min(1),
18600
+ angle: z54.string().trim().min(1),
18484
18601
  /** Why this query is winnable, citing the cited-surface signal. */
18485
- whyWinnable: z53.string().trim().min(1),
18602
+ whyWinnable: z54.string().trim().min(1),
18486
18603
  /** The schema.org type or markup to add or extend. */
18487
- schemaHookup: z53.string().trim().min(1),
18604
+ schemaHookup: z54.string().trim().min(1),
18488
18605
  /** Why the cited surface is controllable (the ownable-vs-ceded reasoning). */
18489
- controllableSurfaceRationale: z53.string().trim().min(1)
18606
+ controllableSurfaceRationale: z54.string().trim().min(1)
18490
18607
  });
18491
- var recommendationBriefDtoSchema = z53.object({
18492
- targetRef: z53.string(),
18608
+ var recommendationBriefDtoSchema = z54.object({
18609
+ targetRef: z54.string(),
18493
18610
  /** Version of the brief prompt template; bumping invalidates the cache forward. */
18494
- promptVersion: z53.string(),
18495
- provider: z53.string(),
18496
- model: z53.string(),
18611
+ promptVersion: z54.string(),
18612
+ provider: z54.string(),
18613
+ model: z54.string(),
18497
18614
  brief: contentBriefDtoSchema,
18498
18615
  /** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
18499
- costMillicents: z53.number().int().nonnegative(),
18500
- generatedAt: z53.string()
18616
+ costMillicents: z54.number().int().nonnegative(),
18617
+ generatedAt: z54.string()
18501
18618
  });
18502
- var domainClassificationDtoSchema = z53.object({
18503
- domain: z53.string(),
18619
+ var domainClassificationDtoSchema = z54.object({
18620
+ domain: z54.string(),
18504
18621
  competitorType: discoveryCompetitorTypeSchema,
18505
- hits: z53.number().int().nonnegative(),
18506
- updatedAt: z53.string()
18622
+ hits: z54.number().int().nonnegative(),
18623
+ updatedAt: z54.string()
18507
18624
  });
18508
- var domainClassificationsResponseDtoSchema = z53.object({
18509
- classifications: z53.array(domainClassificationDtoSchema)
18510
- });
18511
- var contentGroundingSourceSchema = z53.object({
18512
- uri: z53.string(),
18513
- title: z53.string(),
18514
- domain: z53.string(),
18515
- isOurDomain: z53.boolean(),
18516
- isCompetitor: z53.boolean(),
18517
- citationCount: z53.number().int().nonnegative(),
18518
- providers: z53.array(providerNameSchema)
18519
- });
18520
- var contentSourceRowDtoSchema = z53.object({
18521
- query: z53.string(),
18522
- groundingSources: z53.array(contentGroundingSourceSchema)
18523
- });
18524
- var contentSourcesResponseDtoSchema = z53.object({
18525
- sources: z53.array(contentSourceRowDtoSchema),
18526
- latestRunId: z53.string()
18527
- });
18528
- var contentGapRowDtoSchema = z53.object({
18529
- query: z53.string(),
18530
- competitorDomains: z53.array(z53.string()),
18531
- competitorCount: z53.number().int().nonnegative(),
18532
- missRate: fraction(z53.number().min(0).max(1)),
18533
- lastSeenInRunId: z53.string()
18534
- });
18535
- var contentGapsResponseDtoSchema = z53.object({
18536
- gaps: z53.array(contentGapRowDtoSchema),
18537
- latestRunId: z53.string()
18625
+ var domainClassificationsResponseDtoSchema = z54.object({
18626
+ classifications: z54.array(domainClassificationDtoSchema)
18627
+ });
18628
+ var contentGroundingSourceSchema = z54.object({
18629
+ uri: z54.string(),
18630
+ title: z54.string(),
18631
+ domain: z54.string(),
18632
+ isOurDomain: z54.boolean(),
18633
+ isCompetitor: z54.boolean(),
18634
+ citationCount: z54.number().int().nonnegative(),
18635
+ providers: z54.array(providerNameSchema)
18636
+ });
18637
+ var contentSourceRowDtoSchema = z54.object({
18638
+ query: z54.string(),
18639
+ groundingSources: z54.array(contentGroundingSourceSchema)
18640
+ });
18641
+ var contentSourcesResponseDtoSchema = z54.object({
18642
+ sources: z54.array(contentSourceRowDtoSchema),
18643
+ latestRunId: z54.string()
18644
+ });
18645
+ var contentGapRowDtoSchema = z54.object({
18646
+ query: z54.string(),
18647
+ competitorDomains: z54.array(z54.string()),
18648
+ competitorCount: z54.number().int().nonnegative(),
18649
+ missRate: fraction(z54.number().min(0).max(1)),
18650
+ lastSeenInRunId: z54.string()
18651
+ });
18652
+ var contentGapsResponseDtoSchema = z54.object({
18653
+ gaps: z54.array(contentGapRowDtoSchema),
18654
+ latestRunId: z54.string()
18538
18655
  });
18539
18656
 
18540
18657
  // ../contracts/src/doctor.ts
18541
- import { z as z54 } from "zod";
18542
- var checkStatusSchema = z54.enum(["ok", "warn", "fail", "skipped"]);
18658
+ import { z as z55 } from "zod";
18659
+ var checkStatusSchema = z55.enum(["ok", "warn", "fail", "skipped"]);
18543
18660
  var CheckStatuses = checkStatusSchema.enum;
18544
- var checkScopeSchema = z54.enum(["global", "project"]);
18661
+ var checkScopeSchema = z55.enum(["global", "project"]);
18545
18662
  var CheckScopes = checkScopeSchema.enum;
18546
- var checkCategorySchema = z54.enum([
18663
+ var checkCategorySchema = z55.enum([
18547
18664
  "auth",
18548
18665
  "config",
18549
18666
  "providers",
@@ -18555,9 +18672,9 @@ var checkCategorySchema = z54.enum([
18555
18672
  ]);
18556
18673
  var CheckCategories = checkCategorySchema.enum;
18557
18674
  var CHECK_RUNTIME_ERROR_SUFFIX = ".runtime-error";
18558
- var checkNotificationPolicySchema = z54.enum(["health", "silent"]);
18675
+ var checkNotificationPolicySchema = z55.enum(["health", "silent"]);
18559
18676
  var CheckNotificationPolicies = checkNotificationPolicySchema.enum;
18560
- var reportMonthSchema = z54.string().regex(/^[1-9]\d{3}-(0[1-9]|1[0-2])$/, "Expected YYYY-MM");
18677
+ var reportMonthSchema = z55.string().regex(/^[1-9]\d{3}-(0[1-9]|1[0-2])$/, "Expected YYYY-MM");
18561
18678
  function reportMonthsForDoctor(reportMonth, now = /* @__PURE__ */ new Date()) {
18562
18679
  if (reportMonth !== void 0) return [reportMonthSchema.parse(reportMonth)];
18563
18680
  const current = now.toISOString().slice(0, 7);
@@ -18569,40 +18686,40 @@ function gradedReportMonths(months, now = /* @__PURE__ */ new Date()) {
18569
18686
  const current = now.toISOString().slice(0, 7);
18570
18687
  return new Set(months.length > 1 ? months.filter((month) => month !== current) : months);
18571
18688
  }
18572
- var healthAlertCopySchema = z54.object({
18573
- label: z54.string().describe(`The problem in a few words, e.g. "Can't reach Google".`),
18574
- line: z54.string().describe("What happened, in one plain sentence."),
18575
- next: z54.string().nullable().describe("What to do, in one plain sentence; null when nothing needs doing yet.")
18689
+ var healthAlertCopySchema = z55.object({
18690
+ label: z55.string().describe(`The problem in a few words, e.g. "Can't reach Google".`),
18691
+ line: z55.string().describe("What happened, in one plain sentence."),
18692
+ next: z55.string().nullable().describe("What to do, in one plain sentence; null when nothing needs doing yet.")
18576
18693
  });
18577
- var checkResultSchema = z54.object({
18578
- id: z54.string(),
18694
+ var checkResultSchema = z55.object({
18695
+ id: z55.string(),
18579
18696
  category: checkCategorySchema,
18580
18697
  scope: checkScopeSchema,
18581
- title: z54.string(),
18698
+ title: z55.string(),
18582
18699
  status: checkStatusSchema,
18583
18700
  notificationPolicy: checkNotificationPolicySchema.optional().describe("Health checks page by default. Silent checks (report advisories and standing advisories) remain visible but never affect health alert state."),
18584
- code: z54.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
18585
- urgent: z54.boolean().optional().describe('True when this result is a problem a person has to act on now (the website is down, daily data stopped arriving, a sync keeps failing). Urgent results are what the health.urgent notification event reports. A result set aside under its failing cause (status "skipped", code "<id>.superseded") keeps the flag: the problem is still open.'),
18586
- summary: z54.string(),
18587
- remediation: z54.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
18588
- details: z54.record(z54.string(), z54.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
18701
+ code: z55.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
18702
+ urgent: z55.boolean().optional().describe('True when this result is a problem a person has to act on now (the website is down, daily data stopped arriving, a sync keeps failing). Urgent results are what the health.urgent notification event reports. A result set aside under its failing cause (status "skipped", code "<id>.superseded") keeps the flag: the problem is still open.'),
18703
+ summary: z55.string(),
18704
+ remediation: z55.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
18705
+ details: z55.record(z55.string(), z55.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
18589
18706
  alert: healthAlertCopySchema.optional().describe("Plain words a health alert shows for this result. Absent when the check has none; alerts then fall back to the summary."),
18590
- durationMs: z54.number().int().nonnegative().describe("How long the check took to execute.")
18707
+ durationMs: z55.number().int().nonnegative().describe("How long the check took to execute.")
18591
18708
  });
18592
- var doctorReportSchema = z54.object({
18709
+ var doctorReportSchema = z55.object({
18593
18710
  scope: checkScopeSchema,
18594
- project: z54.string().nullable().describe('Project name when scope is "project", null otherwise.'),
18595
- reportMonths: z54.array(reportMonthSchema).optional().describe("Calendar months evaluated by project report readiness checks."),
18596
- generatedAt: z54.string().describe("ISO-8601 timestamp when this doctor run started."),
18597
- durationMs: z54.number().int().nonnegative(),
18598
- summary: z54.object({
18599
- total: z54.number().int().nonnegative(),
18600
- ok: z54.number().int().nonnegative(),
18601
- warn: z54.number().int().nonnegative(),
18602
- fail: z54.number().int().nonnegative(),
18603
- skipped: z54.number().int().nonnegative()
18711
+ project: z55.string().nullable().describe('Project name when scope is "project", null otherwise.'),
18712
+ reportMonths: z55.array(reportMonthSchema).optional().describe("Calendar months evaluated by project report readiness checks."),
18713
+ generatedAt: z55.string().describe("ISO-8601 timestamp when this doctor run started."),
18714
+ durationMs: z55.number().int().nonnegative(),
18715
+ summary: z55.object({
18716
+ total: z55.number().int().nonnegative(),
18717
+ ok: z55.number().int().nonnegative(),
18718
+ warn: z55.number().int().nonnegative(),
18719
+ fail: z55.number().int().nonnegative(),
18720
+ skipped: z55.number().int().nonnegative()
18604
18721
  }),
18605
- checks: z54.array(checkResultSchema)
18722
+ checks: z55.array(checkResultSchema)
18606
18723
  });
18607
18724
  function summarizeCheckResults(results) {
18608
18725
  const summary = { total: results.length, ok: 0, warn: 0, fail: 0, skipped: 0 };
@@ -18635,8 +18752,8 @@ function groupIsoDateRanges(dates) {
18635
18752
  }
18636
18753
 
18637
18754
  // ../contracts/src/skills.ts
18638
- import { z as z55 } from "zod";
18639
- var codingAgentSchema = z55.enum(["claude", "codex"]);
18755
+ import { z as z56 } from "zod";
18756
+ var codingAgentSchema = z56.enum(["claude", "codex"]);
18640
18757
  var CodingAgents = codingAgentSchema.enum;
18641
18758
  function agentPluginClientLabel(client) {
18642
18759
  return client === "claude-code" ? "Claude Code" : "Codex";
@@ -18660,7 +18777,7 @@ function formatCanonryPluginUpdateHint(clients) {
18660
18777
  if (names.length === 1) return `Update or reinstall \`canonry@canonry\` in ${names[0]}.`;
18661
18778
  return `Update or reinstall \`canonry@canonry\` in ${joinPluginSubjects(names)}.`;
18662
18779
  }
18663
- var skillsClientSchema = z55.enum(["claude", "codex", "all"]);
18780
+ var skillsClientSchema = z56.enum(["claude", "codex", "all"]);
18664
18781
  var SkillsClients = skillsClientSchema.enum;
18665
18782
  var SKILL_MANIFEST_FILENAME = ".canonry-skill-manifest.json";
18666
18783
  function classifySkillFile(params) {
@@ -18677,108 +18794,6 @@ function coerceSkillManifest(parsed) {
18677
18794
  return null;
18678
18795
  }
18679
18796
 
18680
- // ../contracts/src/statistics.ts
18681
- import { z as z56 } from "zod";
18682
- function round(value, dp = 4) {
18683
- const f = 10 ** dp;
18684
- return (Math.round(value * f) + 0) / f;
18685
- }
18686
- function roundSignificant(value, sig = 6) {
18687
- if (!Number.isFinite(value) || value === 0) return 0;
18688
- const magnitude = Math.ceil(Math.log10(Math.abs(value)));
18689
- const factor = 10 ** (sig - magnitude);
18690
- return (Math.round(value * factor) + 0) / factor;
18691
- }
18692
- function shareOf(part, total) {
18693
- if (!Number.isFinite(part) || !Number.isFinite(total)) return null;
18694
- if (part <= 0) return 0;
18695
- return total > 0 ? part / total : null;
18696
- }
18697
- function breakdownShares(parts) {
18698
- const total = parts.reduce((sum, part) => sum + Math.max(0, part), 0);
18699
- return parts.map((part) => shareOf(part, total) ?? 0);
18700
- }
18701
- function median(values) {
18702
- const sorted = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
18703
- if (sorted.length === 0) return null;
18704
- const middle = Math.floor(sorted.length / 2);
18705
- return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
18706
- }
18707
- function wilsonInterval(successes, n, z97 = 1.96) {
18708
- if (!Number.isFinite(n) || n <= 0) return null;
18709
- const s = Math.max(0, Math.min(successes, n));
18710
- const p = s / n;
18711
- const z210 = z97 * z97;
18712
- const denom = 1 + z210 / n;
18713
- const center = (p + z210 / (2 * n)) / denom;
18714
- const margin = z97 / denom * Math.sqrt(p * (1 - p) / n + z210 / (4 * n * n));
18715
- return {
18716
- low: round(Math.max(0, center - margin)),
18717
- high: round(Math.min(1, center + margin))
18718
- };
18719
- }
18720
- var linearTrendSchema = z56.object({
18721
- /** Change in the fitted value per one-index step. */
18722
- slope: z56.number(),
18723
- /** Fitted value at index 0. */
18724
- intercept: z56.number(),
18725
- /** Coefficient of determination in [0, 1]. `1` for a series with no variance. */
18726
- r2: z56.number(),
18727
- /** Fitted value at the first index. */
18728
- start: z56.number(),
18729
- /** Fitted value at the last index. */
18730
- end: z56.number(),
18731
- /** Count of finite observations the fit used (not the series length). */
18732
- n: z56.number(),
18733
- /**
18734
- * Index of the first observation the fit used, and of the last.
18735
- *
18736
- * `start` / `end` are evaluated AT these indices, not at the series' own ends.
18737
- * A caller drawing the line must span exactly this range: extending it across
18738
- * leading or trailing indices the metric has no data for draws a fitted claim
18739
- * over dates that were never measured.
18740
- */
18741
- startIndex: z56.number(),
18742
- endIndex: z56.number()
18743
- });
18744
- function linearTrend(values) {
18745
- const points = [];
18746
- for (const [index, value] of values.entries()) {
18747
- if (typeof value === "number" && Number.isFinite(value)) points.push({ x: index, y: value });
18748
- }
18749
- const n = points.length;
18750
- if (n < 2) return null;
18751
- const meanX = points.reduce((sum, p) => sum + p.x, 0) / n;
18752
- const meanY = points.reduce((sum, p) => sum + p.y, 0) / n;
18753
- let sxx = 0;
18754
- let sxy = 0;
18755
- for (const p of points) {
18756
- sxx += (p.x - meanX) ** 2;
18757
- sxy += (p.x - meanX) * (p.y - meanY);
18758
- }
18759
- const slope = sxy / sxx;
18760
- const intercept = meanY - slope * meanX;
18761
- let ssRes = 0;
18762
- let ssTot = 0;
18763
- for (const p of points) {
18764
- ssRes += (p.y - (slope * p.x + intercept)) ** 2;
18765
- ssTot += (p.y - meanY) ** 2;
18766
- }
18767
- const r2 = ssTot === 0 ? 1 : Math.min(1, Math.max(0, 1 - ssRes / ssTot));
18768
- const firstX = points[0].x;
18769
- const lastX = points[n - 1].x;
18770
- return {
18771
- slope: roundSignificant(slope),
18772
- intercept: roundSignificant(intercept),
18773
- r2: round(r2),
18774
- start: roundSignificant(slope * firstX + intercept),
18775
- end: roundSignificant(slope * lastX + intercept),
18776
- n,
18777
- startIndex: firstX,
18778
- endIndex: lastX
18779
- };
18780
- }
18781
-
18782
18797
  // ../contracts/src/traffic.ts
18783
18798
  import { z as z57 } from "zod";
18784
18799
  var trafficCrawlerSegmentsSchema = z57.object({
@@ -19332,6 +19347,16 @@ var trafficEventsResponseSchema = z57.object({
19332
19347
  * early gap, which is the better error than marking real data unmeasured.
19333
19348
  */
19334
19349
  coverageStart: z57.string().nullable(),
19350
+ /**
19351
+ * When the selected sources began counting AI-referral arrivals once per
19352
+ * visit, or null when none holds hours counted by the older rule. Hours
19353
+ * before it also counted a visitor's later pages, assets and API calls as
19354
+ * arrivals and read high; stored hourly rows keep no visitor, so they
19355
+ * cannot be recounted. Like `coverageStart`, not bound to the window.
19356
+ * `trends.aiReferralLandedHits` is fitted only over buckets that begin at
19357
+ * or after it. Optional so a newer client reads an older API.
19358
+ */
19359
+ aiReferralCountingChangedAt: z57.string().nullable().optional(),
19335
19360
  /**
19336
19361
  * Least-squares fit per charted metric, over the densified points.
19337
19362
  *
@@ -21446,6 +21471,14 @@ var trafficActivitySummarySchema = z66.object({
21446
21471
  coverageStart: z66.string().nullable(),
21447
21472
  /** Recording began on or before the prior window start. This does not establish uninterrupted ingestion. */
21448
21473
  priorWindowComplete: z66.boolean(),
21474
+ /**
21475
+ * When the project's sources began counting AI-referral arrivals once per
21476
+ * visit, or null when none holds hours counted by the older rule. Earlier hours
21477
+ * also counted a visitor's later requests and read high, and cannot be
21478
+ * recounted, so referral deltas are null while the prior window starts
21479
+ * before it. Not window-bound. Optional so a newer client reads an older API.
21480
+ */
21481
+ aiReferralCountingChangedAt: z66.string().nullable().optional(),
21449
21482
  hasData: z66.boolean().describe("True when the selected or prior window has stored crawler, user-fetch, countable referral or operator evidence, or the selected window has referral redirects. False means no qualifying stored evidence; it can occur with no, paused, errored or never-synced ingestion and out-of-window observations. It does not establish measured zero traffic or source health."),
21450
21483
  /** Verified crawler hits during the selected period and the equal preceding period. */
21451
21484
  verifiedCrawlerHits: trafficActivityCountSchema,
@@ -28311,6 +28344,7 @@ export {
28311
28344
  GaMoverChangeBases,
28312
28345
  gaSocialReferralTrendResponseSchema,
28313
28346
  gaAttributionTrendResponseSchema,
28347
+ GA_OVERVIEW_WINDOW_DAYS,
28314
28348
  GA_OVERVIEW_WINDOWS,
28315
28349
  isGaOverviewWindow,
28316
28350
  GA_OVERVIEW_HISTORY_DAYS,