seoLlmsFullTxt function
Generates an llms-full.txt: like seoLlmsTxt, but with the complete page content inlined as markdown — AI assistants get the whole site in one request instead of crawling page by page.
The content comes from each page's resolved body (the same SeoNode trees the SSR server and prerenderer use), converted to markdown: headings, paragraphs, lists, links, images and blockquotes. Pages with no body list their metadata only.
Pass exactly one of routes and pages. Unlike the sync
generators this never throws on a dynamic table — it is already
async and resolves internally when given routes. Meta and body of
each page come from a single resolution, so they cannot describe
different records.
Implementation
Future<String> seoLlmsFullTxt({
required String siteBase,
List<SeoRoute>? routes,
List<SeoResolvedPage>? pages,
String? title,
String? description,
List<String> additionalPaths = const [],
int concurrency = 8,
}) async {
if ((routes == null) == (pages == null)) {
throw ArgumentError('Pass exactly one of `routes:` or `pages:`.');
}
if (pages != null && additionalPaths.isNotEmpty) {
throw ArgumentError(
'`additionalPaths` cannot be combined with `pages:`.',
);
}
final base = siteBase.endsWith('/')
? siteBase.substring(0, siteBase.length - 1)
: siteBase;
final resolved = pages ??
await resolveSeoPages(
routes: routes!,
canonicalBase: siteBase,
additionalPaths: additionalPaths,
detail: SeoDetail.full,
concurrency: concurrency,
);
final rootMeta = _rootMeta(resolved);
final siteTitle = title ?? rootMeta?.title ?? Uri.parse(base).host;
final siteDescription = description ?? rootMeta?.description;
final buffer = StringBuffer()..writeln('# ${_singleLine(siteTitle)}');
if (siteDescription != null && siteDescription.isNotEmpty) {
buffer.writeln('> ${_singleLine(siteDescription)}');
}
for (final page in resolved) {
if (!page.isIndexable) continue;
final doc = page.document!;
final meta = doc.meta;
final url = page.path == '/' ? '$base/' : '$base${page.path}';
buffer
..writeln()
..writeln('## ${_singleLine(meta.title ?? page.path)}')
..writeln(url);
final pageDescription = meta.description;
if (pageDescription != null && pageDescription.isNotEmpty) {
buffer.writeln('> ${_singleLine(pageDescription)}');
}
final markdown = _nodesToMarkdown(doc.body, base);
if (markdown.isNotEmpty) {
buffer
..writeln()
..writeln(markdown);
}
}
return buffer.toString();
}