@growth-labs/seo 0.1.5 → 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 +101 -14
  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 +13 -1
@@ -0,0 +1,243 @@
1
+ import { resolveAeoTwins } from '../options.js';
2
+ import { getConfig, getContentProvider } from '../state.js';
3
+ import { generateAeoMarkdown } from '../utils/aeo.js';
4
+ import { generateSummaryTwin } from '../utils/aeo-summary.js';
5
+ import { deleteFreshTwin, writeFreshTwin } from '../utils/fresh-layer.js';
6
+ export const prerender = false;
7
+ const MAX_BATCH = 50;
8
+ const DEFAULT_RATE_LIMIT_RPM = 10;
9
+ /**
10
+ * POST /_seo/revalidate — CMS webhook target. Re-renders the listed slugs,
11
+ * writes to R2 under the current deployment prefix, purges the edge cache for
12
+ * the markdown sitemap so newly-published content is discoverable within seconds.
13
+ *
14
+ * Auth: Authorization: Bearer <revalidateToken>. Token is the same ≥32-byte
15
+ * value configured in aeoTwins.revalidateToken. Compared with constant-time
16
+ * string compare.
17
+ *
18
+ * Rate limit + per-slug lock + idempotency all delegate to the Revalidation
19
+ * Coordinator Durable Object (spec "Revalidation Coordinator DO topology").
20
+ */
21
+ export const POST = async (context) => {
22
+ const config = getConfig();
23
+ const contentProvider = getContentProvider();
24
+ const aeo = resolveAeoTwins(config.aeoTwins);
25
+ if (!aeo?.onDemandRevalidation || !aeo.freshLayer || !aeo.revalidateToken) {
26
+ return json({ error: 'revalidation_disabled' }, 404);
27
+ }
28
+ const env = context.locals?.runtime?.env;
29
+ if (!env) {
30
+ return json({ error: 'binding_unavailable' }, 503);
31
+ }
32
+ // 1. Auth: Bearer token, constant-time compare.
33
+ const authz = context.request.headers.get('Authorization') ?? '';
34
+ const bearerPrefix = 'Bearer ';
35
+ if (!authz.startsWith(bearerPrefix)) {
36
+ return json({ error: 'unauthorized' }, 401);
37
+ }
38
+ const presented = authz.slice(bearerPrefix.length);
39
+ if (!constantTimeEqual(presented, aeo.revalidateToken)) {
40
+ return json({ error: 'unauthorized' }, 401);
41
+ }
42
+ // 2. Parse body.
43
+ let body;
44
+ try {
45
+ body = (await context.request.json());
46
+ }
47
+ catch {
48
+ return json({ error: 'bad_json' }, 400);
49
+ }
50
+ const slugs = body.slugs ?? [];
51
+ if (!Array.isArray(slugs) || slugs.length === 0) {
52
+ return json({ error: 'no_slugs' }, 400);
53
+ }
54
+ if (slugs.length > MAX_BATCH) {
55
+ return json({ error: 'batch_too_large', limit: MAX_BATCH }, 400);
56
+ }
57
+ // 3. Resolve the Revalidation Coordinator DO for this hostname.
58
+ const hostname = new URL(context.request.url).host;
59
+ const coordBindingName = aeo.freshLayer.coordinatorBindingName;
60
+ const coordBinding = env[coordBindingName];
61
+ const coord = coordBinding ? coordBinding.get(coordBinding.idFromName(hostname)) : undefined;
62
+ // 4. Rate-limit check (per-token).
63
+ if (coord) {
64
+ const rl = await coordCall(coord, {
65
+ action: 'rate-check',
66
+ token: presented,
67
+ limitRpm: DEFAULT_RATE_LIMIT_RPM,
68
+ });
69
+ if (!rl.ok) {
70
+ return new Response(JSON.stringify({ error: 'rate_limited', retryAfterMs: rl.retryAfterMs }), {
71
+ status: 429,
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ 'Retry-After': String(Math.ceil(Number(rl.retryAfterMs ?? 5000) / 1000)),
75
+ },
76
+ });
77
+ }
78
+ }
79
+ // 5. Idempotency check — dedupe concurrent webhooks for the same CMS event.
80
+ if (coord && body.idempotency_key) {
81
+ const cached = await coordCall(coord, {
82
+ action: 'idempotency-check',
83
+ key: body.idempotency_key,
84
+ });
85
+ if (cached.ok && cached.hit) {
86
+ return json({ ok: true, deduped: true, ...cached.result });
87
+ }
88
+ }
89
+ // 6. Resolve fresh layer + content provider.
90
+ const freshLayerImpl = env[aeo.freshLayer.bindingName];
91
+ if (!freshLayerImpl || !contentProvider) {
92
+ return json({ error: 'binding_unavailable' }, 503);
93
+ }
94
+ const freshLayer = {
95
+ type: aeo.freshLayer.type,
96
+ impl: freshLayerImpl,
97
+ deploymentId: env.CF_VERSION_METADATA?.id ?? 'dev',
98
+ };
99
+ const outcomes = [];
100
+ for (const slug of slugs) {
101
+ const outcome = await revalidateSlug({
102
+ slug,
103
+ action: body.action ?? 'publish',
104
+ config,
105
+ contentProvider,
106
+ freshLayer,
107
+ coord,
108
+ });
109
+ outcomes.push(outcome);
110
+ }
111
+ // 7. Purge sitemap-markdown cache so newly-published articles show up.
112
+ await purgeSitemapMarkdownCache(context.request).catch(() => { });
113
+ // 8. Cache idempotency result for future replays.
114
+ if (coord && body.idempotency_key) {
115
+ await coordCall(coord, {
116
+ action: 'idempotency-set',
117
+ key: body.idempotency_key,
118
+ result: { outcomes },
119
+ });
120
+ }
121
+ return json({ ok: true, outcomes });
122
+ };
123
+ async function revalidateSlug(input) {
124
+ const { slug, action, config, contentProvider, freshLayer, coord } = input;
125
+ const start = Date.now();
126
+ // Acquire per-slug lock; if someone else holds it, serialize.
127
+ let leaseId;
128
+ if (coord) {
129
+ const acquire = await coordCall(coord, { action: 'acquire-lock', slug });
130
+ if (acquire.ok) {
131
+ leaseId = acquire.leaseId;
132
+ }
133
+ else if (acquire.error === 'locked') {
134
+ // Someone else is rendering this slug. Return without duplicating work;
135
+ // the winner's writeback will be visible to subsequent requests.
136
+ return { slug, outcome: 'serialized' };
137
+ }
138
+ else {
139
+ return { slug, outcome: 'error', error: acquire.error };
140
+ }
141
+ }
142
+ try {
143
+ if (action === 'unpublish') {
144
+ const url = new URL(`${config.site.replace(/\/$/, '')}/${slug}`).pathname;
145
+ const aeo = resolveAeoTwins(config.aeoTwins);
146
+ const twinPath = `${url.replace(/\/+$/, '')}.md`;
147
+ const summaryPath = `${twinPath}.summary.md`;
148
+ await deleteFreshTwin(freshLayer, twinPath);
149
+ if (aeo?.summaryTwin)
150
+ await deleteFreshTwin(freshLayer, summaryPath);
151
+ return { slug, outcome: 'deleted', durationMs: Date.now() - start };
152
+ }
153
+ // Publish / update.
154
+ let items;
155
+ try {
156
+ items = await contentProvider({ type: 'articles', slugs: [slug] }, {});
157
+ }
158
+ catch (err) {
159
+ return {
160
+ slug,
161
+ outcome: 'error',
162
+ error: err instanceof Error ? err.message : String(err),
163
+ };
164
+ }
165
+ const item = items.find((i) => i.url.endsWith(`/${slug}`) || i.url === slug);
166
+ if (!item)
167
+ return { slug, outcome: 'not_found' };
168
+ const body = item.description ?? '';
169
+ const aeo = resolveAeoTwins(config.aeoTwins);
170
+ const primaryUrl = (aeo?.twinUrl ?? defaultTwin)(item.url);
171
+ const summaryUrl = aeo?.summaryTwin ? `${primaryUrl}.summary.md` : undefined;
172
+ const markdown = generateAeoMarkdown(item, {
173
+ publisherName: config.organization.name,
174
+ schemaType: config.schemaType,
175
+ content: body,
176
+ ragChunkMarkers: aeo?.ragChunkMarkers,
177
+ canonical: item.url,
178
+ twinUrl: primaryUrl,
179
+ summaryUrl,
180
+ });
181
+ await writeFreshTwin(freshLayer, pathOf(primaryUrl), markdown);
182
+ if (aeo?.summaryTwin && summaryUrl) {
183
+ const summary = generateSummaryTwin(item, {
184
+ publisherName: config.organization.name,
185
+ schemaType: config.schemaType,
186
+ content: body,
187
+ fullUrl: primaryUrl,
188
+ });
189
+ await writeFreshTwin(freshLayer, pathOf(summaryUrl), summary.markdown);
190
+ }
191
+ return { slug, outcome: 'written', durationMs: Date.now() - start };
192
+ }
193
+ finally {
194
+ if (coord && leaseId) {
195
+ await coordCall(coord, { action: 'release-lock', slug, leaseId }).catch(() => { });
196
+ }
197
+ }
198
+ }
199
+ // ─── Helpers ───
200
+ async function coordCall(stub, body) {
201
+ try {
202
+ const res = await stub.fetch(new Request('https://internal/', {
203
+ method: 'POST',
204
+ body: JSON.stringify(body),
205
+ headers: { 'Content-Type': 'application/json' },
206
+ }));
207
+ return (await res.json());
208
+ }
209
+ catch {
210
+ return { ok: false, error: 'coord_unavailable' };
211
+ }
212
+ }
213
+ function constantTimeEqual(a, b) {
214
+ if (a.length !== b.length)
215
+ return false;
216
+ let diff = 0;
217
+ for (let i = 0; i < a.length; i++)
218
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
219
+ return diff === 0;
220
+ }
221
+ function defaultTwin(url) {
222
+ return `${url.replace(/\/+$/, '')}.md`;
223
+ }
224
+ function pathOf(url) {
225
+ try {
226
+ return new URL(url).pathname;
227
+ }
228
+ catch {
229
+ return url.startsWith('/') ? url : `/${url}`;
230
+ }
231
+ }
232
+ async function purgeSitemapMarkdownCache(request) {
233
+ const origin = new URL(request.url).origin;
234
+ const key = new Request(`${origin}/sitemap-markdown.xml`);
235
+ await caches.default.delete(key).catch(() => { });
236
+ }
237
+ function json(body, status = 200) {
238
+ return new Response(JSON.stringify(body), {
239
+ status,
240
+ headers: { 'Content-Type': 'application/json' },
241
+ });
242
+ }
243
+ //# sourceMappingURL=revalidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidate.js","sourceRoot":"","sources":["../../src/routes/revalidate.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../../src/routes/rss.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAKrC,eAAO,MAAM,SAAS,QAAQ,CAAA;AAE9B,eAAO,MAAM,GAAG,EAAE,QAgBjB,CAAA"}
1
+ {"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../../src/routes/rss.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAMrC,eAAO,MAAM,SAAS,QAAQ,CAAA;AAE9B,eAAO,MAAM,GAAG,EAAE,QAkBjB,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { getConfig, getContentProvider } from '../state.js';
2
+ import { forRss } from '../utils/content-filter.js';
2
3
  import { generateRssFeed } from '../utils/rss.js';
3
4
  export const prerender = false;
4
5
  export const GET = async (context) => {
@@ -11,7 +12,9 @@ export const GET = async (context) => {
11
12
  }
12
13
  catch { }
13
14
  }
14
- const xml = generateRssFeed(articles, config);
15
+ // Members items excluded unconditionally. Public items respect includeInFeed.
16
+ const filtered = forRss(articles);
17
+ const xml = generateRssFeed(filtered, config);
15
18
  return new Response(xml, {
16
19
  headers: { 'Content-Type': 'application/rss+xml; charset=utf-8' },
17
20
  });
@@ -1 +1 @@
1
- {"version":3,"file":"rss.js","sourceRoot":"","sources":["../../src/routes/rss.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAA;AAE9B,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAA;IAE5C,IAAI,QAAQ,GAAkB,EAAE,CAAA;IAChC,IAAI,eAAe,EAAE,CAAC;QACrB,IAAI,CAAC;YACJ,QAAQ,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,OAAc,CAAC,CAAA;QACvE,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED,MAAM,GAAG,GAAG,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAE7C,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE;QACxB,OAAO,EAAE,EAAE,cAAc,EAAE,oCAAoC,EAAE;KACjE,CAAC,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"rss.js","sourceRoot":"","sources":["../../src/routes/rss.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAA;AAE9B,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAA;IAE5C,IAAI,QAAQ,GAAkB,EAAE,CAAA;IAChC,IAAI,eAAe,EAAE,CAAC;QACrB,IAAI,CAAC;YACJ,QAAQ,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,CAAA;QAChE,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;IACjC,MAAM,GAAG,GAAG,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAE7C,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE;QACxB,OAAO,EAAE,EAAE,cAAc,EAAE,oCAAoC,EAAE;KACjE,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { APIRoute } from 'astro';
2
+ export declare const prerender = false;
3
+ export declare const GET: APIRoute;
4
+ //# sourceMappingURL=sitemap-markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap-markdown.d.ts","sourceRoot":"","sources":["../../src/routes/sitemap-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAMrC,eAAO,MAAM,SAAS,QAAQ,CAAA;AAE9B,eAAO,MAAM,GAAG,EAAE,QA6BjB,CAAA"}
@@ -0,0 +1,32 @@
1
+ import { resolveAeoTwins } from '../options.js';
2
+ import { getConfig, getContentProvider } from '../state.js';
3
+ import { generateMarkdownSitemap } from '../utils/sitemap-markdown.js';
4
+ export const prerender = false;
5
+ export const GET = async (context) => {
6
+ const config = getConfig();
7
+ const contentProvider = getContentProvider();
8
+ // Only emit when twin URLs actually exist on disk (static or both modes).
9
+ // Middleware-mode twins live at the same URL as HTML; no distinct twin URLs.
10
+ const aeo = resolveAeoTwins(config.aeoTwins);
11
+ if (!config.markdownSitemap || !aeo || aeo.mode === 'middleware') {
12
+ return new Response('sitemap-markdown not enabled', { status: 404 });
13
+ }
14
+ let articles = [];
15
+ if (contentProvider) {
16
+ try {
17
+ articles = await contentProvider({ type: 'articles' }, context);
18
+ }
19
+ catch { }
20
+ }
21
+ const xml = generateMarkdownSitemap({
22
+ items: articles,
23
+ twinUrl: aeo.twinUrl,
24
+ // R2-freshness freshLayerLastmod is wired at middleware level via the DO;
25
+ // at the build-route layer we use build-time dateModified. After revalidation
26
+ // writes, the consumer's cache purge causes this route to regenerate.
27
+ });
28
+ return new Response(xml, {
29
+ headers: { 'Content-Type': 'application/xml; charset=utf-8' },
30
+ });
31
+ };
32
+ //# sourceMappingURL=sitemap-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap-markdown.js","sourceRoot":"","sources":["../../src/routes/sitemap-markdown.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAE3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAEtE,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAA;AAE9B,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAA;IAE5C,0EAA0E;IAC1E,6EAA6E;IAC7E,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC5C,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAClE,OAAO,IAAI,QAAQ,CAAC,8BAA8B,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACrE,CAAC;IAED,IAAI,QAAQ,GAAkB,EAAE,CAAA;IAChC,IAAI,eAAe,EAAE,CAAC;QACrB,IAAI,CAAC;YACJ,QAAQ,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,CAAA;QAChE,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED,MAAM,GAAG,GAAG,uBAAuB,CAAC;QACnC,KAAK,EAAE,QAAQ;QACf,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,0EAA0E;QAC1E,8EAA8E;QAC9E,sEAAsE;KACtE,CAAC,CAAA;IAEF,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE;QACxB,OAAO,EAAE,EAAE,cAAc,EAAE,gCAAgC,EAAE;KAC7D,CAAC,CAAA;AACH,CAAC,CAAA"}
package/dist/types.d.ts CHANGED
@@ -3,6 +3,7 @@ export interface ContentAuthor {
3
3
  name: string;
4
4
  url?: string;
5
5
  jobTitle?: string;
6
+ knowsAbout?: string[];
6
7
  sameAs?: string[];
7
8
  }
8
9
  export interface ContentAudio {
@@ -52,6 +53,7 @@ export interface PodcastEpisodeMeta {
52
53
  seasonNumber?: number;
53
54
  episodeType?: 'full' | 'trailer' | 'bonus';
54
55
  }
56
+ export type ContentAccess = 'public' | 'members';
55
57
  export interface ContentItem {
56
58
  url: string;
57
59
  title: string;
@@ -64,6 +66,14 @@ export interface ContentItem {
64
66
  podcastEpisode?: PodcastEpisodeMeta;
65
67
  locale?: string;
66
68
  alternateLocales?: ContentLocaleAlternate[];
69
+ appleNewsId?: string;
70
+ appleNewsPublishable?: 'yes' | 'no';
71
+ appleNewsSection?: string;
72
+ newsKeywords?: string[];
73
+ access?: ContentAccess;
74
+ includeInSitemap?: boolean;
75
+ includeInFeed?: boolean;
76
+ summary?: string;
67
77
  isAccessibleForFree?: boolean;
68
78
  paywallCssSelector?: string;
69
79
  product?: ContentProduct;
@@ -84,9 +94,13 @@ export interface ContentItem {
84
94
  };
85
95
  }
86
96
  export type ContentType = 'articles' | 'pages' | 'videos' | 'products' | 'authors';
87
- export type ContentProvider = (params: {
97
+ export interface ContentProviderParams {
88
98
  type: ContentType;
89
- }, context: APIContext) => Promise<ContentItem[]>;
99
+ slugs?: string[];
100
+ }
101
+ export type ContentProvider = (params: ContentProviderParams, context: APIContext) => Promise<ContentItem[]>;
102
+ export type CrawlerClass = 'verifiedSearchCrawler' | 'llmTrainingCrawler' | 'userDirectedLlmAgent' | 'anonymous';
103
+ export type EffectiveAuthSegment = 'anon' | 'member' | 'search-full';
90
104
  export interface MetaTag {
91
105
  [key: string]: string;
92
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { ContentItem } from '../types.js';
2
+ export interface GenerateSummaryTwinOptions {
3
+ publisherName: string;
4
+ schemaType: string;
5
+ /** Full article body in markdown (used by tiers 2 and 3 when item.summary is absent). */
6
+ content?: string;
7
+ /** URL of the full twin for the `fullUrl` frontmatter cross-link. */
8
+ fullUrl: string;
9
+ /** Target cap in tokens. Default 400; hard ceiling. */
10
+ maxTokens?: number;
11
+ /** Emits a build-time warning callback when tier-4 fallback fires (spec 1483). */
12
+ onMinimalFallback?: (item: ContentItem) => void;
13
+ }
14
+ export interface GenerateSummaryTwinResult {
15
+ markdown: string;
16
+ tier: 1 | 2 | 3 | 4;
17
+ }
18
+ /**
19
+ * Generate a summary twin for a ContentItem via the 4-tier fallback chain
20
+ * (spec "Summary twin"):
21
+ *
22
+ * 1. item.summary (consumer-provided) — used verbatim.
23
+ * 2. Bullet-list extraction from the article body — 3-5 claims pulled from
24
+ * top-level `- ` / `* ` list items.
25
+ * 3. Narrative fallback — description + first sentence of each `## section`
26
+ * (max 5) + final sentence of article as conclusion.
27
+ * 4. Minimal — description only. NO frontmatter flag (LLMs would propagate it
28
+ * into user-visible citations per v5 review). Build-time telemetry only,
29
+ * via the onMinimalFallback callback.
30
+ *
31
+ * The returned markdown includes frontmatter: `type: summary`, `title`, `url`,
32
+ * `fullUrl`, `datePublished`/`dateModified` if set. First-match wins.
33
+ */
34
+ export declare function generateSummaryTwin(item: ContentItem, options: GenerateSummaryTwinOptions): GenerateSummaryTwinResult;
35
+ //# sourceMappingURL=aeo-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-summary.d.ts","sourceRoot":"","sources":["../../src/utils/aeo-summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAK9C,MAAM,WAAW,0BAA0B;IAC1C,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAA;CAC/C;AAED,MAAM,WAAW,yBAAyB;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CACnB;AAID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,0BAA0B,GACjC,yBAAyB,CAU3B"}
@@ -0,0 +1,141 @@
1
+ import { estimateTokenCount } from './aeo.js';
2
+ const DEFAULT_MAX_TOKENS = 400;
3
+ /**
4
+ * Generate a summary twin for a ContentItem via the 4-tier fallback chain
5
+ * (spec "Summary twin"):
6
+ *
7
+ * 1. item.summary (consumer-provided) — used verbatim.
8
+ * 2. Bullet-list extraction from the article body — 3-5 claims pulled from
9
+ * top-level `- ` / `* ` list items.
10
+ * 3. Narrative fallback — description + first sentence of each `## section`
11
+ * (max 5) + final sentence of article as conclusion.
12
+ * 4. Minimal — description only. NO frontmatter flag (LLMs would propagate it
13
+ * into user-visible citations per v5 review). Build-time telemetry only,
14
+ * via the onMinimalFallback callback.
15
+ *
16
+ * The returned markdown includes frontmatter: `type: summary`, `title`, `url`,
17
+ * `fullUrl`, `datePublished`/`dateModified` if set. First-match wins.
18
+ */
19
+ export function generateSummaryTwin(item, options) {
20
+ const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
21
+ const { body, tier } = pickBody(item, options.content, options.onMinimalFallback);
22
+ const capped = capToTokens(body, maxTokens);
23
+ const fm = buildFrontmatter(item, options);
24
+ const markdown = `---\n${fm}\n---\n\n${capped}`;
25
+ return { markdown, tier };
26
+ }
27
+ // ─── Internals ───
28
+ function pickBody(item, content, onMinimalFallback) {
29
+ // Tier 1: explicit summary
30
+ if (item.summary && item.summary.trim().length > 0) {
31
+ return { body: item.summary.trim(), tier: 1 };
32
+ }
33
+ // Tier 2: bullet-list extraction
34
+ if (content) {
35
+ const bullets = extractTopLevelBullets(content);
36
+ if (bullets.length >= 3) {
37
+ const selected = bullets.slice(0, 5);
38
+ const lede = item.description?.trim() ?? item.title;
39
+ return {
40
+ body: `${lede}\n\n${selected.map((b) => `- ${b}`).join('\n')}`,
41
+ tier: 2,
42
+ };
43
+ }
44
+ }
45
+ // Tier 3: narrative fallback
46
+ if (content) {
47
+ const sectionFirsts = extractFirstSentencePerSection(content).slice(0, 5);
48
+ const lastSentence = extractLastSentence(content);
49
+ const lede = item.description?.trim();
50
+ const narrativeParts = [];
51
+ if (lede)
52
+ narrativeParts.push(lede);
53
+ if (sectionFirsts.length > 0) {
54
+ narrativeParts.push(sectionFirsts.map((s) => `- ${s}`).join('\n'));
55
+ }
56
+ if (lastSentence)
57
+ narrativeParts.push(lastSentence);
58
+ const narrative = narrativeParts.join('\n\n');
59
+ if (narrative.length >= 100) {
60
+ return { body: narrative, tier: 3 };
61
+ }
62
+ }
63
+ // Tier 4: minimal fallback
64
+ onMinimalFallback?.(item);
65
+ return { body: item.description ?? item.title, tier: 4 };
66
+ }
67
+ function extractTopLevelBullets(md) {
68
+ const out = [];
69
+ // Match top-level (no leading whitespace) `- ` or `* ` bullets.
70
+ const re = /^[ \t]*(?:[-*])\s+(.+)$/gm;
71
+ let m = re.exec(md);
72
+ while (m !== null) {
73
+ const line = m[1]?.trim();
74
+ if (line && line.length > 0)
75
+ out.push(line);
76
+ m = re.exec(md);
77
+ }
78
+ return out;
79
+ }
80
+ function extractFirstSentencePerSection(md) {
81
+ const out = [];
82
+ // Split on `## ` headings; skip the heading line itself and find first sentence of body.
83
+ const sections = md.split(/^## .+$/gm).slice(1);
84
+ for (const section of sections) {
85
+ const firstPara = section
86
+ .split(/\n{2,}/)
87
+ .map((p) => p.trim())
88
+ .find((p) => p.length > 0 && !p.startsWith('#'));
89
+ if (firstPara) {
90
+ const sentence = firstSentence(firstPara);
91
+ if (sentence)
92
+ out.push(sentence);
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+ function extractLastSentence(md) {
98
+ // Take the last non-empty paragraph, pick its last sentence.
99
+ const paras = md
100
+ .split(/\n{2,}/)
101
+ .map((p) => p.trim())
102
+ .filter((p) => p.length > 0 && !p.startsWith('#'));
103
+ const lastPara = paras[paras.length - 1];
104
+ if (!lastPara)
105
+ return undefined;
106
+ const sentences = lastPara.split(/(?<=[.!?])\s+/);
107
+ return sentences[sentences.length - 1]?.trim();
108
+ }
109
+ function firstSentence(text) {
110
+ const match = /^(.+?[.!?])(?:\s|$)/.exec(text);
111
+ return match?.[1]?.trim() ?? text.split('\n')[0]?.trim();
112
+ }
113
+ function capToTokens(body, maxTokens) {
114
+ if (estimateTokenCount(body) <= maxTokens)
115
+ return body;
116
+ const maxChars = maxTokens * 4;
117
+ return `${body.slice(0, maxChars).trimEnd()}…`;
118
+ }
119
+ function buildFrontmatter(item, options) {
120
+ const lines = [];
121
+ lines.push(`type: summary`);
122
+ lines.push(`title: ${yv(item.title)}`);
123
+ lines.push(`url: ${yv(`${options.fullUrl}.summary.md`)}`);
124
+ lines.push(`fullUrl: ${yv(options.fullUrl)}`);
125
+ if (item.datePublished)
126
+ lines.push(`datePublished: ${yv(item.datePublished)}`);
127
+ if (item.dateModified)
128
+ lines.push(`dateModified: ${yv(item.dateModified)}`);
129
+ lines.push(`publisher: ${yv(options.publisherName)}`);
130
+ lines.push(`schemaType: ${yv(options.schemaType)}`);
131
+ return lines.join('\n');
132
+ }
133
+ function yv(v) {
134
+ const isUrl = /^https?:\/\//.test(v);
135
+ const isDate = /^\d{4}-\d{2}-\d{2}/.test(v);
136
+ const needsQuotes = /[#{}[\],&*?|>!%@`'"\n]/.test(v) || (v.includes(':') && !isUrl && !isDate);
137
+ if (!needsQuotes)
138
+ return v;
139
+ return `"${v.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
140
+ }
141
+ //# sourceMappingURL=aeo-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-summary.js","sourceRoot":"","sources":["../../src/utils/aeo-summary.ts"],"names":[],"mappings":"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"}