import { buildLocalizedPath } from '../i18n/index.js'; /** * Builds the body of /llms.txt — a Markdown file describing the site for AI * agents / LLM crawlers, per the llmstxt.org convention. Mirrors buildRobots / * buildSitemapEntries: the plugin already knows the site's name, description, * and pages, so it can generate this automatically. * * Structure (llmstxt.org): H1 site name, a blockquote/summary, then a list of * key pages as Markdown links with short descriptions. Agents read this to * understand the site quickly without crawling everything. * * Wire it as a route that returns text/plain: * * // app/llms.txt/route.ts * import { buildLlmsTxt } from '@intecion/ipal-kit' * import { getCachedPayload } from '@/lib/content' * import { i18nConfig } from '@/i18n.config' * export const dynamic = 'force-dynamic' * export async function GET() { * const body = await buildLlmsTxt({ * payload: await getCachedPayload(), * config: i18nConfig, * baseUrl: process.env.NEXT_PUBLIC_SERVER_URL!, * }) * return new Response(body, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }) * } * * Data comes from the panel (siteName, siteDescription, pages) — nothing * hardcoded. Skips drafts and noindex pages (same as the sitemap). */ export async function buildLlmsTxt({ baseUrl, config, locale, pagesSlug = 'pages', payload, settingsSlug = 'site-settings' }) { const loc = locale ?? config.defaultLocale; const origin = baseUrl.replace(/\/$/, ''); const settings = await payload.findGlobal({ slug: settingsSlug, depth: 0, locale: loc }); const name = settings.siteName?.trim() || 'Website'; const description = settings.siteDescription?.trim(); // NO where:{_status} filter — collections without drafts enabled don't // register the _status field, and querying it throws // "path cannot be queried: _status" (a real bug report). Draft filtering // happens in memory below, which is safe for every collection. Same as // buildSitemapEntries and generateStaticParams. const result = await payload.find({ collection: pagesSlug, depth: 0, limit: 1000, locale: loc }); const lines = [ `# ${name}`, '' ]; if (description) { lines.push(`> ${description}`, ''); } const pageLinks = []; for (const raw of result.docs){ if (raw._status && raw._status !== 'published') { continue; } if (raw.meta?.noindex) { continue; } const title = typeof raw.title === 'string' ? raw.title : undefined; const slug = typeof raw.slug === 'string' ? raw.slug : undefined; if (!title || !slug) { continue; } const path = buildLocalizedPath({ config, locale: loc, slugs: { [loc]: slug } }); if (!path) { continue; } const pageDesc = typeof raw.meta?.description === 'string' ? ` — ${raw.meta.description}` : ''; pageLinks.push(`- [${title}](${origin}${path})${pageDesc}`); } if (pageLinks.length > 0) { lines.push('## Strony', '', ...pageLinks, ''); } return lines.join('\n'); } //# sourceMappingURL=buildLlmsTxt.js.map