@prenta/core 5.8.0 → 5.9.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 (104) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/api/handler-factory.d.ts.map +1 -1
  3. package/dist/api/handler-factory.js +15 -6
  4. package/dist/api/handler-factory.js.map +1 -1
  5. package/dist/api/handlers.d.ts.map +1 -1
  6. package/dist/api/handlers.js +2 -0
  7. package/dist/api/handlers.js.map +1 -1
  8. package/dist/api/routes/scheduling.d.ts.map +1 -1
  9. package/dist/api/routes/scheduling.js +8 -1
  10. package/dist/api/routes/scheduling.js.map +1 -1
  11. package/dist/api/routes/seo-geo.d.ts +23 -0
  12. package/dist/api/routes/seo-geo.d.ts.map +1 -0
  13. package/dist/api/routes/seo-geo.js +188 -0
  14. package/dist/api/routes/seo-geo.js.map +1 -0
  15. package/dist/astro-redirects.d.ts +5 -0
  16. package/dist/astro-redirects.d.ts.map +1 -1
  17. package/dist/astro-redirects.js +13 -1
  18. package/dist/astro-redirects.js.map +1 -1
  19. package/dist/cron/index.d.ts +8 -0
  20. package/dist/cron/index.d.ts.map +1 -1
  21. package/dist/cron/index.js +21 -0
  22. package/dist/cron/index.js.map +1 -1
  23. package/dist/db/model-types.d.ts +21 -0
  24. package/dist/db/model-types.d.ts.map +1 -1
  25. package/dist/forms/attribution.d.ts +6 -2
  26. package/dist/forms/attribution.d.ts.map +1 -1
  27. package/dist/forms/attribution.js +5 -0
  28. package/dist/forms/attribution.js.map +1 -1
  29. package/dist/forms/index.d.ts +2 -2
  30. package/dist/forms/index.d.ts.map +1 -1
  31. package/dist/forms/index.js +1 -1
  32. package/dist/forms/index.js.map +1 -1
  33. package/dist/generated/browser.d.ts +18 -0
  34. package/dist/generated/client.d.ts +18 -0
  35. package/dist/generated/internal/class.d.ts +22 -0
  36. package/dist/generated/internal/class.js +4 -4
  37. package/dist/generated/internal/prismaNamespace.d.ts +187 -1
  38. package/dist/generated/internal/prismaNamespace.js +32 -0
  39. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +36 -0
  40. package/dist/generated/internal/prismaNamespaceBrowser.js +32 -0
  41. package/dist/generated/models/GeoCrawlDaily.d.ts +1144 -0
  42. package/dist/generated/models/GeoCrawlDaily.js +1 -0
  43. package/dist/generated/models/GeoReferralDaily.d.ts +1116 -0
  44. package/dist/generated/models/GeoReferralDaily.js +1 -0
  45. package/dist/generated/models.d.ts +2 -0
  46. package/dist/index.d.ts +7 -4
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +5 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/public-geo.d.ts +39 -0
  51. package/dist/public-geo.d.ts.map +1 -0
  52. package/dist/public-geo.js +79 -0
  53. package/dist/public-geo.js.map +1 -0
  54. package/dist/security/rate-limit-policy.d.ts +23 -0
  55. package/dist/security/rate-limit-policy.d.ts.map +1 -1
  56. package/dist/security/rate-limit-policy.js +36 -2
  57. package/dist/security/rate-limit-policy.js.map +1 -1
  58. package/dist/seo/ai-bots.d.ts +38 -7
  59. package/dist/seo/ai-bots.d.ts.map +1 -1
  60. package/dist/seo/ai-bots.js +82 -27
  61. package/dist/seo/ai-bots.js.map +1 -1
  62. package/dist/seo/ai-crawler-log.d.ts +2 -0
  63. package/dist/seo/ai-crawler-log.d.ts.map +1 -1
  64. package/dist/seo/ai-crawler-log.js +19 -1
  65. package/dist/seo/ai-crawler-log.js.map +1 -1
  66. package/dist/seo/ai-referrers.d.ts +25 -0
  67. package/dist/seo/ai-referrers.d.ts.map +1 -0
  68. package/dist/seo/ai-referrers.js +96 -0
  69. package/dist/seo/ai-referrers.js.map +1 -0
  70. package/dist/seo/config-store.d.ts +4 -0
  71. package/dist/seo/config-store.d.ts.map +1 -1
  72. package/dist/seo/config-store.js +3 -0
  73. package/dist/seo/config-store.js.map +1 -1
  74. package/dist/seo/digest.d.ts +23 -0
  75. package/dist/seo/digest.d.ts.map +1 -1
  76. package/dist/seo/digest.js +89 -3
  77. package/dist/seo/digest.js.map +1 -1
  78. package/dist/seo/geo-record.d.ts +75 -0
  79. package/dist/seo/geo-record.d.ts.map +1 -0
  80. package/dist/seo/geo-record.js +252 -0
  81. package/dist/seo/geo-record.js.map +1 -0
  82. package/dist/seo/geo-report.d.ts +96 -0
  83. package/dist/seo/geo-report.d.ts.map +1 -0
  84. package/dist/seo/geo-report.js +315 -0
  85. package/dist/seo/geo-report.js.map +1 -0
  86. package/dist/seo/geo-retention.d.ts +19 -0
  87. package/dist/seo/geo-retention.d.ts.map +1 -0
  88. package/dist/seo/geo-retention.js +23 -0
  89. package/dist/seo/geo-retention.js.map +1 -0
  90. package/dist/seo/index.d.ts +9 -2
  91. package/dist/seo/index.d.ts.map +1 -1
  92. package/dist/seo/index.js +5 -1
  93. package/dist/seo/index.js.map +1 -1
  94. package/dist/seo/search-console/sync.d.ts +8 -0
  95. package/dist/seo/search-console/sync.d.ts.map +1 -1
  96. package/dist/seo/search-console/sync.js +6 -3
  97. package/dist/seo/search-console/sync.js.map +1 -1
  98. package/dist/site.d.ts +5 -0
  99. package/dist/site.d.ts.map +1 -1
  100. package/dist/site.js +3 -0
  101. package/dist/site.js.map +1 -1
  102. package/package.json +3 -3
  103. package/prisma/migrations/0018_geo_daily/migration.sql +35 -0
  104. package/prisma/schema.prisma +41 -0
@@ -63,26 +63,92 @@ export const AI_RETRIEVAL_BOTS = [
63
63
  'DuckAssistBot',
64
64
  ];
65
65
  /**
66
- * Identify a known AI crawler from a `User-Agent` string. Substring match
67
- * (case-insensitive) against the curated taxonomy. TRAINING tokens are checked
68
- * first so the more specific `*-Extended` variants win over their search-crawler
69
- * namesakes (e.g. `Applebot-Extended` training, plain `Applebot` → retrieval).
70
- * Returns `null` for humans and unknown bots — never guesses.
66
+ * General web-search crawlers. Never blocked by the AI policy; recorded by GEO
67
+ * measurement as the baseline that makes AI crawl volume legible. Well-attested
68
+ * tokens only. `Applebot` is deliberately NOT here it feeds Siri/Spotlight
69
+ * answers and stays in the retrieval list.
71
70
  */
72
- export function detectAiBot(userAgent) {
71
+ export const SEARCH_BOTS = [
72
+ 'Googlebot',
73
+ 'Bingbot',
74
+ 'DuckDuckBot',
75
+ 'YandexBot',
76
+ 'Baiduspider',
77
+ ];
78
+ const REGISTRY = [
79
+ [AI_TRAINING_BOTS, 'training'],
80
+ [AI_RETRIEVAL_BOTS, 'retrieval'],
81
+ [SEARCH_BOTS, 'search'],
82
+ ];
83
+ /**
84
+ * Identify a known crawler from a `User-Agent` string. Substring match
85
+ * (case-insensitive). Precedence is training → retrieval → search, so the more
86
+ * specific `*-Extended` variants win over their search-crawler namesakes
87
+ * (`Applebot-Extended` → training, `Applebot` → retrieval, `Google-Extended` →
88
+ * training, `Googlebot` → search). Returns `null` for humans and unknown bots.
89
+ */
90
+ export function detectBot(userAgent) {
73
91
  if (!userAgent)
74
92
  return null;
75
93
  const ua = userAgent.toLowerCase();
76
- for (const token of AI_TRAINING_BOTS) {
77
- if (ua.includes(token.toLowerCase()))
78
- return { token, category: 'training' };
94
+ for (const [tokens, category] of REGISTRY) {
95
+ for (const token of tokens) {
96
+ if (ua.includes(token.toLowerCase()))
97
+ return { token, category };
98
+ }
79
99
  }
80
- for (const token of AI_RETRIEVAL_BOTS) {
81
- if (ua.includes(token.toLowerCase()))
82
- return { token, category: 'retrieval' };
100
+ return null;
101
+ }
102
+ /**
103
+ * Identify a known **AI** crawler (training or retrieval) — the original
104
+ * contract used by robots.txt and the lifetime crawler log. Search bots return
105
+ * `null` here; use {@link detectBot} when you want them too.
106
+ */
107
+ export function detectAiBot(userAgent) {
108
+ const found = detectBot(userAgent);
109
+ if (!found || found.category === 'search')
110
+ return null;
111
+ return { token: found.token, category: found.category };
112
+ }
113
+ /**
114
+ * Exact (case-insensitive) lookup of a canonical token. This is the server-side
115
+ * allowlist for beacon payloads: a middleware running a different core version
116
+ * can only ever record tokens this build knows about.
117
+ */
118
+ export function botByToken(token) {
119
+ if (!token)
120
+ return null;
121
+ const needle = token.toLowerCase();
122
+ for (const [tokens, category] of REGISTRY) {
123
+ for (const candidate of tokens) {
124
+ if (candidate.toLowerCase() === needle)
125
+ return { token: candidate, category };
126
+ }
83
127
  }
84
128
  return null;
85
129
  }
130
+ /**
131
+ * Effective stance for one category. Precedence mirrors
132
+ * {@link buildAiBotRobotsLines}: explicit policy → legacy `blockAIBots` → allow.
133
+ * Search crawlers are never blocked by the AI policy.
134
+ */
135
+ export function aiBotStanceFor(category, policy, legacyBlockAIBots) {
136
+ switch (category) {
137
+ case 'search':
138
+ return 'allow';
139
+ case 'training':
140
+ case 'retrieval': {
141
+ if (policy && (policy.training || policy.retrieval)) {
142
+ return policy[category] === 'block' ? 'block' : 'allow';
143
+ }
144
+ return legacyBlockAIBots ? 'block' : 'allow';
145
+ }
146
+ default: {
147
+ const _exhaustive = category;
148
+ return _exhaustive;
149
+ }
150
+ }
151
+ }
86
152
  /**
87
153
  * Build the robots.txt User-agent/Disallow line groups for blocked AI bots.
88
154
  *
@@ -96,24 +162,13 @@ export function detectAiBot(userAgent) {
96
162
  * `User-agent` + `Disallow: /` + blank-line group). Pure + deterministic.
97
163
  */
98
164
  export function buildAiBotRobotsLines(policy, legacyBlockAIBots) {
99
- let blockTraining;
100
- let blockRetrieval;
101
- if (policy && (policy.training || policy.retrieval)) {
102
- blockTraining = policy.training === 'block';
103
- blockRetrieval = policy.retrieval === 'block';
104
- }
105
- else if (legacyBlockAIBots) {
106
- blockTraining = true;
107
- blockRetrieval = true;
108
- }
109
- else {
110
- return [];
111
- }
112
165
  const bots = [];
113
- if (blockTraining)
166
+ if (aiBotStanceFor('training', policy, legacyBlockAIBots) === 'block') {
114
167
  bots.push(...AI_TRAINING_BOTS);
115
- if (blockRetrieval)
168
+ }
169
+ if (aiBotStanceFor('retrieval', policy, legacyBlockAIBots) === 'block') {
116
170
  bots.push(...AI_RETRIEVAL_BOTS);
171
+ }
117
172
  const lines = [];
118
173
  for (const bot of bots) {
119
174
  lines.push(`User-agent: ${bot}`);
@@ -1 +1 @@
1
- {"version":3,"file":"ai-bots.js","sourceRoot":"","sources":["../../src/seo/ai-bots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IACjD,SAAS;IACT,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,cAAc;IACd,2DAA2D;IAC3D,iBAAiB;IACjB,0CAA0C;IAC1C,OAAO;IACP,YAAY;IACZ,YAAY;IACZ,qEAAqE;IACrE,mBAAmB;IACnB,OAAO;IACP,oBAAoB;IACpB,aAAa;IACb,kCAAkC;IAClC,SAAS;IACT,WAAW;IACX,QAAQ;IACR,UAAU;IACV,UAAU;IACV,WAAW;IACX,cAAc;IACd,UAAU;CACX,CAAA;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,SAAS;IACT,eAAe;IACf,cAAc;IACd,aAAa;IACb,eAAe;IACf,iBAAiB;IACjB,sBAAsB;IACtB,YAAY;IACZ,aAAa;IACb,gCAAgC;IAChC,UAAU;IACV,iBAAiB;IACjB,WAAW;IACX,8BAA8B;IAC9B,QAAQ;IACR,eAAe;CAChB,CAAA;AAUD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,SAAoC;IAC9D,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAA;IAC3B,MAAM,EAAE,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;IAClC,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;IAC9E,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,iBAAiB,EAAE,CAAC;QACtC,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAA;IAC/E,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAWD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAA+B,EAC/B,iBAAsC;IAEtC,IAAI,aAAsB,CAAA;IAC1B,IAAI,cAAuB,CAAA;IAE3B,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACpD,aAAa,GAAG,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAA;QAC3C,cAAc,GAAG,MAAM,CAAC,SAAS,KAAK,OAAO,CAAA;IAC/C,CAAC;SAAM,IAAI,iBAAiB,EAAE,CAAC;QAC7B,aAAa,GAAG,IAAI,CAAA;QACpB,cAAc,GAAG,IAAI,CAAA;IACvB,CAAC;SAAM,CAAC;QACN,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,IAAI,aAAa;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAA;IACjD,IAAI,cAAc;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAA;IAEnD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC,CAAA;QAChC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAChB,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
1
+ {"version":3,"file":"ai-bots.js","sourceRoot":"","sources":["../../src/seo/ai-bots.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  export type AiCrawlerSurface = 'llms' | 'llms-full' | 'md' | 'honeypot';
2
+ /** Path recorded in the GEO daily trend for each lifetime-counter surface. */
3
+ export declare const AI_CRAWLER_SURFACE_PATH: Record<AiCrawlerSurface, string>;
2
4
  /** Upsert one aggregated hit. Safe to await on any public route. */
3
5
  export declare function recordAiCrawlerHit(db: any, entry: {
4
6
  bot: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ai-crawler-log.d.ts","sourceRoot":"","sources":["../../src/seo/ai-crawler-log.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,WAAW,GAAG,IAAI,GAAG,UAAU,CAAA;AAIvE,oEAAoE;AACpE,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,GAAG,EACP,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,GAAG,EACP,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAQf"}
1
+ {"version":3,"file":"ai-crawler-log.d.ts","sourceRoot":"","sources":["../../src/seo/ai-crawler-log.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,WAAW,GAAG,IAAI,GAAG,UAAU,CAAA;AAIvE,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAKpE,CAAA;AAED,oEAAoE;AACpE,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,GAAG,EACP,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,GAAG,EACP,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -7,8 +7,16 @@
7
7
  * `prenta_ai_crawler_hits` table — must never break a public surface.
8
8
  */
9
9
  import { hasModel } from '../api/route-helpers.js';
10
- import { detectAiBot } from './ai-bots.js';
10
+ import { botByToken, detectAiBot } from './ai-bots.js';
11
+ import { recordGeoCrawl } from './geo-record.js';
11
12
  const MAX_BOT_LEN = 128;
13
+ /** Path recorded in the GEO daily trend for each lifetime-counter surface. */
14
+ export const AI_CRAWLER_SURFACE_PATH = {
15
+ llms: '/llms.txt',
16
+ 'llms-full': '/llms-full.txt',
17
+ md: '/md',
18
+ honeypot: '/ai-crawlers/trap',
19
+ };
12
20
  /** Upsert one aggregated hit. Safe to await on any public route. */
13
21
  export async function recordAiCrawlerHit(db, entry) {
14
22
  if (!db || !hasModel(db, 'aiCrawlerHit'))
@@ -29,6 +37,16 @@ export async function recordAiCrawlerHit(db, entry) {
29
37
  catch {
30
38
  // best-effort — swallow so logging can never break the response
31
39
  }
40
+ // Keep the daily trend in step with the lifetime counter. Only registry
41
+ // tokens are recorded (the honeypot's `unknown` stays lifetime-only).
42
+ const known = botByToken(bot);
43
+ if (known) {
44
+ await recordGeoCrawl(db, {
45
+ bot: known.token,
46
+ category: known.category,
47
+ path: AI_CRAWLER_SURFACE_PATH[entry.surface],
48
+ });
49
+ }
32
50
  }
33
51
  /**
34
52
  * Detect a known AI crawler from the request's User-Agent and, if found, record
@@ -1 +1 @@
1
- {"version":3,"file":"ai-crawler-log.js","sourceRoot":"","sources":["../../src/seo/ai-crawler-log.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAI1C,MAAM,WAAW,GAAG,GAAG,CAAA;AAEvB,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,EAAO,EACP,KAAoE;IAEpE,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;QAAE,OAAM;IAChD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;SAChC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC;SACrB,IAAI,EAAE,CAAA;IACT,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAA;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;YAC3B,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE;YACvD,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE;YACpE,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE;SAC3D,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,EAAO,EACP,OAAgB,EAChB,OAAyB;IAEzB,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAA;IAC/D,IAAI,CAAC,QAAQ;QAAE,OAAM;IACrB,MAAM,kBAAkB,CAAC,EAAE,EAAE;QAC3B,GAAG,EAAE,QAAQ,CAAC,KAAK;QACnB,OAAO;QACP,QAAQ,EAAE,QAAQ,CAAC,QAAQ;KAC5B,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"ai-crawler-log.js","sourceRoot":"","sources":["../../src/seo/ai-crawler-log.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,MAAM,WAAW,GAAG,GAAG,CAAA;AAEvB,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAqC;IACvE,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,gBAAgB;IAC7B,EAAE,EAAE,KAAK;IACT,QAAQ,EAAE,mBAAmB;CAC9B,CAAA;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,EAAO,EACP,KAAoE;IAEpE,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;QAAE,OAAM;IAChD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;SAChC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC;SACrB,IAAI,EAAE,CAAA;IACT,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAA;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;YAC3B,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE;YACvD,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE;YACpE,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE;SAC3D,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;IACD,wEAAwE;IACxE,sEAAsE;IACtE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,cAAc,CAAC,EAAE,EAAE;YACvB,GAAG,EAAE,KAAK,CAAC,KAAK;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,IAAI,EAAE,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC;SAC7C,CAAC,CAAA;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,EAAO,EACP,OAAgB,EAChB,OAAyB;IAEzB,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAA;IAC/D,IAAI,CAAC,QAAQ;QAAE,OAAM;IACrB,MAAM,kBAAkB,CAAC,EAAE,EAAE;QAC3B,GAAG,EAAE,QAAQ,CAAC,KAAK;QACnB,OAAO;QACP,QAAQ,EAAE,QAAQ,CAAC,QAAQ;KAC5B,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface DetectedAiReferrer {
2
+ /** `KnownReferrer.name`, e.g. `ChatGPT`. */
3
+ engine: string;
4
+ via: 'referer' | 'utm';
5
+ }
6
+ /** Exact allowlist check for beacon payloads. */
7
+ export declare function isAiReferrerEngine(name: string | null | undefined): boolean;
8
+ /**
9
+ * Detects an AI-engine referral from the `Referer` header, falling back to the
10
+ * request URL's `utm_source`. Returns `null` (never throws) when neither signal
11
+ * matches or the inputs are malformed.
12
+ *
13
+ * @param input.referer - Raw `Referer` header value, if any.
14
+ * @param input.url - The current request URL.
15
+ * @param input.selfOrigin - The site's own origin, used to skip same-site
16
+ * referers. Must be a full origin including the scheme (e.g.
17
+ * `https://example.com`); bare hosts such as `example.com` do not parse as a
18
+ * URL and are ignored, so no same-site filtering is applied.
19
+ */
20
+ export declare function detectAiReferrer(input: {
21
+ referer?: string | null;
22
+ url: string | URL;
23
+ selfOrigin?: string | null;
24
+ }): DetectedAiReferrer | null;
25
+ //# sourceMappingURL=ai-referrers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-referrers.d.ts","sourceRoot":"","sources":["../../src/seo/ai-referrers.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,SAAS,GAAG,KAAK,CAAA;CACvB;AA2CD,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B,GAAG,kBAAkB,GAAG,IAAI,CAqB5B"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * AI-engine referral detection for GEO measurement.
3
+ *
4
+ * Two signals, in order:
5
+ * 1. the `Referer` header's hostname, looked up in the shared attribution
6
+ * registry (`KNOWN_REFERRERS`, `channel: 'ai'`);
7
+ * 2. failing that, the request URL's `utm_source` — ChatGPT appends
8
+ * `?utm_source=chatgpt.com` to outbound links while sending no `Referer`,
9
+ * and other engines tag links with their name.
10
+ *
11
+ * Pure. Never returns anything derived from the raw referrer URL or query
12
+ * string other than the engine name.
13
+ */
14
+ import { AI_REFERRER_NAMES, lookupKnownReferrer } from '../forms/attribution.js';
15
+ /**
16
+ * Explicit `utm_source` aliases (lowercased) → registry `name`. Only exact
17
+ * matches are accepted; word fragments of engine names (`google`, `microsoft`,
18
+ * `chat`, `studio`) are deliberately NOT matched — they are common paid-campaign
19
+ * tags and would corrupt the AI-referral metric.
20
+ */
21
+ const UTM_ENGINE_ALIASES = {
22
+ copilot: 'Microsoft Copilot',
23
+ mistral: 'Le Chat (Mistral)',
24
+ };
25
+ function hostOf(value) {
26
+ try {
27
+ return new URL(value).hostname.toLowerCase();
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ function aiEngineForHost(host) {
34
+ const meta = lookupKnownReferrer(host);
35
+ return meta && meta.channel === 'ai' ? meta.name : null;
36
+ }
37
+ /**
38
+ * Matches a `utm_source` value against an engine name exactly (case-insensitive),
39
+ * against the name with non-alphanumerics removed (`chatgpt`, `deepseek`), or
40
+ * against an explicit alias. Never matches partial words.
41
+ */
42
+ function aiEngineForWord(raw) {
43
+ const word = raw.trim().toLowerCase();
44
+ if (!word)
45
+ return null;
46
+ for (const name of AI_REFERRER_NAMES) {
47
+ const lowered = name.toLowerCase();
48
+ if (lowered === word)
49
+ return name;
50
+ if (lowered.replace(/[^a-z0-9]/g, '') === word)
51
+ return name;
52
+ }
53
+ const alias = Object.hasOwn(UTM_ENGINE_ALIASES, word) ? UTM_ENGINE_ALIASES[word] : undefined;
54
+ return alias ?? null;
55
+ }
56
+ /** Exact allowlist check for beacon payloads. */
57
+ export function isAiReferrerEngine(name) {
58
+ return typeof name === 'string' && name.length > 0 && AI_REFERRER_NAMES.includes(name);
59
+ }
60
+ /**
61
+ * Detects an AI-engine referral from the `Referer` header, falling back to the
62
+ * request URL's `utm_source`. Returns `null` (never throws) when neither signal
63
+ * matches or the inputs are malformed.
64
+ *
65
+ * @param input.referer - Raw `Referer` header value, if any.
66
+ * @param input.url - The current request URL.
67
+ * @param input.selfOrigin - The site's own origin, used to skip same-site
68
+ * referers. Must be a full origin including the scheme (e.g.
69
+ * `https://example.com`); bare hosts such as `example.com` do not parse as a
70
+ * URL and are ignored, so no same-site filtering is applied.
71
+ */
72
+ export function detectAiReferrer(input) {
73
+ const refHost = input.referer ? hostOf(input.referer) : null;
74
+ if (refHost) {
75
+ const selfHost = input.selfOrigin ? hostOf(input.selfOrigin) : null;
76
+ if (!selfHost || refHost !== selfHost) {
77
+ const engine = aiEngineForHost(refHost);
78
+ if (engine)
79
+ return { engine, via: 'referer' };
80
+ }
81
+ }
82
+ let url;
83
+ try {
84
+ url = typeof input.url === 'string' ? new URL(input.url) : input.url;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ const utm = url.searchParams.get('utm_source');
90
+ if (!utm)
91
+ return null;
92
+ const asHost = utm.includes('.') ? aiEngineForHost(utm.toLowerCase()) : null;
93
+ const engine = asHost ?? aiEngineForWord(utm);
94
+ return engine ? { engine, via: 'utm' } : null;
95
+ }
96
+ //# sourceMappingURL=ai-referrers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-referrers.js","sourceRoot":"","sources":["../../src/seo/ai-referrers.ts"],"names":[],"mappings":"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"}
@@ -99,6 +99,10 @@ export interface SeoModuleSettings {
99
99
  * margin) halts `already_running` instead of racing the first past `maxPerDay`.
100
100
  */
101
101
  autopilotRunStartedAt: string | null;
102
+ /** GEO measurement (spec 2026-09-08): accept crawl/referral beacons. On by default. */
103
+ geoMeasurementEnabled: boolean;
104
+ /** Days of GeoCrawlDaily / GeoReferralDaily to keep (7–365). */
105
+ geoRetentionDays: number;
102
106
  }
103
107
  /** Safe defaults applied when the overrides document has no `module` block. */
104
108
  export declare const DEFAULT_SEO_MODULE_SETTINGS: SeoModuleSettings;
@@ -1 +1 @@
1
- {"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/seo/config-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/seo/config-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACV,eAAe,EAEf,mBAAmB,EACnB,aAAa,EACd,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AACjE,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAA;AAG9B,eAAO,MAAM,qBAAqB,mBAAmB,CAAA;AAErD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,IAAI,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;IAC7B,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAA;IAC1D,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACnC,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,OAAO,CAAA;IAC1B,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;IACtB,8BAA8B,EAAE,OAAO,CAAA;IACvC,2EAA2E;IAC3E,0BAA0B,EAAE,OAAO,CAAA;IACnC,sBAAsB,EAAE,OAAO,CAAA;IAC/B,qBAAqB,EAAE,OAAO,CAAA;IAC9B,eAAe,EAAE,OAAO,CAAA;IACxB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAA;IACjB,+EAA+E;IAC/E,sBAAsB,EAAE,OAAO,CAAA;IAC/B,4EAA4E;IAC5E,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,8FAA8F;IAC9F,wBAAwB,EAAE,OAAO,CAAA;IACjC,+FAA+F;IAC/F,yBAAyB,EAAE,OAAO,CAAA;IAClC,sGAAsG;IACtG,sCAAsC,EAAE,MAAM,CAAA;IAC9C,sEAAsE;IACtE,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAA;IACtB,4FAA4F;IAC5F,oBAAoB,EAAE,MAAM,CAAA;IAC5B,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,oEAAoE;IACpE,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACzC,uFAAuF;IACvF,oBAAoB,EAAE,MAAM,CAAA;IAC5B,6EAA6E;IAC7E,aAAa,EAAE,OAAO,CAAA;IACtB,qEAAqE;IACrE,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,0EAA0E;IAC1E,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,gFAAgF;IAChF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,0EAA0E;IAC1E,SAAS,EAAE,kBAAkB,CAAA;IAC7B,iEAAiE;IACjE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,4EAA4E;IAC5E,gBAAgB,EAAE,mBAAmB,GAAG,IAAI,CAAA;IAC5C;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,uFAAuF;IACvF,qBAAqB,EAAE,OAAO,CAAA;IAC9B,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,+EAA+E;AAC/E,eAAO,MAAM,2BAA2B,EAAE,iBAgCzC,CAAA;AAED,uEAAuE;AACvE,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,kBAAkB,GAAG,IAAI,GAAG,iBAAiB,CAgBhG;AAED,wDAAwD;AACxD,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACjC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAY5B;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,EAAE,aAAa,CAAC,GAAG;IAC7E,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAA;CACjE,CAAA;AAmBD;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAe5B;AAED,4EAA4E;AAC5E,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAerF;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,kBAAkB,EAC7B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,kBAAkB,CAAC,CA8B7B;AAwBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,GACvC,aAAa,CAiCf;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,mBAAmB,GAAG,SAAS,EACrC,IAAI,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,SAAS,GAC7C,mBAAmB,CAiBrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,EAC1C,SAAS,EAAE,kBAAkB,GAAG,IAAI,GACnC,eAAe,GAAG,IAAI,CAkBxB"}
@@ -17,6 +17,7 @@
17
17
  * never destructive of code-level config.
18
18
  */
19
19
  import { resolveAutopilotPolicy, } from './autopilot-policy.js';
20
+ import { GEO_RETENTION_DEFAULT_DAYS } from './geo-retention.js';
20
21
  export const SEO_CONFIG_COLLECTION = '__seo_config__';
21
22
  /** Safe defaults applied when the overrides document has no `module` block. */
22
23
  export const DEFAULT_SEO_MODULE_SETTINGS = {
@@ -49,6 +50,8 @@ export const DEFAULT_SEO_MODULE_SETTINGS = {
49
50
  autopilotLastRunAt: null,
50
51
  autopilotLastRun: null,
51
52
  autopilotRunStartedAt: null,
53
+ geoMeasurementEnabled: true,
54
+ geoRetentionDays: GEO_RETENTION_DEFAULT_DAYS,
52
55
  };
53
56
  /** Merge stored module settings over the defaults (field-by-field). */
54
57
  export function resolveSeoModuleSettings(overrides) {
@@ -1 +1 @@
1
- {"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/seo/config-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/seo/config-store.ts"],"names":[],"mappings":"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"}
@@ -64,6 +64,27 @@ export interface SeoDigestAutopilot {
64
64
  /** Newest applied rows in the window, capped at `AUTOPILOT_DIGEST_TOP`. */
65
65
  top: SeoDigestAutopilotRow[];
66
66
  }
67
+ /**
68
+ * Trailing-7-day GEO measurement summary (spec
69
+ * 2026-09-08-geo-measurement-design.md §8), derived from
70
+ * `buildSeoGeoReport(days: 7)`. `null` when measurement is off, the tables are
71
+ * absent, or nothing was recorded in the 14-day range.
72
+ */
73
+ export interface SeoDigestGeo {
74
+ crawlHits7d: number;
75
+ crawlHitsPrev7d: number | null;
76
+ /** Distinct retrieval (answer-engine) bots seen in the window. */
77
+ retrievalBots: number;
78
+ aiVisits7d: number;
79
+ aiVisitsPrev7d: number | null;
80
+ /** Most-cited paths, ≤ GEO_DIGEST_TOP. */
81
+ topCited: Array<{
82
+ path: string;
83
+ visits: number;
84
+ }>;
85
+ /** Bots whose category is blocked in robots but that still crawled. */
86
+ blockedStillCrawling: string[];
87
+ }
67
88
  export interface SeoDigest {
68
89
  generatedAt: string;
69
90
  audit: SeoDigestAudit;
@@ -79,6 +100,8 @@ export interface SeoDigest {
79
100
  };
80
101
  /** `null` when the consumer schema has no `SeoAutoApply` model. */
81
102
  autopilot: SeoDigestAutopilot | null;
103
+ /** `null` when GEO measurement is off/absent or recorded nothing in 14 days. */
104
+ geo: SeoDigestGeo | null;
82
105
  }
83
106
  export declare function buildSeoDigest(db: PrentaDB, options?: {
84
107
  now?: Date;
@@ -1 +1 @@
1
- {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/seo/digest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/seo/digest.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
+ import { hasModel } from '../api/route-helpers.js';
1
2
  import { getPrentaConfig } from '../config/runtime.js';
2
3
  import { resolveEmailSenderAsync } from '../email/resolve.js';
3
4
  import { buildQuickWins } from './audit-engine.js';
4
5
  import { getSeoOverrides, patchSeoModuleSettings, resolveSeoModuleSettings, } from './config-store.js';
6
+ import { buildSeoGeoReport } from './geo-report.js';
5
7
  import { pendingFixFromMetadata } from './issue-fix-ai.js';
6
8
  import { gradeForSeoScore } from './score.js';
7
9
  /** Latest COMPLETED run older than this ⇒ prominent staleness callout. */
@@ -12,6 +14,10 @@ const SEVERITY_RANK = { critical: 3, warning: 2, info: 1 };
12
14
  const AUTOPILOT_WINDOW_MS = 7 * DAY_MS;
13
15
  /** Max applied rows listed in the autopilot block. */
14
16
  const AUTOPILOT_DIGEST_TOP = 5;
17
+ /** Max cited paths listed in the AI visibility block. */
18
+ const GEO_DIGEST_TOP = 3;
19
+ /** AI visibility block window (current 7 days; the prior 7 form the baseline). */
20
+ const GEO_WINDOW_DAYS = 7;
15
21
  export async function buildSeoDigest(db, options = {}) {
16
22
  const now = options.now ?? new Date();
17
23
  if (!db?.seoAuditRun?.findFirst)
@@ -118,11 +124,17 @@ export async function buildSeoDigest(db, options = {}) {
118
124
  redirectSuggestions = (await db.redirectSuggestion.count({ where: { status: 'open' } })) ?? 0;
119
125
  }
120
126
  const issueFixes = openIssues.filter((i) => pendingFixFromMetadata(i.metadata) !== null).length;
127
+ // Module settings feed both optional blocks below (autopilot policy, GEO
128
+ // measurement toggle) — resolved once.
129
+ const hasAutopilot = hasModel(db, 'seoAutoApply');
130
+ const hasGeo = hasModel(db, 'geoCrawlDaily') && hasModel(db, 'geoReferralDaily');
131
+ const settings = hasAutopilot || hasGeo
132
+ ? resolveSeoModuleSettings(await getSeoOverrides(db).catch(() => null))
133
+ : null;
121
134
  // Autopilot ledger (optional model — consumer schemas may predate it).
122
135
  let autopilot = null;
123
- if (db?.seoAutoApply?.count && db?.seoAutoApply?.findMany) {
136
+ if (hasAutopilot && settings) {
124
137
  const since = new Date(now.getTime() - AUTOPILOT_WINDOW_MS);
125
- const settings = resolveSeoModuleSettings(await getSeoOverrides(db).catch(() => null));
126
138
  const applied = { status: 'applied', appliedAt: { gte: since } };
127
139
  const [applied7d, verifyFailed7d, reverted7d, regressed7d, conflicts7d, topRows] = await Promise.all([
128
140
  db.seoAutoApply.count({ where: applied }),
@@ -173,6 +185,50 @@ export async function buildSeoDigest(db, options = {}) {
173
185
  })),
174
186
  };
175
187
  }
188
+ // GEO measurement (optional tables; block is null when nothing to say).
189
+ // Best-effort: a failing report logs and yields `null`, never a failed digest.
190
+ let geo = null;
191
+ if (hasGeo && settings) {
192
+ try {
193
+ const report = await buildSeoGeoReport(db, {
194
+ days: GEO_WINDOW_DAYS,
195
+ now,
196
+ settings,
197
+ config: getPrentaConfig(),
198
+ });
199
+ const { total: crawl } = report.crawl;
200
+ const { total: referrals } = report.referrals;
201
+ // "Nothing recorded in the 14-day range" ⇒ no block. Prior-window rows
202
+ // count even when the baseline is incomparable (prev* null): in that
203
+ // case the first-ever row is inside the range, so `firstSeenAt` tells.
204
+ const rangeStart = Date.parse(report.window.from) - GEO_WINDOW_DAYS * DAY_MS;
205
+ const firstSeenInRange = report.firstSeenAt !== null && Date.parse(report.firstSeenAt) >= rangeStart;
206
+ const anyData = crawl.hits > 0 ||
207
+ referrals.visits > 0 ||
208
+ (crawl.prevHits ?? 0) > 0 ||
209
+ (referrals.prevVisits ?? 0) > 0 ||
210
+ firstSeenInRange;
211
+ if (report.enabled && anyData) {
212
+ geo = {
213
+ crawlHits7d: crawl.hits,
214
+ crawlHitsPrev7d: crawl.prevHits,
215
+ retrievalBots: report.crawl.bots.filter((b) => b.category === 'retrieval' && b.hits > 0)
216
+ .length,
217
+ aiVisits7d: referrals.visits,
218
+ aiVisitsPrev7d: referrals.prevVisits,
219
+ topCited: report.referrals.topPaths
220
+ .slice(0, GEO_DIGEST_TOP)
221
+ .map((p) => ({ path: p.path, visits: p.visits })),
222
+ blockedStillCrawling: report.crawl.bots
223
+ .filter((b) => b.policy === 'blocked_still_crawling')
224
+ .map((b) => b.bot),
225
+ };
226
+ }
227
+ }
228
+ catch (err) {
229
+ console.warn('[seo/digest] geo block skipped; report failed', err);
230
+ }
231
+ }
176
232
  return {
177
233
  generatedAt: now.toISOString(),
178
234
  audit: {
@@ -202,6 +258,7 @@ export async function buildSeoDigest(db, options = {}) {
202
258
  quickWins,
203
259
  pending: { redirectSuggestions, issueFixes },
204
260
  autopilot,
261
+ geo,
205
262
  };
206
263
  }
207
264
  // ── Email rendering ─────────────────────────────────────────────────────────
@@ -218,6 +275,15 @@ function pct(current, prev) {
218
275
  const change = Math.round(((current - prev) / prev) * 100);
219
276
  return `${change >= 0 ? '+' : ''}${change}%`;
220
277
  }
278
+ /**
279
+ * Period-over-period delta for the GEO block: `prev === null` means the prior
280
+ * window is not comparable (rendered as "—"); a zero prior window has no
281
+ * meaningful percentage and renders nothing.
282
+ */
283
+ function geoDelta(current, prev) {
284
+ return prev === null ? '—' : pct(current, prev);
285
+ }
286
+ const plural = (n, one, many = `${one}s`) => (n === 1 ? one : many);
221
287
  function fmtDate(iso) {
222
288
  return new Date(iso).toISOString().slice(0, 10);
223
289
  }
@@ -231,7 +297,7 @@ export function seoAdminUrl() {
231
297
  return `${siteUrl.replace(/\/$/, '')}${adminPath}/seo`;
232
298
  }
233
299
  export function renderSeoDigestEmail(digest, opts) {
234
- const { audit, search, newIssues, quickWins, pending, autopilot } = digest;
300
+ const { audit, search, newIssues, quickWins, pending, autopilot, geo } = digest;
235
301
  const alert = audit.stale || audit.lastRunFailed;
236
302
  const deltaPart = audit.delta !== null ? ` (${audit.delta >= 0 ? '+' : ''}${audit.delta})` : '';
237
303
  const winsPart = `${quickWins.length} quick win${quickWins.length === 1 ? '' : 's'}`;
@@ -261,6 +327,26 @@ export function renderSeoDigestEmail(digest, opts) {
261
327
  <p style="margin:0;font-size:14px;color:#374151;">Clicks: <strong>${search.clicks7d}</strong>${clicksPct ? ` (${clicksPct})` : ''} · Impressions: <strong>${search.impressions7d}</strong>${imprPct ? ` (${imprPct})` : ''}</p>`);
262
328
  textParts.push(`Search (7d): ${search.clicks7d} clicks${clicksPct ? ` (${clicksPct})` : ''}, ${search.impressions7d} impressions${imprPct ? ` (${imprPct})` : ''}`);
263
329
  }
330
+ if (geo) {
331
+ const crawlPct = geoDelta(geo.crawlHits7d, geo.crawlHitsPrev7d);
332
+ const visitsPct = geoDelta(geo.aiVisits7d, geo.aiVisitsPrev7d);
333
+ const crawlPart = crawlPct ? ` (${crawlPct})` : '';
334
+ const visitsPart = visitsPct ? ` (${visitsPct})` : '';
335
+ const botsPart = `${geo.retrievalBots} retrieval ${plural(geo.retrievalBots, 'bot')}`;
336
+ const hitsUnit = `crawler ${plural(geo.crawlHits7d, 'hit')}`;
337
+ const visitsUnit = `AI referral ${plural(geo.aiVisits7d, 'visit')}`;
338
+ // Paths and bot names originate from the public beacon — always escape.
339
+ const cited = geo.topCited
340
+ .map((c) => `<li style="margin:0 0 4px;font-size:14px;color:#374151;">${escapeHtml(c.path)} — ${c.visits} ${plural(c.visits, 'visit')}</li>`)
341
+ .join('');
342
+ const blockedLine = geo.blockedStillCrawling.length > 0
343
+ ? `<p style="margin:8px 0 0;font-size:14px;color:#92400e;">Blocked in robots.txt but still crawling: ${escapeHtml(geo.blockedStillCrawling.join(', '))}.</p>`
344
+ : '';
345
+ sections.push(`<h3 style="margin:20px 0 8px;font-size:13px;font-weight:600;color:#555;text-transform:uppercase;letter-spacing:0.04em;">AI visibility (7 days)</h3>
346
+ <p style="margin:0;font-size:14px;color:#374151;"><strong>${geo.crawlHits7d}</strong> ${hitsUnit}${crawlPart} · ${botsPart} · <strong>${geo.aiVisits7d}</strong> ${visitsUnit}${visitsPart} (${link('?tab=ai-visibility', 'view trends →')})</p>
347
+ ${cited ? `<ul style="margin:8px 0 0;padding-left:20px;">${cited}</ul>` : ''}${blockedLine}`);
348
+ textParts.push(`AI visibility: ${geo.crawlHits7d} ${hitsUnit}${crawlPart}, ${botsPart}, ${geo.aiVisits7d} ${visitsUnit}${visitsPart}${geo.blockedStillCrawling.length > 0 ? `\nBlocked but still crawling: ${geo.blockedStillCrawling.join(', ')}` : ''}`);
349
+ }
264
350
  if (quickWins.length > 0) {
265
351
  const rows = quickWins
266
352
  .map((w) => `<li style="margin:0 0 6px;font-size:14px;color:#374151;"><strong>${escapeHtml(w.title)}</strong> — ${escapeHtml(w.explanation)} <span style="color:#9ca3af;">(${escapeHtml(w.impact)} impact)</span></li>`)