@growth-labs/seo 0.1.5 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -54
- package/dist/bindings.d.ts +127 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +11 -0
- package/dist/bindings.js.map +1 -0
- package/dist/cron/prune-aeo-r2.d.ts +36 -0
- package/dist/cron/prune-aeo-r2.d.ts.map +1 -0
- package/dist/cron/prune-aeo-r2.js +94 -0
- package/dist/cron/prune-aeo-r2.js.map +1 -0
- package/dist/durable-objects/aeo-revalidation-coord.d.ts +69 -0
- package/dist/durable-objects/aeo-revalidation-coord.d.ts.map +1 -0
- package/dist/durable-objects/aeo-revalidation-coord.js +177 -0
- package/dist/durable-objects/aeo-revalidation-coord.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -12
- package/dist/index.js.map +1 -1
- package/dist/middleware/seo.d.ts +44 -4
- package/dist/middleware/seo.d.ts.map +1 -1
- package/dist/middleware/seo.js +237 -41
- package/dist/middleware/seo.js.map +1 -1
- package/dist/options.d.ts +1293 -6
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +238 -1
- package/dist/options.js.map +1 -1
- package/dist/routes/aeo-twin.d.ts +5 -0
- package/dist/routes/aeo-twin.d.ts.map +1 -0
- package/dist/routes/aeo-twin.js +108 -0
- package/dist/routes/aeo-twin.js.map +1 -0
- package/dist/routes/apple-news.d.ts +4 -0
- package/dist/routes/apple-news.d.ts.map +1 -0
- package/dist/routes/apple-news.js +28 -0
- package/dist/routes/apple-news.js.map +1 -0
- package/dist/routes/llms-full.d.ts +4 -0
- package/dist/routes/llms-full.d.ts.map +1 -0
- package/dist/routes/llms-full.js +29 -0
- package/dist/routes/llms-full.js.map +1 -0
- package/dist/routes/revalidate.d.ts +16 -0
- package/dist/routes/revalidate.d.ts.map +1 -0
- package/dist/routes/revalidate.js +243 -0
- package/dist/routes/revalidate.js.map +1 -0
- package/dist/routes/rss.d.ts.map +1 -1
- package/dist/routes/rss.js +4 -1
- package/dist/routes/rss.js.map +1 -1
- package/dist/routes/sitemap-markdown.d.ts +4 -0
- package/dist/routes/sitemap-markdown.d.ts.map +1 -0
- package/dist/routes/sitemap-markdown.js +32 -0
- package/dist/routes/sitemap-markdown.js.map +1 -0
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/aeo-summary.d.ts +35 -0
- package/dist/utils/aeo-summary.d.ts.map +1 -0
- package/dist/utils/aeo-summary.js +141 -0
- package/dist/utils/aeo-summary.js.map +1 -0
- package/dist/utils/aeo-twin-emitter.d.ts +79 -0
- package/dist/utils/aeo-twin-emitter.d.ts.map +1 -0
- package/dist/utils/aeo-twin-emitter.js +99 -0
- package/dist/utils/aeo-twin-emitter.js.map +1 -0
- package/dist/utils/aeo.d.ts +62 -12
- package/dist/utils/aeo.d.ts.map +1 -1
- package/dist/utils/aeo.js +187 -26
- package/dist/utils/aeo.js.map +1 -1
- package/dist/utils/apple-news-anf.d.ts +38 -0
- package/dist/utils/apple-news-anf.d.ts.map +1 -0
- package/dist/utils/apple-news-anf.js +120 -0
- package/dist/utils/apple-news-anf.js.map +1 -0
- package/dist/utils/apple-news-rss.d.ts +31 -0
- package/dist/utils/apple-news-rss.d.ts.map +1 -0
- package/dist/utils/apple-news-rss.js +103 -0
- package/dist/utils/apple-news-rss.js.map +1 -0
- package/dist/utils/content-filter.d.ts +52 -0
- package/dist/utils/content-filter.d.ts.map +1 -0
- package/dist/utils/content-filter.js +75 -0
- package/dist/utils/content-filter.js.map +1 -0
- package/dist/utils/crawler-class.d.ts +39 -0
- package/dist/utils/crawler-class.d.ts.map +1 -0
- package/dist/utils/crawler-class.js +127 -0
- package/dist/utils/crawler-class.js.map +1 -0
- package/dist/utils/effective-auth.d.ts +28 -0
- package/dist/utils/effective-auth.d.ts.map +1 -0
- package/dist/utils/effective-auth.js +33 -0
- package/dist/utils/effective-auth.js.map +1 -0
- package/dist/utils/fcrdns.d.ts +73 -0
- package/dist/utils/fcrdns.d.ts.map +1 -0
- package/dist/utils/fcrdns.js +219 -0
- package/dist/utils/fcrdns.js.map +1 -0
- package/dist/utils/fresh-layer.d.ts +53 -0
- package/dist/utils/fresh-layer.d.ts.map +1 -0
- package/dist/utils/fresh-layer.js +147 -0
- package/dist/utils/fresh-layer.js.map +1 -0
- package/dist/utils/index.d.ts +14 -3
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +14 -3
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/json-ld/article.d.ts +13 -1
- package/dist/utils/json-ld/article.d.ts.map +1 -1
- package/dist/utils/json-ld/article.js +37 -8
- package/dist/utils/json-ld/article.js.map +1 -1
- package/dist/utils/llms-full.d.ts +29 -0
- package/dist/utils/llms-full.d.ts.map +1 -0
- package/dist/utils/llms-full.js +67 -0
- package/dist/utils/llms-full.js.map +1 -0
- package/dist/utils/meta.d.ts +4 -1
- package/dist/utils/meta.d.ts.map +1 -1
- package/dist/utils/meta.js +25 -2
- package/dist/utils/meta.js.map +1 -1
- package/dist/utils/sitemap-markdown.d.ts +24 -0
- package/dist/utils/sitemap-markdown.d.ts.map +1 -0
- package/dist/utils/sitemap-markdown.js +57 -0
- package/dist/utils/sitemap-markdown.js.map +1 -0
- package/dist/utils/staleness.d.ts +27 -0
- package/dist/utils/staleness.d.ts.map +1 -0
- package/dist/utils/staleness.js +46 -0
- package/dist/utils/staleness.js.map +1 -0
- package/dist/utils/validation.d.ts +41 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +78 -0
- package/dist/utils/validation.js.map +1 -1
- package/package.json +13 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Revalidation Coordinator — a single Durable Object class that manages three
|
|
2
|
+
// concerns for the revalidation endpoint and middleware fallthrough path:
|
|
3
|
+
//
|
|
4
|
+
// rl:<token> — token-bucket rate-limit state (10 RPM per token)
|
|
5
|
+
// lock:<slug> — per-slug advisory lock (30s max hold)
|
|
6
|
+
// idempotency:<key> — response dedup cache (10-minute TTL)
|
|
7
|
+
//
|
|
8
|
+
// One DO instance per request-hostname (derived from url.host at request time,
|
|
9
|
+
// NOT build-time options.site). Multi-tenant Workers serving multiple domains
|
|
10
|
+
// from one deployment get per-tenant isolation.
|
|
11
|
+
//
|
|
12
|
+
// Uses the async KV API (ctx.storage.get/put/delete/list) on the SQLite-backed
|
|
13
|
+
// DO runtime — no raw SQL. TTL eviction runs via a self-scheduled 5-minute
|
|
14
|
+
// alarm; if an alarm misses (Worker restart), the next write re-arms it.
|
|
15
|
+
//
|
|
16
|
+
// Export the class from `@growth-labs/seo/durable-objects`; consumers re-export
|
|
17
|
+
// from their Worker entrypoint per Cloudflare DO conventions.
|
|
18
|
+
const LOCK_DEFAULT_LEASE_MS = 30_000;
|
|
19
|
+
const IDEMPOTENCY_DEFAULT_TTL_MS = 10 * 60 * 1000;
|
|
20
|
+
const ALARM_INTERVAL_MS = 5 * 60 * 1000;
|
|
21
|
+
const RATE_LIMIT_MAX_IDLE_MS = 10 * 60 * 1000;
|
|
22
|
+
// ─── DO class ───
|
|
23
|
+
export class AeoRevalidationCoordinator {
|
|
24
|
+
ctx;
|
|
25
|
+
constructor(ctx) {
|
|
26
|
+
this.ctx = ctx;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Dispatch a JSON-encoded CoordRequest. Returns a JSON response. Not a
|
|
30
|
+
* public HTTP API — called from the consumer's Worker via the namespace
|
|
31
|
+
* binding: `await stub.fetch(new Request('https://internal/', { method: 'POST', body: JSON.stringify(req) }))`.
|
|
32
|
+
*/
|
|
33
|
+
async fetch(request) {
|
|
34
|
+
if (request.method !== 'POST') {
|
|
35
|
+
return json({ ok: false, error: 'method_not_allowed' }, 405);
|
|
36
|
+
}
|
|
37
|
+
let body;
|
|
38
|
+
try {
|
|
39
|
+
body = (await request.json());
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return json({ ok: false, error: 'bad_json' }, 400);
|
|
43
|
+
}
|
|
44
|
+
await this.ensureAlarm();
|
|
45
|
+
switch (body.action) {
|
|
46
|
+
case 'rate-check':
|
|
47
|
+
return json(await this.rateCheck(body.token, body.limitRpm));
|
|
48
|
+
case 'acquire-lock':
|
|
49
|
+
return json(await this.acquireLock(body.slug, body.leaseMs ?? LOCK_DEFAULT_LEASE_MS));
|
|
50
|
+
case 'release-lock':
|
|
51
|
+
return json(await this.releaseLock(body.slug, body.leaseId));
|
|
52
|
+
case 'idempotency-check':
|
|
53
|
+
return json(await this.idempotencyCheck(body.key));
|
|
54
|
+
case 'idempotency-set':
|
|
55
|
+
return json(await this.idempotencySet(body.key, body.result, body.ttlMs ?? IDEMPOTENCY_DEFAULT_TTL_MS));
|
|
56
|
+
default:
|
|
57
|
+
return json({ ok: false, error: 'unknown_action' }, 400);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Alarm handler: runs every 5 minutes, sweeps expired entries across all
|
|
62
|
+
* three namespaces. Self-re-arming.
|
|
63
|
+
*/
|
|
64
|
+
async alarm() {
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
// rate-limit: delete token buckets that haven't been refilled in >10 min.
|
|
67
|
+
const rlEntries = await this.ctx.storage.list({ prefix: 'rl:' });
|
|
68
|
+
for (const [key, entry] of rlEntries) {
|
|
69
|
+
if (now - entry.lastRefill > RATE_LIMIT_MAX_IDLE_MS) {
|
|
70
|
+
await this.ctx.storage.delete(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// lock: delete expired leases.
|
|
74
|
+
const lockEntries = await this.ctx.storage.list({ prefix: 'lock:' });
|
|
75
|
+
for (const [key, entry] of lockEntries) {
|
|
76
|
+
if (now >= entry.expiresAt)
|
|
77
|
+
await this.ctx.storage.delete(key);
|
|
78
|
+
}
|
|
79
|
+
// idempotency: delete expired entries.
|
|
80
|
+
const idemEntries = await this.ctx.storage.list({ prefix: 'idempotency:' });
|
|
81
|
+
for (const [key, entry] of idemEntries) {
|
|
82
|
+
if (now >= entry.expiresAt)
|
|
83
|
+
await this.ctx.storage.delete(key);
|
|
84
|
+
}
|
|
85
|
+
// Re-arm the alarm.
|
|
86
|
+
await this.ctx.storage.setAlarm(now + ALARM_INTERVAL_MS);
|
|
87
|
+
}
|
|
88
|
+
// ─── Rate limit ───
|
|
89
|
+
async rateCheck(token, limitRpm) {
|
|
90
|
+
const key = `rl:${token}`;
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const existing = await this.ctx.storage.get(key);
|
|
93
|
+
const refillRate = limitRpm / 60_000; // tokens per ms
|
|
94
|
+
let tokens;
|
|
95
|
+
if (existing) {
|
|
96
|
+
const elapsed = now - existing.lastRefill;
|
|
97
|
+
tokens = Math.min(limitRpm, existing.tokens + elapsed * refillRate);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
tokens = limitRpm;
|
|
101
|
+
}
|
|
102
|
+
if (tokens < 1) {
|
|
103
|
+
const retryAfterMs = Math.ceil((1 - tokens) / refillRate);
|
|
104
|
+
return { ok: false, error: 'rate_limited', retryAfterMs };
|
|
105
|
+
}
|
|
106
|
+
const next = { tokens: tokens - 1, lastRefill: now, capacity: limitRpm };
|
|
107
|
+
await this.ctx.storage.put(key, next);
|
|
108
|
+
return { ok: true, remaining: Math.floor(next.tokens) };
|
|
109
|
+
}
|
|
110
|
+
// ─── Per-slug lock ───
|
|
111
|
+
async acquireLock(slug, leaseMs) {
|
|
112
|
+
const key = `lock:${slug}`;
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
const existing = await this.ctx.storage.get(key);
|
|
115
|
+
if (existing && now < existing.expiresAt) {
|
|
116
|
+
return { ok: false, error: 'locked', expiresAt: existing.expiresAt };
|
|
117
|
+
}
|
|
118
|
+
const leaseId = newLeaseId();
|
|
119
|
+
const entry = { leaseId, expiresAt: now + leaseMs };
|
|
120
|
+
await this.ctx.storage.put(key, entry);
|
|
121
|
+
return { ok: true, leaseId, expiresAt: entry.expiresAt };
|
|
122
|
+
}
|
|
123
|
+
async releaseLock(slug, leaseId) {
|
|
124
|
+
const key = `lock:${slug}`;
|
|
125
|
+
const existing = await this.ctx.storage.get(key);
|
|
126
|
+
if (!existing)
|
|
127
|
+
return { ok: true, note: 'not_held' };
|
|
128
|
+
if (existing.leaseId !== leaseId) {
|
|
129
|
+
return { ok: false, error: 'lease_mismatch' };
|
|
130
|
+
}
|
|
131
|
+
await this.ctx.storage.delete(key);
|
|
132
|
+
return { ok: true };
|
|
133
|
+
}
|
|
134
|
+
// ─── Idempotency ───
|
|
135
|
+
async idempotencyCheck(key) {
|
|
136
|
+
const storageKey = `idempotency:${key}`;
|
|
137
|
+
const entry = await this.ctx.storage.get(storageKey);
|
|
138
|
+
if (!entry || Date.now() >= entry.expiresAt) {
|
|
139
|
+
return { ok: true, hit: false };
|
|
140
|
+
}
|
|
141
|
+
return { ok: true, hit: true, result: entry.result };
|
|
142
|
+
}
|
|
143
|
+
async idempotencySet(key, result, ttlMs) {
|
|
144
|
+
const storageKey = `idempotency:${key}`;
|
|
145
|
+
const entry = { result, expiresAt: Date.now() + ttlMs };
|
|
146
|
+
await this.ctx.storage.put(storageKey, entry);
|
|
147
|
+
return { ok: true };
|
|
148
|
+
}
|
|
149
|
+
// ─── Alarm management ───
|
|
150
|
+
async ensureAlarm() {
|
|
151
|
+
const current = await this.ctx.storage.getAlarm();
|
|
152
|
+
if (current === null) {
|
|
153
|
+
await this.ctx.storage.setAlarm(Date.now() + ALARM_INTERVAL_MS);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// ─── Helpers ───
|
|
158
|
+
function json(body, status = 200) {
|
|
159
|
+
return new Response(JSON.stringify(body), {
|
|
160
|
+
status,
|
|
161
|
+
headers: { 'content-type': 'application/json' },
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function newLeaseId() {
|
|
165
|
+
const bytes = new Uint8Array(16);
|
|
166
|
+
crypto.getRandomValues(bytes);
|
|
167
|
+
let hex = '';
|
|
168
|
+
for (const b of bytes)
|
|
169
|
+
hex += b.toString(16).padStart(2, '0');
|
|
170
|
+
return hex;
|
|
171
|
+
}
|
|
172
|
+
export const _internals = {
|
|
173
|
+
LOCK_DEFAULT_LEASE_MS,
|
|
174
|
+
IDEMPOTENCY_DEFAULT_TTL_MS,
|
|
175
|
+
ALARM_INTERVAL_MS,
|
|
176
|
+
};
|
|
177
|
+
//# sourceMappingURL=aeo-revalidation-coord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aeo-revalidation-coord.js","sourceRoot":"","sources":["../../src/durable-objects/aeo-revalidation-coord.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AstroIntegration } from 'astro';
|
|
|
2
2
|
import { type SeoOptions } from './options.js';
|
|
3
3
|
export default function seo(userOptions: SeoOptions): AstroIntegration;
|
|
4
4
|
export type { ResolvedSeoOptions, SeoOptions } from './options.js';
|
|
5
|
-
export { seoOptionsSchema } from './options.js';
|
|
5
|
+
export { resolveAeoTwins, seoOptionsSchema, validatedSeoOptionsSchema } from './options.js';
|
|
6
6
|
export { getConfig, getContentProvider } from './state.js';
|
|
7
7
|
export type * from './types.js';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAmB,KAAK,UAAU,EAA6B,MAAM,cAAc,CAAA;AAW1F,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,WAAW,EAAE,UAAU,GAAG,gBAAgB,CA0NrE;AAeD,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAC3F,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC1D,mBAAmB,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import {
|
|
4
|
+
import { resolveAeoTwins, validatedSeoOptionsSchema } from './options.js';
|
|
5
5
|
import { _setConfig, _setContentProvider } from './state.js';
|
|
6
6
|
import { SITEMAP_INDEX_PATH } from './utils/sitemap.js';
|
|
7
|
-
import { validateJsonLd, validatePage } from './utils/validation.js';
|
|
7
|
+
import { validateJsonLd, validatePage, validatePrerenderedGatedRoutes } from './utils/validation.js';
|
|
8
8
|
import { growthLabsSeoPlugin } from './vite-plugin.js';
|
|
9
9
|
function resolveEntrypoint(path) {
|
|
10
10
|
const ext = typeof import.meta.url === 'string' && import.meta.url.endsWith('.ts') ? '.ts' : '.js';
|
|
11
11
|
return fileURLToPath(new URL(`${path}${ext}`, import.meta.url));
|
|
12
12
|
}
|
|
13
13
|
export default function seo(userOptions) {
|
|
14
|
-
const options =
|
|
14
|
+
const options = validatedSeoOptionsSchema.parse(userOptions);
|
|
15
|
+
const aeo = resolveAeoTwins(options.aeoTwins);
|
|
15
16
|
_setConfig(options);
|
|
16
17
|
if (userOptions.contentProvider) {
|
|
17
18
|
_setContentProvider(userOptions.contentProvider);
|
|
@@ -22,7 +23,7 @@ export default function seo(userOptions) {
|
|
|
22
23
|
'astro:config:setup': ({ addMiddleware, injectRoute, updateConfig }) => {
|
|
23
24
|
updateConfig({ vite: { plugins: [growthLabsSeoPlugin(options)] } });
|
|
24
25
|
addMiddleware({ entrypoint: resolveEntrypoint('./middleware/seo'), order: 'post' });
|
|
25
|
-
// Sitemaps
|
|
26
|
+
// ─── Sitemaps ───
|
|
26
27
|
injectRoute({
|
|
27
28
|
pattern: SITEMAP_INDEX_PATH,
|
|
28
29
|
entrypoint: resolveEntrypoint('./routes/sitemap-index'),
|
|
@@ -50,7 +51,15 @@ export default function seo(userOptions) {
|
|
|
50
51
|
prerender: false,
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
|
-
//
|
|
54
|
+
// Markdown sitemap — only emitted when twin URLs exist (static or both mode).
|
|
55
|
+
if (options.markdownSitemap && aeo && aeo.mode !== 'middleware') {
|
|
56
|
+
injectRoute({
|
|
57
|
+
pattern: '/sitemap-markdown.xml',
|
|
58
|
+
entrypoint: resolveEntrypoint('./routes/sitemap-markdown'),
|
|
59
|
+
prerender: false,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// ─── Robots + llms.txt + llms-full.txt ───
|
|
54
63
|
injectRoute({
|
|
55
64
|
pattern: '/robots.txt',
|
|
56
65
|
entrypoint: resolveEntrypoint('./routes/robots'),
|
|
@@ -63,7 +72,14 @@ export default function seo(userOptions) {
|
|
|
63
72
|
prerender: false,
|
|
64
73
|
});
|
|
65
74
|
}
|
|
66
|
-
|
|
75
|
+
if (options.llmsFullTxt) {
|
|
76
|
+
injectRoute({
|
|
77
|
+
pattern: '/llms-full.txt',
|
|
78
|
+
entrypoint: resolveEntrypoint('./routes/llms-full'),
|
|
79
|
+
prerender: false,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// ─── RSS ───
|
|
67
83
|
if (options.rss) {
|
|
68
84
|
injectRoute({
|
|
69
85
|
pattern: '/feed.xml',
|
|
@@ -71,7 +87,15 @@ export default function seo(userOptions) {
|
|
|
71
87
|
prerender: false,
|
|
72
88
|
});
|
|
73
89
|
}
|
|
74
|
-
//
|
|
90
|
+
// ─── Apple News Publisher RSS ───
|
|
91
|
+
if (options.appleNews?.enabled) {
|
|
92
|
+
injectRoute({
|
|
93
|
+
pattern: options.appleNews.feedPath,
|
|
94
|
+
entrypoint: resolveEntrypoint('./routes/apple-news'),
|
|
95
|
+
prerender: false,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// ─── Podcast ───
|
|
75
99
|
if (options.podcast?.enabled) {
|
|
76
100
|
injectRoute({
|
|
77
101
|
pattern: options.podcast.feedPath,
|
|
@@ -79,7 +103,7 @@ export default function seo(userOptions) {
|
|
|
79
103
|
prerender: false,
|
|
80
104
|
});
|
|
81
105
|
}
|
|
82
|
-
// Narrated articles podcast feed
|
|
106
|
+
// ─── Narrated articles podcast feed ───
|
|
83
107
|
if (options.audioNarration?.asPodcastFeed) {
|
|
84
108
|
injectRoute({
|
|
85
109
|
pattern: options.audioNarration.podcastFeedPath,
|
|
@@ -87,11 +111,55 @@ export default function seo(userOptions) {
|
|
|
87
111
|
prerender: false,
|
|
88
112
|
});
|
|
89
113
|
}
|
|
114
|
+
// ─── Revalidation endpoint ───
|
|
115
|
+
if (aeo?.onDemandRevalidation) {
|
|
116
|
+
injectRoute({
|
|
117
|
+
pattern: '/_seo/revalidate',
|
|
118
|
+
entrypoint: resolveEntrypoint('./routes/revalidate'),
|
|
119
|
+
prerender: false,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// ─── AEO twin emission (static + both modes) ───
|
|
123
|
+
// Injected as a prerender route so contentProvider runs inside Astro's
|
|
124
|
+
// build pipeline. Raw-Node astro:build:done couldn't resolve Vite-virtual
|
|
125
|
+
// imports like `astro:content` that consumers commonly use in their
|
|
126
|
+
// contentProvider implementations.
|
|
127
|
+
if (aeo && aeo.mode !== 'middleware') {
|
|
128
|
+
injectRoute({
|
|
129
|
+
pattern: '/[...aeoPath].md',
|
|
130
|
+
entrypoint: resolveEntrypoint('./routes/aeo-twin'),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
90
133
|
},
|
|
91
|
-
'astro:build:done': async ({ dir, logger }) => {
|
|
134
|
+
'astro:build:done': async ({ dir, pages, logger }) => {
|
|
135
|
+
const outDir = fileURLToPath(dir);
|
|
136
|
+
const contentProvider = userOptions.contentProvider;
|
|
137
|
+
// ─── Build-time prerender-gated guard (test 32) ───
|
|
138
|
+
// Note: we try to load items here to check the guard, but this is
|
|
139
|
+
// best-effort. If contentProvider uses astro:content or other Vite
|
|
140
|
+
// virtuals, it will fail at raw-Node import time and we skip silently.
|
|
141
|
+
// The guard is a defence-in-depth check; the primary guarantee comes
|
|
142
|
+
// from the consumer-set `export const prerender = false` on gated routes.
|
|
143
|
+
if (contentProvider && options.flexibleSampling?.enabled) {
|
|
144
|
+
try {
|
|
145
|
+
const items = await contentProvider({ type: 'articles' }, {});
|
|
146
|
+
const prerenderedUrls = new Set(pages.map((p) => `/${p.pathname}`.replace(/\/+/g, '/')));
|
|
147
|
+
const issues = validatePrerenderedGatedRoutes({ prerenderedUrls, items });
|
|
148
|
+
if (issues.length > 0) {
|
|
149
|
+
for (const issue of issues)
|
|
150
|
+
logger.error(issue.message);
|
|
151
|
+
throw new Error(`[@growth-labs/seo] ${issues.length} prerendered route(s) serve members-gated items. See errors above.`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
// Don't fail the build if the provider can't run from Node context —
|
|
156
|
+
// the injected prerender route handles the primary emission path.
|
|
157
|
+
logger.info(`Skipped prerender-gated-content guard (contentProvider not Node-ESM-safe): ${err instanceof Error ? err.message : String(err)}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// ─── Legacy per-page validation ───
|
|
92
161
|
if (!options.validation.enabled)
|
|
93
162
|
return;
|
|
94
|
-
const outDir = fileURLToPath(dir);
|
|
95
163
|
const htmlFiles = findHtmlFiles(outDir);
|
|
96
164
|
let errorCount = 0;
|
|
97
165
|
let warningCount = 0;
|
|
@@ -111,7 +179,6 @@ export default function seo(userOptions) {
|
|
|
111
179
|
logger.warn(`${relPath}: ${warning}`);
|
|
112
180
|
warningCount++;
|
|
113
181
|
}
|
|
114
|
-
// Validate JSON-LD blocks
|
|
115
182
|
const jsonLdMatches = html.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/gi);
|
|
116
183
|
for (const match of jsonLdMatches) {
|
|
117
184
|
try {
|
|
@@ -157,6 +224,6 @@ function findHtmlFiles(dir) {
|
|
|
157
224
|
catch { }
|
|
158
225
|
return results;
|
|
159
226
|
}
|
|
160
|
-
export { seoOptionsSchema } from './options.js';
|
|
227
|
+
export { resolveAeoTwins, seoOptionsSchema, validatedSeoOptionsSchema } from './options.js';
|
|
161
228
|
export { getConfig, getContentProvider } from './state.js';
|
|
162
229
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAmB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,eAAe,EAAmB,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAC1F,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAA;AACpG,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAEtD,SAAS,iBAAiB,CAAC,IAAY;IACtC,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAA;IAClG,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAChE,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,WAAuB;IAClD,MAAM,OAAO,GAAG,yBAAyB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAC5D,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE7C,UAAU,CAAC,OAAO,CAAC,CAAA;IACnB,IAAI,WAAW,CAAC,eAAe,EAAE,CAAC;QACjC,mBAAmB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAA;IACjD,CAAC;IAED,OAAO;QACN,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE;YACN,oBAAoB,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,EAAE;gBACtE,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;gBAEnE,aAAa,CAAC,EAAE,UAAU,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;gBAEnF,mBAAmB;gBACnB,WAAW,CAAC;oBACX,OAAO,EAAE,kBAAkB;oBAC3B,UAAU,EAAE,iBAAiB,CAAC,wBAAwB,CAAC;oBACvD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBACF,WAAW,CAAC;oBACX,OAAO,EAAE,uBAAuB;oBAChC,UAAU,EAAE,iBAAiB,CAAC,2BAA2B,CAAC;oBAC1D,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBACF,WAAW,CAAC;oBACX,OAAO,EAAE,oBAAoB;oBAC7B,UAAU,EAAE,iBAAiB,CAAC,wBAAwB,CAAC;oBACvD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBACF,WAAW,CAAC;oBACX,OAAO,EAAE,qBAAqB;oBAC9B,UAAU,EAAE,iBAAiB,CAAC,yBAAyB,CAAC;oBACxD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBACF,IAAI,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;oBAC/B,WAAW,CAAC;wBACX,OAAO,EAAE,uBAAuB;wBAChC,UAAU,EAAE,iBAAiB,CAAC,2BAA2B,CAAC;wBAC1D,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,8EAA8E;gBAC9E,IAAI,OAAO,CAAC,eAAe,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;oBACjE,WAAW,CAAC;wBACX,OAAO,EAAE,uBAAuB;wBAChC,UAAU,EAAE,iBAAiB,CAAC,2BAA2B,CAAC;wBAC1D,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,4CAA4C;gBAC5C,WAAW,CAAC;oBACX,OAAO,EAAE,aAAa;oBACtB,UAAU,EAAE,iBAAiB,CAAC,iBAAiB,CAAC;oBAChD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBACF,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACrB,WAAW,CAAC;wBACX,OAAO,EAAE,WAAW;wBACpB,UAAU,EAAE,iBAAiB,CAAC,eAAe,CAAC;wBAC9C,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBACD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;oBACzB,WAAW,CAAC;wBACX,OAAO,EAAE,gBAAgB;wBACzB,UAAU,EAAE,iBAAiB,CAAC,oBAAoB,CAAC;wBACnD,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,cAAc;gBACd,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;oBACjB,WAAW,CAAC;wBACX,OAAO,EAAE,WAAW;wBACpB,UAAU,EAAE,iBAAiB,CAAC,cAAc,CAAC;wBAC7C,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,mCAAmC;gBACnC,IAAI,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;oBAChC,WAAW,CAAC;wBACX,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,QAAQ;wBACnC,UAAU,EAAE,iBAAiB,CAAC,qBAAqB,CAAC;wBACpD,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,kBAAkB;gBAClB,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;oBAC9B,WAAW,CAAC;wBACX,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ;wBACjC,UAAU,EAAE,iBAAiB,CAAC,kBAAkB,CAAC;wBACjD,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,yCAAyC;gBACzC,IAAI,OAAO,CAAC,cAAc,EAAE,aAAa,EAAE,CAAC;oBAC3C,WAAW,CAAC;wBACX,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,eAAe;wBAC/C,UAAU,EAAE,iBAAiB,CAAC,4BAA4B,CAAC;wBAC3D,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,gCAAgC;gBAChC,IAAI,GAAG,EAAE,oBAAoB,EAAE,CAAC;oBAC/B,WAAW,CAAC;wBACX,OAAO,EAAE,kBAAkB;wBAC3B,UAAU,EAAE,iBAAiB,CAAC,qBAAqB,CAAC;wBACpD,SAAS,EAAE,KAAK;qBAChB,CAAC,CAAA;gBACH,CAAC;gBAED,kDAAkD;gBAClD,uEAAuE;gBACvE,0EAA0E;gBAC1E,oEAAoE;gBACpE,mCAAmC;gBACnC,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;oBACtC,WAAW,CAAC;wBACX,OAAO,EAAE,kBAAkB;wBAC3B,UAAU,EAAE,iBAAiB,CAAC,mBAAmB,CAAC;qBAClD,CAAC,CAAA;gBACH,CAAC;YACF,CAAC;YAED,kBAAkB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;gBACpD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;gBACjC,MAAM,eAAe,GAAG,WAAW,CAAC,eAAe,CAAA;gBAEnD,qDAAqD;gBACrD,kEAAkE;gBAClE,mEAAmE;gBACnE,uEAAuE;gBACvE,qEAAqE;gBACrE,0EAA0E;gBAC1E,IAAI,eAAe,IAAI,OAAO,CAAC,gBAAgB,EAAE,OAAO,EAAE,CAAC;oBAC1D,IAAI,CAAC;wBACJ,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAW,CAAC,CAAA;wBACtE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;wBACxF,MAAM,MAAM,GAAG,8BAA8B,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAA;wBACzE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACvB,KAAK,MAAM,KAAK,IAAI,MAAM;gCAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;4BACvD,MAAM,IAAI,KAAK,CACd,sBAAsB,MAAM,CAAC,MAAM,oEAAoE,CACvG,CAAA;wBACF,CAAC;oBACF,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACd,qEAAqE;wBACrE,kEAAkE;wBAClE,MAAM,CAAC,IAAI,CACV,8EAA8E,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAChI,CAAA;oBACF,CAAC;gBACF,CAAC;gBAED,qCAAqC;gBACrC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO;oBAAE,OAAM;gBAEvC,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;gBACvC,IAAI,UAAU,GAAG,CAAC,CAAA;gBAClB,IAAI,YAAY,GAAG,CAAC,CAAA;gBAEpB,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;oBAC9B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;oBACxC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE;wBACjC,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,cAAc;wBACjD,oBAAoB,EAAE,OAAO,CAAC,UAAU,CAAC,oBAAoB;wBAC7D,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC,YAAY;qBAC7C,CAAC,CAAA;oBACF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;oBACxC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;wBACnC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,KAAK,KAAK,EAAE,CAAC,CAAA;wBACpC,UAAU,EAAE,CAAA;oBACb,CAAC;oBACD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;wBACvC,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,KAAK,OAAO,EAAE,CAAC,CAAA;wBACrC,YAAY,EAAE,CAAA;oBACf,CAAC;oBAED,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAClC,6DAA6D,CAC7D,CAAA;oBACD,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;wBACnC,IAAI,CAAC;4BACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;4BACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;4BACvC,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gCACjC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,eAAe,CAAC,EAAE,CAAC,CAAA;gCAC1C,UAAU,EAAE,CAAA;4BACb,CAAC;4BACD,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;gCACnC,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,eAAe,CAAC,EAAE,CAAC,CAAA;gCACzC,YAAY,EAAE,CAAA;4BACf,CAAC;wBACF,CAAC;wBAAC,MAAM,CAAC;4BACR,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,mBAAmB,CAAC,CAAA;4BAC3C,UAAU,EAAE,CAAA;wBACb,CAAC;oBACF,CAAC;gBACF,CAAC;gBAED,IAAI,UAAU,IAAI,YAAY,EAAE,CAAC;oBAChC,MAAM,CAAC,IAAI,CAAC,mBAAmB,UAAU,YAAY,YAAY,WAAW,CAAC,CAAA;gBAC9E,CAAC;qBAAM,CAAC;oBACP,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAA;gBACjD,CAAC;YACF,CAAC;SACD;KACD,CAAA;AACF,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IACjC,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YACtC,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAA;iBAC5D,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC9D,CAAC;IACF,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,OAAO,CAAA;AACf,CAAC;AAGD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAC3F,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/middleware/seo.d.ts
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { SeoEnv } from '../bindings.js';
|
|
2
|
+
import { type ResolvedSeoOptions } from '../options.js';
|
|
3
|
+
import type { ContentItem, ContentProvider, CrawlerClass } from '../types.js';
|
|
4
|
+
import { type RawAuthSegment } from '../utils/effective-auth.js';
|
|
5
|
+
import { type FreshLayerBinding } from '../utils/fresh-layer.js';
|
|
6
|
+
export interface SeoMiddlewareDeps {
|
|
7
|
+
/** Request classifier. Defaults to one backed by a DoH FCrDNS verifier. */
|
|
8
|
+
classify?: (request: Request) => Promise<CrawlerClass>;
|
|
9
|
+
/** R2/KV binding for the fresh-twin layer. Required for mode 'middleware' or 'both'. */
|
|
10
|
+
freshLayer?: FreshLayerBinding;
|
|
11
|
+
/** Assets binding for reading the stale build-time twin on R2 miss. */
|
|
12
|
+
assets?: {
|
|
13
|
+
fetch(request: Request): Promise<Response>;
|
|
14
|
+
};
|
|
15
|
+
/** Consumer's raw auth segment for this request. Defaults to 'anon'. */
|
|
16
|
+
rawAuthSegment?: (request: Request) => RawAuthSegment;
|
|
17
|
+
/** Fire-and-forget ctx.waitUntil equivalent. Used to schedule background revalidation. */
|
|
18
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface SeoMiddlewareContext {
|
|
21
|
+
request: Request;
|
|
22
|
+
locals?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export declare function createSeoMiddleware(options: ResolvedSeoOptions, contentProvider?: ContentProvider, deps?: SeoMiddlewareDeps): (context: SeoMiddlewareContext, next: () => Promise<Response>) => Promise<Response>;
|
|
25
|
+
export declare const onRequest: (context: SeoMiddlewareContext & {
|
|
26
|
+
locals?: {
|
|
27
|
+
runtime?: {
|
|
28
|
+
env?: SeoEnv;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}, next: () => Promise<Response>) => Promise<Response>;
|
|
32
|
+
declare function appendVaryHeaders(headers: Headers): void;
|
|
33
|
+
declare function dedupVary(combined: string): string;
|
|
34
|
+
declare function twinUrlFor(url: URL, aeo: {
|
|
35
|
+
twinUrl?: (u: string) => string;
|
|
36
|
+
}): string;
|
|
37
|
+
declare function findItemForPath(contentProvider: ContentProvider, context: SeoMiddlewareContext, pathname: string): Promise<ContentItem | undefined>;
|
|
38
|
+
export declare const _internals: {
|
|
39
|
+
appendVaryHeaders: typeof appendVaryHeaders;
|
|
40
|
+
dedupVary: typeof dedupVary;
|
|
41
|
+
twinUrlFor: typeof twinUrlFor;
|
|
42
|
+
findItemForPath: typeof findItemForPath;
|
|
43
|
+
};
|
|
44
|
+
export {};
|
|
5
45
|
//# sourceMappingURL=seo.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../src/middleware/seo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../src/middleware/seo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,KAAK,kBAAkB,EAAmB,MAAM,eAAe,CAAA;AAExE,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAA;AAGnG,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAE7F,OAAO,EAAE,KAAK,iBAAiB,EAAiC,MAAM,yBAAyB,CAAA;AAI/F,MAAM,WAAW,iBAAiB;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;IACtD,wFAAwF;IACxF,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,uEAAuE;IACvE,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;IACvD,wEAAwE;IACxE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,cAAc,CAAA;IACrD,0FAA0F;IAC1F,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;CAC/C;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,kBAAkB,EAC3B,eAAe,CAAC,EAAE,eAAe,EACjC,IAAI,GAAE,iBAAsB,IAM3B,SAAS,oBAAoB,EAC7B,MAAM,MAAM,OAAO,CAAC,QAAQ,CAAC,KAC3B,OAAO,CAAC,QAAQ,CAAC,CAyFpB;AAMD,eAAO,MAAM,SAAS,GACrB,SAAS,oBAAoB,GAAG;IAAE,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,EAC3E,MAAM,MAAM,OAAO,CAAC,QAAQ,CAAC,KAC3B,OAAO,CAAC,QAAQ,CA+BlB,CAAA;AAqBD,iBAAS,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAIjD;AAED,iBAAS,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM3C;AAOD,iBAAS,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;IAAE,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,GAAG,MAAM,CAG9E;AAED,iBAAe,eAAe,CAC7B,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAmBlC;AA4GD,eAAO,MAAM,UAAU;;;;;CAKtB,CAAA"}
|