@growth-labs/seo 0.1.3 → 0.2.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 (115) hide show
  1. package/README.md +142 -54
  2. package/dist/bindings.d.ts +127 -0
  3. package/dist/bindings.d.ts.map +1 -0
  4. package/dist/bindings.js +11 -0
  5. package/dist/bindings.js.map +1 -0
  6. package/dist/cron/prune-aeo-r2.d.ts +36 -0
  7. package/dist/cron/prune-aeo-r2.d.ts.map +1 -0
  8. package/dist/cron/prune-aeo-r2.js +94 -0
  9. package/dist/cron/prune-aeo-r2.js.map +1 -0
  10. package/dist/durable-objects/aeo-revalidation-coord.d.ts +69 -0
  11. package/dist/durable-objects/aeo-revalidation-coord.d.ts.map +1 -0
  12. package/dist/durable-objects/aeo-revalidation-coord.js +177 -0
  13. package/dist/durable-objects/aeo-revalidation-coord.js.map +1 -0
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +103 -16
  17. package/dist/index.js.map +1 -1
  18. package/dist/middleware/seo.d.ts +44 -4
  19. package/dist/middleware/seo.d.ts.map +1 -1
  20. package/dist/middleware/seo.js +237 -41
  21. package/dist/middleware/seo.js.map +1 -1
  22. package/dist/options.d.ts +1293 -6
  23. package/dist/options.d.ts.map +1 -1
  24. package/dist/options.js +238 -1
  25. package/dist/options.js.map +1 -1
  26. package/dist/routes/apple-news.d.ts +4 -0
  27. package/dist/routes/apple-news.d.ts.map +1 -0
  28. package/dist/routes/apple-news.js +28 -0
  29. package/dist/routes/apple-news.js.map +1 -0
  30. package/dist/routes/llms-full.d.ts +4 -0
  31. package/dist/routes/llms-full.d.ts.map +1 -0
  32. package/dist/routes/llms-full.js +29 -0
  33. package/dist/routes/llms-full.js.map +1 -0
  34. package/dist/routes/revalidate.d.ts +16 -0
  35. package/dist/routes/revalidate.d.ts.map +1 -0
  36. package/dist/routes/revalidate.js +243 -0
  37. package/dist/routes/revalidate.js.map +1 -0
  38. package/dist/routes/rss.d.ts.map +1 -1
  39. package/dist/routes/rss.js +4 -1
  40. package/dist/routes/rss.js.map +1 -1
  41. package/dist/routes/sitemap-markdown.d.ts +4 -0
  42. package/dist/routes/sitemap-markdown.d.ts.map +1 -0
  43. package/dist/routes/sitemap-markdown.js +32 -0
  44. package/dist/routes/sitemap-markdown.js.map +1 -0
  45. package/dist/types.d.ts +16 -2
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/utils/aeo-summary.d.ts +35 -0
  48. package/dist/utils/aeo-summary.d.ts.map +1 -0
  49. package/dist/utils/aeo-summary.js +141 -0
  50. package/dist/utils/aeo-summary.js.map +1 -0
  51. package/dist/utils/aeo-twin-emitter.d.ts +79 -0
  52. package/dist/utils/aeo-twin-emitter.d.ts.map +1 -0
  53. package/dist/utils/aeo-twin-emitter.js +99 -0
  54. package/dist/utils/aeo-twin-emitter.js.map +1 -0
  55. package/dist/utils/aeo.d.ts +62 -12
  56. package/dist/utils/aeo.d.ts.map +1 -1
  57. package/dist/utils/aeo.js +187 -26
  58. package/dist/utils/aeo.js.map +1 -1
  59. package/dist/utils/apple-news-anf.d.ts +38 -0
  60. package/dist/utils/apple-news-anf.d.ts.map +1 -0
  61. package/dist/utils/apple-news-anf.js +120 -0
  62. package/dist/utils/apple-news-anf.js.map +1 -0
  63. package/dist/utils/apple-news-rss.d.ts +31 -0
  64. package/dist/utils/apple-news-rss.d.ts.map +1 -0
  65. package/dist/utils/apple-news-rss.js +103 -0
  66. package/dist/utils/apple-news-rss.js.map +1 -0
  67. package/dist/utils/content-filter.d.ts +52 -0
  68. package/dist/utils/content-filter.d.ts.map +1 -0
  69. package/dist/utils/content-filter.js +75 -0
  70. package/dist/utils/content-filter.js.map +1 -0
  71. package/dist/utils/crawler-class.d.ts +39 -0
  72. package/dist/utils/crawler-class.d.ts.map +1 -0
  73. package/dist/utils/crawler-class.js +127 -0
  74. package/dist/utils/crawler-class.js.map +1 -0
  75. package/dist/utils/effective-auth.d.ts +28 -0
  76. package/dist/utils/effective-auth.d.ts.map +1 -0
  77. package/dist/utils/effective-auth.js +33 -0
  78. package/dist/utils/effective-auth.js.map +1 -0
  79. package/dist/utils/fcrdns.d.ts +73 -0
  80. package/dist/utils/fcrdns.d.ts.map +1 -0
  81. package/dist/utils/fcrdns.js +219 -0
  82. package/dist/utils/fcrdns.js.map +1 -0
  83. package/dist/utils/fresh-layer.d.ts +53 -0
  84. package/dist/utils/fresh-layer.d.ts.map +1 -0
  85. package/dist/utils/fresh-layer.js +147 -0
  86. package/dist/utils/fresh-layer.js.map +1 -0
  87. package/dist/utils/index.d.ts +14 -3
  88. package/dist/utils/index.d.ts.map +1 -1
  89. package/dist/utils/index.js +14 -3
  90. package/dist/utils/index.js.map +1 -1
  91. package/dist/utils/json-ld/article.d.ts +13 -1
  92. package/dist/utils/json-ld/article.d.ts.map +1 -1
  93. package/dist/utils/json-ld/article.js +37 -8
  94. package/dist/utils/json-ld/article.js.map +1 -1
  95. package/dist/utils/llms-full.d.ts +29 -0
  96. package/dist/utils/llms-full.d.ts.map +1 -0
  97. package/dist/utils/llms-full.js +67 -0
  98. package/dist/utils/llms-full.js.map +1 -0
  99. package/dist/utils/meta.d.ts +4 -1
  100. package/dist/utils/meta.d.ts.map +1 -1
  101. package/dist/utils/meta.js +25 -2
  102. package/dist/utils/meta.js.map +1 -1
  103. package/dist/utils/sitemap-markdown.d.ts +24 -0
  104. package/dist/utils/sitemap-markdown.d.ts.map +1 -0
  105. package/dist/utils/sitemap-markdown.js +57 -0
  106. package/dist/utils/sitemap-markdown.js.map +1 -0
  107. package/dist/utils/staleness.d.ts +27 -0
  108. package/dist/utils/staleness.d.ts.map +1 -0
  109. package/dist/utils/staleness.js +46 -0
  110. package/dist/utils/staleness.js.map +1 -0
  111. package/dist/utils/validation.d.ts +41 -0
  112. package/dist/utils/validation.d.ts.map +1 -1
  113. package/dist/utils/validation.js +78 -0
  114. package/dist/utils/validation.js.map +1 -1
  115. package/package.json +70 -58
@@ -1,54 +1,79 @@
1
+ import { resolveAeoTwins } from '../options.js';
1
2
  import { getConfig, getContentProvider } from '../state.js';
2
3
  import { estimateTokenCount, generateAeoMarkdown } from '../utils/aeo.js';
3
- function buildContentSignalHeader(config) {
4
- const { aiTrain, search, aiInput } = config.contentSignal;
5
- return `ai-train=${aiTrain}, search=${search}, ai-input=${aiInput}`;
6
- }
7
- // Factory for testability
8
- export function createSeoMiddleware(options, contentProvider) {
4
+ import { classifyRequest } from '../utils/crawler-class.js';
5
+ import { computeEffectiveAuthSegment } from '../utils/effective-auth.js';
6
+ import { createDohResolver, createFcrdnsVerifier } from '../utils/fcrdns.js';
7
+ import { readFreshTwin, writeFreshTwin } from '../utils/fresh-layer.js';
8
+ export function createSeoMiddleware(options, contentProvider, deps = {}) {
9
+ const aeo = resolveAeoTwins(options.aeoTwins);
10
+ const classify = deps.classify ?? createDefaultClassifier();
9
11
  return async (context, next) => {
10
12
  const { request } = context;
11
13
  const accept = request.headers.get('accept') ?? '';
12
14
  const url = new URL(request.url);
13
- // AEO content negotiation: intercept Accept: text/markdown requests
14
- if (options.aeoTwins && accept.includes('text/markdown') && contentProvider) {
15
- try {
16
- const articles = await contentProvider({ type: 'articles' }, context);
17
- const matched = articles.find((a) => {
18
- try {
19
- return new URL(a.url).pathname === url.pathname;
20
- }
21
- catch {
22
- return a.url === url.pathname;
23
- }
15
+ // 1. Classify the request. Expose classification on Astro.locals for the
16
+ // consumer's cache-key builder.
17
+ const crawlerClass = await classify(request);
18
+ const rawAuth = deps.rawAuthSegment?.(request) ?? 'anon';
19
+ const effectiveAuthSegment = computeEffectiveAuthSegment(crawlerClass, rawAuth);
20
+ if (context.locals) {
21
+ context.locals.crawlerClass = crawlerClass;
22
+ context.locals.effectiveAuthSegment = effectiveAuthSegment;
23
+ }
24
+ // 2. LLM training crawlers are 403'd on members items, before any other work.
25
+ // We look up the item only when crawlerClass requires it to avoid per-request
26
+ // contentProvider calls on the hot path for anonymous requests.
27
+ const blockLlmTraining = options.crawlerPolicy.blockLlmTrainingCrawlers;
28
+ if (blockLlmTraining && crawlerClass === 'llmTrainingCrawler' && contentProvider) {
29
+ const item = await findItemForPath(contentProvider, context, url.pathname);
30
+ if (item?.access === 'members') {
31
+ return new Response('Forbidden', {
32
+ status: 403,
33
+ headers: {
34
+ 'Content-Type': 'text/plain; charset=utf-8',
35
+ 'X-Robots-Tag': 'noindex',
36
+ ...varyHeaders(),
37
+ },
24
38
  });
25
- if (matched) {
26
- const content = matched.description ?? '';
27
- const markdown = generateAeoMarkdown(matched, content, options.schemaType, options.organization.name);
28
- const tokenCount = estimateTokenCount(markdown);
29
- const contentSignal = buildContentSignalHeader(options);
30
- return new Response(markdown, {
31
- headers: {
32
- 'Content-Type': 'text/markdown; charset=utf-8',
33
- 'x-markdown-tokens': String(tokenCount),
34
- 'content-signal': contentSignal,
35
- },
36
- });
37
- }
38
39
  }
39
- catch { }
40
40
  }
41
- // Pass through to next middleware/route handler
41
+ // 3. Accept: text/markdown handling (middleware mode of AEO twins).
42
+ const wantsMarkdown = accept.includes('text/markdown');
43
+ if (aeo && aeo.mode !== 'static' && wantsMarkdown && contentProvider) {
44
+ const markdownResponse = await serveAeoMarkdown({
45
+ request,
46
+ context,
47
+ options,
48
+ contentProvider,
49
+ freshLayer: deps.freshLayer,
50
+ assets: deps.assets,
51
+ waitUntil: deps.waitUntil,
52
+ });
53
+ if (markdownResponse)
54
+ return markdownResponse;
55
+ }
56
+ // 4. Pass through to the next handler (SSR page or static asset route).
42
57
  const response = await next();
43
- // Clone to add headers (Response headers are immutable)
58
+ // 5. Wrap response with our observability + policy headers.
44
59
  const newHeaders = new Headers(response.headers);
45
- // Add Content-Signal header to every response
60
+ // Content-Signal on every response.
46
61
  newHeaders.set('content-signal', buildContentSignalHeader(options));
47
- // Add Link alternate header on HTML responses when aeoTwins enabled
48
- if (options.aeoTwins) {
49
- const contentType = response.headers.get('content-type') ?? '';
50
- if (contentType.includes('text/html')) {
51
- const linkValue = `<${url.toString()}>; rel="alternate"; type="text/markdown"`;
62
+ // Vary must cover every axis that branches the response:
63
+ // - Accept: response body differs on text/markdown
64
+ // - User-Agent: crawler class depends on it
65
+ // - Cookie: auth segment depends on it
66
+ // - CF-Connecting-IP: FCrDNS is keyed on it
67
+ appendVaryHeaders(newHeaders);
68
+ // Link alternate header on HTML responses when aeoTwins enabled, except for
69
+ // members items (they have no markdown counterpart at rest).
70
+ if (aeo && isHtmlResponse(response)) {
71
+ const linkItem = contentProvider
72
+ ? await findItemForPath(contentProvider, context, url.pathname)
73
+ : undefined;
74
+ if (!linkItem || linkItem.access !== 'members') {
75
+ const target = aeo.mode === 'static' || aeo.mode === 'both' ? twinUrlFor(url, aeo) : url.toString();
76
+ const linkValue = `<${target}>; rel="alternate"; type="text/markdown"`;
52
77
  const existing = newHeaders.get('Link');
53
78
  newHeaders.set('Link', existing ? `${existing}, ${linkValue}` : linkValue);
54
79
  }
@@ -60,11 +85,182 @@ export function createSeoMiddleware(options, contentProvider) {
60
85
  });
61
86
  };
62
87
  }
63
- // Astro middleware entry point
88
+ // ─── Astro middleware entry ───
89
+ let _classifier;
64
90
  export const onRequest = async (context, next) => {
65
91
  const config = getConfig();
66
92
  const contentProvider = getContentProvider();
67
- const middleware = createSeoMiddleware(config, contentProvider);
93
+ const env = context.locals?.runtime?.env;
94
+ const aeo = resolveAeoTwins(config.aeoTwins);
95
+ // Wire bindings from env when aeoTwins is enabled. Middleware-mode requires R2+DO;
96
+ // static-only consumers get the lighter path.
97
+ let freshLayer;
98
+ if (env && aeo && aeo.mode !== 'static' && aeo.freshLayer) {
99
+ const bindingName = aeo.freshLayer.bindingName;
100
+ const impl = env[bindingName];
101
+ if (impl) {
102
+ freshLayer = {
103
+ type: aeo.freshLayer.type,
104
+ impl: impl,
105
+ deploymentId: env.CF_VERSION_METADATA?.id ?? 'dev',
106
+ };
107
+ }
108
+ }
109
+ const middleware = createSeoMiddleware(config, contentProvider, {
110
+ freshLayer,
111
+ assets: env?.ASSETS,
112
+ waitUntil: (promise) => {
113
+ const ctx = context.locals
114
+ ?.cfContext;
115
+ ctx?.waitUntil?.(promise);
116
+ },
117
+ });
68
118
  return middleware(context, next);
69
119
  };
120
+ // ─── Internals ───
121
+ function createDefaultClassifier() {
122
+ if (!_classifier) {
123
+ _classifier = createFcrdnsVerifier(createDohResolver());
124
+ }
125
+ const verifier = _classifier;
126
+ return (request) => classifyRequest({ request, fcrdnsVerify: verifier });
127
+ }
128
+ function buildContentSignalHeader(config) {
129
+ const { aiTrain, search, aiInput } = config.contentSignal;
130
+ return `ai-train=${aiTrain}, search=${search}, ai-input=${aiInput}`;
131
+ }
132
+ function varyHeaders() {
133
+ return { Vary: 'Accept, User-Agent, Cookie, CF-Connecting-IP' };
134
+ }
135
+ function appendVaryHeaders(headers) {
136
+ const existing = headers.get('Vary');
137
+ const required = 'Accept, User-Agent, Cookie, CF-Connecting-IP';
138
+ headers.set('Vary', existing ? dedupVary(`${existing}, ${required}`) : required);
139
+ }
140
+ function dedupVary(combined) {
141
+ const parts = combined
142
+ .split(',')
143
+ .map((s) => s.trim())
144
+ .filter(Boolean);
145
+ return [...new Set(parts)].join(', ');
146
+ }
147
+ function isHtmlResponse(response) {
148
+ const ct = response.headers.get('content-type') ?? '';
149
+ return ct.includes('text/html');
150
+ }
151
+ function twinUrlFor(url, aeo) {
152
+ const fn = aeo.twinUrl ?? ((u) => `${u.replace(/\/+$/, '')}.md`);
153
+ return fn(url.toString());
154
+ }
155
+ async function findItemForPath(contentProvider, context, pathname) {
156
+ // Look up a single item by path; narrow the fetch via `slugs` so CMS providers
157
+ // can short-circuit the lookup instead of loading the whole catalog.
158
+ const slug = pathname.replace(/^\/+/, '').split('/').pop() ?? '';
159
+ try {
160
+ // Type assertion: APIContext is the Astro request context in real code but our
161
+ // middleware context is narrower; contentProvider's APIContext parameter is
162
+ // treated opaquely at this layer.
163
+ const items = await contentProvider({ type: 'articles', slugs: [slug] }, context);
164
+ return items.find((item) => {
165
+ try {
166
+ return new URL(item.url).pathname === pathname;
167
+ }
168
+ catch {
169
+ return item.url === pathname;
170
+ }
171
+ });
172
+ }
173
+ catch {
174
+ return undefined;
175
+ }
176
+ }
177
+ async function serveAeoMarkdown(input) {
178
+ const { request, context, options, contentProvider, freshLayer, assets, waitUntil } = input;
179
+ const url = new URL(request.url);
180
+ const aeo = resolveAeoTwins(options.aeoTwins);
181
+ if (!aeo)
182
+ return null;
183
+ const twinPath = urlPathFromTwinUrl(twinUrlFor(url, aeo));
184
+ // Layer 1: fresh layer (R2/KV).
185
+ if (freshLayer) {
186
+ const hit = await readFreshTwin(freshLayer, twinPath);
187
+ if (hit) {
188
+ return markdownResponse(hit.body, options, hit.contentType);
189
+ }
190
+ }
191
+ // Layer 2: stale baseline via ASSETS binding.
192
+ if (assets) {
193
+ const assetReq = new Request(`https://assets.local${twinPath}`, { method: 'GET' });
194
+ const assetResp = await assets.fetch(assetReq);
195
+ if (assetResp.status === 200) {
196
+ const body = await assetResp.text();
197
+ return markdownResponse(body, options);
198
+ }
199
+ }
200
+ // Layer 3: fallthrough render.
201
+ const matched = await findItemForPath(contentProvider, context, url.pathname);
202
+ if (!matched) {
203
+ return null; // Let the route handler serve HTML as usual.
204
+ }
205
+ if (matched.access === 'members') {
206
+ // Never serve markdown for gated items via negotiation.
207
+ return null;
208
+ }
209
+ const body = matched.description ?? '';
210
+ const markdown = generateAeoMarkdown(matched, {
211
+ publisherName: options.organization.name,
212
+ schemaType: options.schemaType,
213
+ content: body,
214
+ ragChunkMarkers: aeo.ragChunkMarkers,
215
+ canonical: matched.url,
216
+ twinUrl: twinUrlFor(url, aeo),
217
+ });
218
+ // When the fresh layer is wired, return a 503 stub and schedule the write in
219
+ // the background. 200 OK with thin body would risk Googlebot indexing the
220
+ // description-only response as canonical and not re-crawling for days.
221
+ if (waitUntil && freshLayer) {
222
+ waitUntil(writeFreshTwin(freshLayer, twinPath, markdown).catch(() => { }));
223
+ return new Response('aeo twin generating', {
224
+ status: 503,
225
+ headers: {
226
+ 'Content-Type': 'text/plain; charset=utf-8',
227
+ 'Retry-After': '5',
228
+ 'X-Robots-Tag': 'noindex',
229
+ 'Cache-Control': 'no-store',
230
+ 'content-signal': buildContentSignalHeader(options),
231
+ ...varyHeaders(),
232
+ },
233
+ });
234
+ }
235
+ // Simple mode: no fresh layer wired (tests, static-first consumers without a
236
+ // Worker DO). Render inline synchronously. Still safe — we're not caching a
237
+ // thin stub, we're serving the full markdown.
238
+ return markdownResponse(markdown, options);
239
+ }
240
+ function markdownResponse(body, options, contentType = 'text/markdown; charset=utf-8') {
241
+ const tokenCount = estimateTokenCount(body);
242
+ return new Response(body, {
243
+ headers: {
244
+ 'Content-Type': contentType,
245
+ 'x-markdown-tokens': String(tokenCount),
246
+ 'X-Robots-Tag': 'noindex',
247
+ 'content-signal': buildContentSignalHeader(options),
248
+ ...varyHeaders(),
249
+ },
250
+ });
251
+ }
252
+ function urlPathFromTwinUrl(twinUrl) {
253
+ try {
254
+ return new URL(twinUrl).pathname;
255
+ }
256
+ catch {
257
+ return twinUrl.startsWith('/') ? twinUrl : `/${twinUrl}`;
258
+ }
259
+ }
260
+ export const _internals = {
261
+ appendVaryHeaders,
262
+ dedupVary,
263
+ twinUrlFor,
264
+ findItemForPath,
265
+ };
70
266
  //# sourceMappingURL=seo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"seo.js","sourceRoot":"","sources":["../../src/middleware/seo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"seo.js","sourceRoot":"","sources":["../../src/middleware/seo.ts"],"names":[],"mappings":"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"}