@actuate-media/cms-core 0.14.0 → 0.15.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.
@@ -0,0 +1,204 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { composePageMeta, detectSchemaType } from '../../seo/page-meta.js';
3
+ function makeCollection(overrides = {}) {
4
+ return {
5
+ slug: 'pages',
6
+ labels: { singular: 'Page', plural: 'Pages' },
7
+ fields: {},
8
+ ...overrides,
9
+ };
10
+ }
11
+ function makeConfig(overrides = {}) {
12
+ return {
13
+ seo: {
14
+ siteUrl: 'https://example.com',
15
+ siteName: 'Example',
16
+ ...overrides,
17
+ },
18
+ };
19
+ }
20
+ describe('detectSchemaType', () => {
21
+ it('maps known collection slugs', () => {
22
+ expect(detectSchemaType('posts')).toBe('BlogPosting');
23
+ expect(detectSchemaType('blog')).toBe('BlogPosting');
24
+ expect(detectSchemaType('articles')).toBe('Article');
25
+ expect(detectSchemaType('news')).toBe('NewsArticle');
26
+ expect(detectSchemaType('case-studies')).toBe('Article');
27
+ expect(detectSchemaType('faq')).toBe('FAQPage');
28
+ expect(detectSchemaType('services')).toBe('Service');
29
+ expect(detectSchemaType('locations')).toBe('LocalBusiness');
30
+ expect(detectSchemaType('jobs')).toBe('JobPosting');
31
+ expect(detectSchemaType('team')).toBe('Person');
32
+ expect(detectSchemaType('portfolios')).toBe('CreativeWork');
33
+ });
34
+ it('soft-matches via includes', () => {
35
+ expect(detectSchemaType('blog-posts')).toBe('BlogPosting');
36
+ expect(detectSchemaType('our-services')).toBe('Service');
37
+ });
38
+ it('falls back to WebPage for unknown slugs', () => {
39
+ expect(detectSchemaType('xyz-unknown')).toBe('WebPage');
40
+ });
41
+ });
42
+ describe('composePageMeta', () => {
43
+ it('builds canonical URL from site URL + collection prefix + slug', () => {
44
+ const result = composePageMeta({
45
+ doc: { id: '1', collection: 'posts', slug: 'hello-world', data: { title: 'Hello World' } },
46
+ collection: makeCollection({ slug: 'posts', urlPrefix: 'blog', type: 'post' }),
47
+ config: makeConfig(),
48
+ });
49
+ expect(result.url).toBe('https://example.com/blog/hello-world');
50
+ expect(result.canonical).toBe('https://example.com/blog/hello-world');
51
+ });
52
+ it('treats slug "home" as the site root', () => {
53
+ const result = composePageMeta({
54
+ doc: { id: '1', collection: 'pages', slug: 'home', data: { title: 'Home' } },
55
+ collection: makeCollection({ slug: 'pages', urlPrefix: '', type: 'page' }),
56
+ config: makeConfig(),
57
+ });
58
+ expect(result.url).toBe('https://example.com');
59
+ });
60
+ it('prefers explicit pageSettings.metaTitle over data.title', () => {
61
+ const result = composePageMeta({
62
+ doc: {
63
+ id: '1',
64
+ collection: 'pages',
65
+ slug: 'about',
66
+ data: { title: 'Plain Title' },
67
+ pageSettings: { metaTitle: 'SEO Title' },
68
+ },
69
+ collection: makeCollection(),
70
+ config: makeConfig(),
71
+ });
72
+ expect(result.title).toBe('SEO Title');
73
+ });
74
+ it('composes BlogPosting JSON-LD for posts with author + publisher', () => {
75
+ const result = composePageMeta({
76
+ doc: {
77
+ id: '1',
78
+ collection: 'posts',
79
+ slug: 'first-post',
80
+ data: { title: 'First Post', metaDescription: 'Hello', authorName: 'Jane Doe' },
81
+ publishedAt: '2026-01-01T00:00:00.000Z',
82
+ },
83
+ collection: makeCollection({ slug: 'posts', urlPrefix: 'blog', type: 'post' }),
84
+ config: makeConfig({
85
+ organization: { name: 'Example Inc', logo: 'https://example.com/logo.png' },
86
+ }),
87
+ });
88
+ expect(result.jsonLd).toMatchObject({
89
+ '@context': 'https://schema.org',
90
+ '@type': 'BlogPosting',
91
+ name: 'First Post',
92
+ description: 'Hello',
93
+ url: 'https://example.com/blog/first-post',
94
+ datePublished: '2026-01-01T00:00:00.000Z',
95
+ author: { '@type': 'Person', name: 'Jane Doe' },
96
+ publisher: {
97
+ '@type': 'Organization',
98
+ name: 'Example Inc',
99
+ logo: { '@type': 'ImageObject', url: 'https://example.com/logo.png' },
100
+ },
101
+ });
102
+ });
103
+ it('respects per-collection defaultSchemaType', () => {
104
+ const result = composePageMeta({
105
+ doc: { id: '1', collection: 'locations', slug: 'nyc', data: { title: 'NYC' } },
106
+ collection: makeCollection({
107
+ slug: 'locations',
108
+ seo: { defaultSchemaType: 'LocalBusiness' },
109
+ }),
110
+ config: makeConfig(),
111
+ });
112
+ expect(result.jsonLd?.['@type']).toBe('LocalBusiness');
113
+ });
114
+ it('honors doc-level structuredData verbatim when set', () => {
115
+ const result = composePageMeta({
116
+ doc: {
117
+ id: '1',
118
+ collection: 'pages',
119
+ slug: 'special',
120
+ data: { title: 'Special' },
121
+ structuredData: { '@type': 'FAQPage', mainEntity: [] },
122
+ },
123
+ collection: makeCollection(),
124
+ config: makeConfig(),
125
+ });
126
+ expect(result.jsonLd).toEqual({
127
+ '@context': 'https://schema.org',
128
+ '@type': 'FAQPage',
129
+ mainEntity: [],
130
+ });
131
+ });
132
+ it('inherits robots defaults from CollectionSEOConfig', () => {
133
+ const result = composePageMeta({
134
+ doc: { id: '1', collection: 'drafts', slug: 'x', data: { title: 'X' } },
135
+ collection: makeCollection({
136
+ slug: 'drafts',
137
+ seo: { defaultRobots: { noIndex: true } },
138
+ }),
139
+ config: makeConfig(),
140
+ });
141
+ expect(result.meta['robots']).toContain('noindex');
142
+ });
143
+ it('emits og:image, og:url, og:title, twitter:card', () => {
144
+ const result = composePageMeta({
145
+ doc: {
146
+ id: '1',
147
+ collection: 'pages',
148
+ slug: 'home',
149
+ data: { title: 'Hi', metaDescription: 'A page', ogImage: 'https://example.com/cover.png' },
150
+ },
151
+ collection: makeCollection(),
152
+ config: makeConfig({ twitterHandle: '@example' }),
153
+ });
154
+ expect(result.meta['og:image']).toBe('https://example.com/cover.png');
155
+ expect(result.meta['og:url']).toBe('https://example.com');
156
+ expect(result.meta['og:title']).toBe('Hi');
157
+ expect(result.meta['twitter:card']).toBe('summary_large_image');
158
+ expect(result.meta['twitter:site']).toBe('@example');
159
+ });
160
+ it('auto-builds og:image URL pointing at /og.png when none configured', () => {
161
+ const result = composePageMeta({
162
+ doc: { id: '1', collection: 'pages', slug: 'about', data: { title: 'About Us' } },
163
+ collection: makeCollection(),
164
+ config: makeConfig(),
165
+ });
166
+ expect(result.meta['og:image']).toBe('https://example.com/og.png?title=' + encodeURIComponent('About Us'));
167
+ });
168
+ it('escapes < in JSON-LD HTML so it cannot break out of <script>', () => {
169
+ const result = composePageMeta({
170
+ doc: { id: '1', collection: 'pages', slug: 'x', data: { title: '</script><img src=x>' } },
171
+ collection: makeCollection(),
172
+ config: makeConfig(),
173
+ });
174
+ // The wrapping `<script>...</script>` legitimately contains the closing
175
+ // tag, so we strip it off and assert no closing `<` survives in the body.
176
+ const body = result.jsonLdHtml
177
+ .replace(/^<script type="application\/ld\+json">/, '')
178
+ .replace(/<\/script>$/, '');
179
+ expect(body).not.toContain('</script>');
180
+ expect(body).not.toContain('<img');
181
+ expect(body).toContain('\\u003c');
182
+ });
183
+ it('exposes Next.js-compatible metadata object', () => {
184
+ const result = composePageMeta({
185
+ doc: { id: '1', collection: 'pages', slug: 'home', data: { title: 'Home' } },
186
+ collection: makeCollection(),
187
+ config: makeConfig(),
188
+ });
189
+ const m = result.metadata;
190
+ expect(m.title).toBe('Home');
191
+ expect(m.openGraph.siteName).toBe('Example');
192
+ expect(m.alternates.canonical).toBe('https://example.com');
193
+ });
194
+ it('overrides siteUrl when input.siteUrl is provided (preview deploys)', () => {
195
+ const result = composePageMeta({
196
+ doc: { id: '1', collection: 'pages', slug: 'about', data: { title: 'About' } },
197
+ collection: makeCollection(),
198
+ config: makeConfig(),
199
+ siteUrl: 'https://preview-abc.vercel.app',
200
+ });
201
+ expect(result.url).toBe('https://preview-abc.vercel.app/about');
202
+ });
203
+ });
204
+ //# sourceMappingURL=page-meta.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-meta.test.js","sourceRoot":"","sources":["../../../src/__tests__/seo/page-meta.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler-factory.d.ts","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"AA2EA,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5C;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,IAsFtC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAmHnE"}
1
+ {"version":3,"file":"handler-factory.d.ts","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"AA2EA,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5C;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,IAsFtC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAsHnE"}
@@ -188,7 +188,8 @@ export function handleActuateAPI(options = {}) {
188
188
  },
189
189
  });
190
190
  }
191
- if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method) && !isApiKeyRequest(request)) {
191
+ if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method) &&
192
+ !isApiKeyRequest(request)) {
192
193
  const csrfToken = request.headers.get('x-csrf-token');
193
194
  const csrfCookie = parseCookieHeader(request.headers.get('cookie') ?? '')['actuate_csrf'];
194
195
  if (!csrfToken || !csrfCookie || !validateCsrfToken(csrfToken, csrfCookie)) {
@@ -1 +1 @@
1
- {"version":3,"file":"handler-factory.js","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handler-factory.js","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAya5C,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AA2ND,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6zJzD"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAigB5C,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AA2ND,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgmKzD"}
@@ -177,6 +177,79 @@ function modelNotAvailable(name) {
177
177
  'Run `actuate db:init` for new schemas, or carefully update the existing Actuate block, create/apply a Prisma migration, then regenerate Prisma Client. ' +
178
178
  'See https://actuatecms.dev/docs/database-setup for required models.', 501);
179
179
  }
180
+ /**
181
+ * XML-escape a value for safe inclusion in sitemap content. Sitemaps reject
182
+ * unescaped ampersands and angle brackets; URLs frequently contain `&` query
183
+ * separators, so this is non-optional.
184
+ */
185
+ function escapeXml(value) {
186
+ return value
187
+ .replace(/&/g, '&amp;')
188
+ .replace(/</g, '&lt;')
189
+ .replace(/>/g, '&gt;')
190
+ .replace(/"/g, '&quot;')
191
+ .replace(/'/g, '&apos;');
192
+ }
193
+ /**
194
+ * Renders a 1200x630 SVG suitable for og:image. We don't pull in Satori/resvg
195
+ * because most integrators don't need PNG — major crawlers (Facebook, Twitter,
196
+ * LinkedIn, Slack, Discord) handle image/svg+xml correctly. Sites that
197
+ * specifically need PNG can override with their own /og endpoint via
198
+ * @vercel/og and point `seo.defaultOgImage` at it.
199
+ */
200
+ function renderOgSvg(opts) {
201
+ const { title, description, siteName, bg, fg, muted } = opts;
202
+ // Naive line wrapping at ~22 chars (large font). Good enough for an OG card;
203
+ // anything longer than ~3 lines gets truncated with an ellipsis.
204
+ const wrap = (text, maxChars, maxLines) => {
205
+ const words = text.split(/\s+/);
206
+ const lines = [];
207
+ let current = '';
208
+ for (const w of words) {
209
+ if (lines.length >= maxLines)
210
+ break;
211
+ const next = current ? `${current} ${w}` : w;
212
+ if (next.length > maxChars) {
213
+ if (current)
214
+ lines.push(current);
215
+ current = w;
216
+ if (lines.length === maxLines - 1 && words.indexOf(w) < words.length - 1) {
217
+ lines.push(current.length > maxChars ? current.slice(0, maxChars - 1) + '…' : current + '…');
218
+ current = '';
219
+ break;
220
+ }
221
+ }
222
+ else {
223
+ current = next;
224
+ }
225
+ }
226
+ if (current && lines.length < maxLines)
227
+ lines.push(current);
228
+ return lines;
229
+ };
230
+ const escapeSvg = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
231
+ const titleLines = wrap(title, 28, 3);
232
+ const descLines = description ? wrap(description, 60, 2) : [];
233
+ const titleEls = titleLines
234
+ .map((line, i) => `<tspan x="60" dy="${i === 0 ? 0 : 78}">${escapeSvg(line)}</tspan>`)
235
+ .join('');
236
+ const descEls = descLines
237
+ .map((line, i) => `<tspan x="60" dy="${i === 0 ? 0 : 36}">${escapeSvg(line)}</tspan>`)
238
+ .join('');
239
+ // Layout: site name top-left, title bottom-left, description below title.
240
+ // Coordinates are roughly aligned to the 1200x630 spec used by every major
241
+ // social platform.
242
+ const titleY = descLines.length > 0 ? 360 : 420;
243
+ const descY = titleY + 80 * titleLines.length;
244
+ return `<?xml version="1.0" encoding="UTF-8"?>
245
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
246
+ <rect width="1200" height="630" fill="${bg}"/>
247
+ ${siteName ? `<text x="60" y="100" font-family="system-ui, -apple-system, Segoe UI, Arial, sans-serif" font-size="28" font-weight="500" fill="${muted}">${escapeSvg(siteName)}</text>` : ''}
248
+ <text x="60" y="${titleY}" font-family="system-ui, -apple-system, Segoe UI, Arial, sans-serif" font-size="68" font-weight="700" fill="${fg}">${titleEls}</text>
249
+ ${descLines.length > 0 ? `<text x="60" y="${descY}" font-family="system-ui, -apple-system, Segoe UI, Arial, sans-serif" font-size="30" fill="${muted}">${descEls}</text>` : ''}
250
+ <rect x="60" y="560" width="60" height="6" fill="${fg}" rx="3"/>
251
+ </svg>`;
252
+ }
180
253
  async function safeCount(model, where) {
181
254
  try {
182
255
  if (!model || typeof model !== 'object')
@@ -313,7 +386,7 @@ async function extractSession(request) {
313
386
  const ipRestrictions = Array.isArray(apiKey.ipRestrictions)
314
387
  ? apiKey.ipRestrictions
315
388
  : apiKey.ipRestrictions
316
- ? apiKey.ipRestrictions.allow ?? null
389
+ ? (apiKey.ipRestrictions.allow ?? null)
317
390
  : null;
318
391
  if (ipRestrictions && ipRestrictions.length > 0) {
319
392
  const ip = getClientIp(request);
@@ -3118,6 +3191,237 @@ export function registerCMSRoutes(router) {
3118
3191
  return internalError(err, 'llms.txt');
3119
3192
  }
3120
3193
  });
3194
+ // ---------------------------------------------------------------------------
3195
+ // Public SEO surfaces: sitemap.xml, per-collection sitemaps, robots.txt,
3196
+ // and the dynamic /og.png OG-image endpoint. These are unauthenticated by
3197
+ // design — search engines and social crawlers will fetch them.
3198
+ // ---------------------------------------------------------------------------
3199
+ function siteUrlFromRequest(request) {
3200
+ const cfg = getActuateConfig()?.seo;
3201
+ if (cfg?.siteUrl)
3202
+ return cfg.siteUrl.replace(/\/+$/, '');
3203
+ // Fall back to the request origin so the routes work on preview deploys
3204
+ // before the integrator has configured siteUrl.
3205
+ try {
3206
+ const u = new URL(request.url);
3207
+ return `${u.protocol}//${u.host}`;
3208
+ }
3209
+ catch {
3210
+ return '';
3211
+ }
3212
+ }
3213
+ function sitemapEligibleCollections() {
3214
+ const cfg = getActuateConfig();
3215
+ if (!cfg)
3216
+ return [];
3217
+ const excluded = new Set(cfg.seo?.sitemap?.excludeCollections ?? []);
3218
+ const out = [];
3219
+ for (const [slug, col] of Object.entries(cfg.collections ?? {})) {
3220
+ if (excluded.has(slug))
3221
+ continue;
3222
+ if (col.seo?.excludeFromSitemap)
3223
+ continue;
3224
+ out.push({ slug, urlPrefix: col.urlPrefix, type: col.type, seo: col.seo });
3225
+ }
3226
+ return out;
3227
+ }
3228
+ router.get('/sitemap.xml', async (request) => {
3229
+ try {
3230
+ const cfg = getActuateConfig();
3231
+ if (cfg?.seo?.sitemap?.disabled)
3232
+ return errorResponse('Sitemap disabled', 404);
3233
+ const base = siteUrlFromRequest(request);
3234
+ const cols = sitemapEligibleCollections();
3235
+ // Sitemap index points at /sitemaps/:slug.xml for each collection.
3236
+ const sitemaps = cols
3237
+ .map((c) => [
3238
+ ' <sitemap>',
3239
+ ` <loc>${base}/api/cms/sitemaps/${c.slug}.xml</loc>`,
3240
+ ` <lastmod>${new Date().toISOString()}</lastmod>`,
3241
+ ' </sitemap>',
3242
+ ].join('\n'))
3243
+ .join('\n');
3244
+ const xml = [
3245
+ '<?xml version="1.0" encoding="UTF-8"?>',
3246
+ '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
3247
+ sitemaps,
3248
+ '</sitemapindex>',
3249
+ ].join('\n');
3250
+ return new Response(xml, {
3251
+ status: 200,
3252
+ headers: {
3253
+ 'Content-Type': 'application/xml; charset=utf-8',
3254
+ 'Cache-Control': 'public, max-age=300, s-maxage=600',
3255
+ },
3256
+ });
3257
+ }
3258
+ catch (err) {
3259
+ return internalError(err, 'sitemap.xml');
3260
+ }
3261
+ });
3262
+ // The router's path-param syntax (`:name`) greedily matches everything up
3263
+ // to the next `/`, so registering as `/sitemaps/:slug.xml` would capture
3264
+ // `slug.xml` into a single param. We register `/sitemaps/:slug` instead and
3265
+ // require the `.xml` suffix in the handler.
3266
+ router.get('/sitemaps/:slug', async (request, params) => {
3267
+ try {
3268
+ const cfg = getActuateConfig();
3269
+ if (cfg?.seo?.sitemap?.disabled)
3270
+ return errorResponse('Sitemap disabled', 404);
3271
+ const slugParam = params.slug ?? '';
3272
+ if (!/\.xml$/i.test(slugParam))
3273
+ return errorResponse('Sitemap not found', 404);
3274
+ const rawSlug = slugParam.replace(/\.xml$/i, '');
3275
+ const collection = cfg?.collections?.[rawSlug];
3276
+ if (!collection)
3277
+ return errorResponse('Collection not found', 404);
3278
+ if (collection.seo?.excludeFromSitemap)
3279
+ return errorResponse('Collection excluded from sitemap', 404);
3280
+ const base = siteUrlFromRequest(request);
3281
+ const docs = await db().document.findMany({
3282
+ where: { collection: rawSlug, deletedAt: null, status: 'PUBLISHED' },
3283
+ select: { slug: true, updatedAt: true, data: true },
3284
+ orderBy: { updatedAt: 'desc' },
3285
+ take: 5000,
3286
+ });
3287
+ const prefix = (collection.urlPrefix ?? '').replace(/^\/|\/$/g, '');
3288
+ const defaultPriority = collection.seo?.sitemapPriority ??
3289
+ cfg?.seo?.sitemap?.defaultPriority ??
3290
+ (collection.type === 'page' ? 0.8 : 0.6);
3291
+ const changefreq = collection.seo?.sitemapChangeFreq ?? cfg?.seo?.sitemap?.defaultChangeFreq ?? 'weekly';
3292
+ const urls = [];
3293
+ // Archive page first (e.g. /blog) for post-type collections.
3294
+ if (collection.seo?.archivePath && collection.type === 'post') {
3295
+ const archive = collection.seo.archivePath.startsWith('http')
3296
+ ? collection.seo.archivePath
3297
+ : `${base}${collection.seo.archivePath}`;
3298
+ urls.push([
3299
+ ' <url>',
3300
+ ` <loc>${escapeXml(archive)}</loc>`,
3301
+ ` <lastmod>${new Date().toISOString()}</lastmod>`,
3302
+ ` <changefreq>${changefreq}</changefreq>`,
3303
+ ' <priority>0.6</priority>',
3304
+ ' </url>',
3305
+ ].join('\n'));
3306
+ }
3307
+ for (const d of docs) {
3308
+ const data = d.data || {};
3309
+ const slug = d.slug ?? data.slug;
3310
+ if (!slug)
3311
+ continue;
3312
+ const loc = prefix ? `${base}/${prefix}/${slug}` : `${base}/${slug}`;
3313
+ urls.push([
3314
+ ' <url>',
3315
+ ` <loc>${escapeXml(loc)}</loc>`,
3316
+ ` <lastmod>${d.updatedAt?.toISOString() ?? new Date().toISOString()}</lastmod>`,
3317
+ ` <changefreq>${changefreq}</changefreq>`,
3318
+ ` <priority>${defaultPriority.toFixed(1)}</priority>`,
3319
+ ' </url>',
3320
+ ].join('\n'));
3321
+ }
3322
+ const xml = [
3323
+ '<?xml version="1.0" encoding="UTF-8"?>',
3324
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
3325
+ urls.join('\n'),
3326
+ '</urlset>',
3327
+ ].join('\n');
3328
+ return new Response(xml, {
3329
+ status: 200,
3330
+ headers: {
3331
+ 'Content-Type': 'application/xml; charset=utf-8',
3332
+ 'Cache-Control': 'public, max-age=300, s-maxage=600',
3333
+ },
3334
+ });
3335
+ }
3336
+ catch (err) {
3337
+ return internalError(err, 'sitemaps/:slug.xml');
3338
+ }
3339
+ });
3340
+ router.get('/robots.txt', async (request) => {
3341
+ try {
3342
+ const cfg = getActuateConfig();
3343
+ const seo = cfg?.seo;
3344
+ if (seo?.robots?.disabled)
3345
+ return errorResponse('robots.txt disabled', 404);
3346
+ const base = siteUrlFromRequest(request);
3347
+ const sitemapUrl = seo?.sitemap?.disabled ? undefined : `${base}/api/cms/sitemap.xml`;
3348
+ const lines = [
3349
+ 'User-agent: *',
3350
+ 'Allow: /',
3351
+ 'Disallow: /admin',
3352
+ 'Disallow: /api/',
3353
+ '',
3354
+ ];
3355
+ for (const rule of seo?.robots?.additionalRules ?? []) {
3356
+ lines.push(`User-agent: ${rule.userAgent}`);
3357
+ for (const allow of rule.allow ?? [])
3358
+ lines.push(`Allow: ${allow}`);
3359
+ for (const dis of rule.disallow ?? [])
3360
+ lines.push(`Disallow: ${dis}`);
3361
+ lines.push('');
3362
+ }
3363
+ if (seo?.robots?.blockAIBots) {
3364
+ const bots = [
3365
+ 'GPTBot',
3366
+ 'ChatGPT-User',
3367
+ 'ClaudeBot',
3368
+ 'Claude-Web',
3369
+ 'anthropic-ai',
3370
+ 'Bytespider',
3371
+ 'CCBot',
3372
+ 'Google-Extended',
3373
+ ];
3374
+ for (const bot of bots) {
3375
+ lines.push(`User-agent: ${bot}`);
3376
+ lines.push('Disallow: /');
3377
+ lines.push('');
3378
+ }
3379
+ }
3380
+ if (sitemapUrl)
3381
+ lines.push(`Sitemap: ${sitemapUrl}`, '');
3382
+ return new Response(lines.join('\n'), {
3383
+ status: 200,
3384
+ headers: {
3385
+ 'Content-Type': 'text/plain; charset=utf-8',
3386
+ 'Cache-Control': 'public, max-age=3600, s-maxage=3600',
3387
+ },
3388
+ });
3389
+ }
3390
+ catch (err) {
3391
+ return internalError(err, 'robots.txt');
3392
+ }
3393
+ });
3394
+ router.get('/og.png', async (request) => {
3395
+ try {
3396
+ const cfg = getActuateConfig();
3397
+ if (cfg?.seo?.ogImage?.disabled)
3398
+ return errorResponse('og.png disabled', 404);
3399
+ const url = new URL(request.url);
3400
+ const title = url.searchParams.get('title') ?? cfg?.seo?.siteName ?? 'Untitled';
3401
+ const description = url.searchParams.get('description') ?? undefined;
3402
+ const siteName = url.searchParams.get('siteName') ?? cfg?.seo?.siteName;
3403
+ const theme = url.searchParams.get('theme') ?? cfg?.seo?.ogImage?.theme ?? 'light';
3404
+ const bg = theme === 'dark' ? '#0a0a0a' : '#ffffff';
3405
+ const fg = theme === 'dark' ? '#fafafa' : '#0a0a0a';
3406
+ const muted = theme === 'dark' ? '#a1a1aa' : '#71717a';
3407
+ // SVG OG image. Crawlers (Facebook, Twitter/X, LinkedIn, Slack, Discord)
3408
+ // all accept image/svg+xml when served with the right Content-Type. We
3409
+ // chose SVG over PNG to avoid forcing a Satori/resvg dependency on
3410
+ // every integrator; sites that need PNG can override with `og:image`
3411
+ // pointing at their own /og endpoint built with @vercel/og.
3412
+ const svg = renderOgSvg({ title, description, siteName, theme, bg, fg, muted });
3413
+ return new Response(svg, {
3414
+ status: 200,
3415
+ headers: {
3416
+ 'Content-Type': 'image/svg+xml; charset=utf-8',
3417
+ 'Cache-Control': 'public, max-age=86400, s-maxage=86400',
3418
+ },
3419
+ });
3420
+ }
3421
+ catch (err) {
3422
+ return internalError(err, 'og.png');
3423
+ }
3424
+ });
3121
3425
  router.get('/seo/schema/:documentId', async (request, params) => {
3122
3426
  try {
3123
3427
  const auth = await requireAuth(request);
@@ -3454,6 +3758,28 @@ export function registerCMSRoutes(router) {
3454
3758
  const docData = doc.data && typeof doc.data === 'object' ? doc.data : {};
3455
3759
  const layout = await resolveLayout(pathParam, docData, matchedCollection);
3456
3760
  const { _layout: _omit, ...cleanData } = docData;
3761
+ // Compose page meta + JSON-LD up front so client renderers (Next.js
3762
+ // generateMetadata, plain SSR, MCP agents) don't have to re-derive
3763
+ // schema, OG tags, and canonical URLs from raw doc data. Reads the
3764
+ // collection's SEO config and the site-wide SEO defaults.
3765
+ const cfg = getActuateConfig();
3766
+ const collectionDef = cfg?.collections?.[matchedCollection] ?? null;
3767
+ const { composePageMeta } = await import('../seo/page-meta.js');
3768
+ const composed = composePageMeta({
3769
+ doc: {
3770
+ id: doc.id,
3771
+ collection: doc.collection,
3772
+ slug: doc.slug ?? cleanData.slug ?? null,
3773
+ data: cleanData,
3774
+ publishedAt: doc.publishedAt,
3775
+ updatedAt: doc.updatedAt,
3776
+ structuredData: doc.structuredData ?? null,
3777
+ pageSettings: cleanData.pageSettings ?? null,
3778
+ },
3779
+ collection: collectionDef,
3780
+ config: cfg ?? null,
3781
+ siteUrl: siteUrlFromRequest(request),
3782
+ });
3457
3783
  return json({
3458
3784
  data: {
3459
3785
  id: doc.id,
@@ -3461,8 +3787,18 @@ export function registerCMSRoutes(router) {
3461
3787
  data: cleanData,
3462
3788
  status: doc.status,
3463
3789
  publishedAt: doc.publishedAt,
3464
- structuredData: doc.structuredData,
3790
+ structuredData: composed.jsonLd ?? doc.structuredData,
3791
+ },
3792
+ meta: {
3793
+ title: composed.title,
3794
+ description: composed.description,
3795
+ canonical: composed.canonical,
3796
+ url: composed.url,
3797
+ tags: composed.meta,
3798
+ html: composed.metaHtml,
3465
3799
  },
3800
+ jsonLd: composed.jsonLd,
3801
+ jsonLdHtml: composed.jsonLdHtml,
3466
3802
  ...(Object.keys(layout).length > 0 ? { layout } : {}),
3467
3803
  });
3468
3804
  }