p1-knowledge-seo
v0.1.0
Published
Sitemap, robots.txt, and llms.txt builders for P1 Knowledge CMS sites (SEO / GEO discovery)
Maintainers
Readme
p1-knowledge-seo
Configurable sitemap, robots.txt, and llms.txt builders for sites powered by the P1 Knowledge CMS. Framework-agnostic core with optional Next.js App Router adapters.
| Surface | API |
|---------|-----|
| Public route model | createKnowledgeSeo, getPublicSeoRoutes, mergePublicSeoRoutes |
| /sitemap.xml | toSitemapEntries → Next routesToNextSitemap or pure buildSitemapXml |
| /robots.txt | buildRobotsRules → toNextRobots |
| /llms.txt / /llms-full.txt | buildLlmsTxt, buildLlmsFullTxt → llmsTextResponse |
| Webhook path helper | slugAndLocaleToPath |
Zero hard runtime dependencies. Works with @p1/knowledge-client /
@p1/knowledge-next via a duck-typed client, or with any listDocuments fn.
Docs: Architecture · API reference · Install & publish · Contributing · Changelog · Examples
Install
npm install p1-knowledge-seo
# or
pnpm add p1-knowledge-seo
# Adjacent checkout
pnpm add p1-knowledge-seo@file:../p1-knowledge-seoOptional peer (only if you import the Next adapters):
pnpm add nextimport { createKnowledgeSeo } from 'p1-knowledge-seo';
import { routesToNextSitemap } from 'p1-knowledge-seo/next';Note: The npm name is unscoped
p1-knowledge-seo(not@p1/knowledge-seo).
Quick start (Next.js App Router)
// lib/seo.ts
import { createKnowledgeSeo } from 'p1-knowledge-seo';
import { createClient } from '@p1/knowledge-next'; // optional
export const seo = createKnowledgeSeo({
siteUrl: () => process.env.NEXT_PUBLIC_APP_URL!,
client: createClient(), // or omit + pass listDocuments
coreRoutes: [
{
path: '/',
title: 'Home',
description: 'Product overview',
priority: 1,
changefreq: 'daily',
},
{
path: '/pricing',
title: 'Pricing',
description: 'Plans',
priority: 0.8,
},
],
// Unioned with package base (login, api, sitemap.xml, _next, …)
reservedPathSegments: ['app', 'organic'],
onError: (err, ctx) => console.error('knowledge-seo', ctx, err),
llms: {
siteName: 'My Product',
tagline: 'One-line pitch for agents',
intro: 'What this site is and how agents should use public pages.',
privateProductNote: 'Authenticated app routes are not for public crawl.',
contactLinks: [
{ label: 'Support', path: '/support' },
{ label: 'Legal', path: '/legal' },
],
markdownAlternatePaths: ['/'],
},
robots: {
disallow: ['/admin/', '/api/', '/app/'],
aiBots: 'default',
},
sitemap: { hreflang: true },
});// app/sitemap.ts
import { routesToNextSitemap } from 'p1-knowledge-seo/next';
import { seo } from '@/lib/seo';
export const revalidate = 3600;
export default async function sitemap() {
const routes = await seo.getPublicSeoRoutes();
return routesToNextSitemap(routes, seo.resolveSiteUrl());
}// app/robots.ts
import { toNextRobots } from 'p1-knowledge-seo/next';
import { seo } from '@/lib/seo';
export default function robots() {
return toNextRobots(seo.buildRobotsRules());
}// app/llms.txt/route.ts
import { llmsTextResponse } from 'p1-knowledge-seo/next';
import { seo } from '@/lib/seo';
export const revalidate = 3600;
export async function GET() {
return llmsTextResponse(await seo.buildLlmsTxt());
}// app/api/webhooks/knowledge/route.ts
import { createKnowledgeWebhookHandler } from '@p1/knowledge-next/webhook';
import { seo } from '@/lib/seo';
export const POST = createKnowledgeWebhookHandler({
slugToPath: (slug, locale) => seo.slugAndLocaleToPath(slug, locale),
});Without a Knowledge client, pass listDocuments + hasAuth:
createKnowledgeSeo({
siteUrl: 'https://example.com',
listDocuments: async (opts) => myCms.list(opts),
hasAuth: true, // false → core routes only (skip CMS)
// …
});Pure XML sitemap (any framework)
const xml = await seo.buildSitemapXml();
// or for very large sites:
const docs = await seo.buildSitemapDocuments(undefined, { maxPerFile: 50_000 });What the host app owns
- Core public routes and reserved path segments (product areas only — base list is free)
- Brand / product copy for
llms.txt - robots disallow list for authenticated product paths
- Knowledge API credentials and published content
- Optional
onError/onDropfor observability
Empty CMS credentials → sitemap/llms still work with core routes only. CMS errors fail open so discovery surfaces stay up.
Develop & validate
git pull origin main
pnpm install # always after pull — installs tsc, vitest, biome, …
pnpm validate # ensure-deps + build + test + typecheck + lint
pnpm pack:check # dry-run tarball contents| Error | Fix |
|-------|-----|
| tsc: command not found | pnpm install |
| biome: command not found | pnpm install (Biome is a devDependency) |
Runnable demo: node examples/basic/run.mjs (after build).
Publish to npm
Full guide: docs/PUBLISH.md.
pnpm install
pnpm validate
npm publish # unscoped public package| | |
|--|--|
| Package name | p1-knowledge-seo |
| Access | Public (unscoped). Never use --access restricted → EUNSCOPED |
With a version bump:
npm version patch
npm publish
git push origin main --follow-tagsConsumers (examples)
| Consumer | Install |
|----------|---------|
| matext-web | pnpm add p1-knowledge-seo or file:../p1-knowledge-seo |
| Other apps | pnpm add p1-knowledge-seo after publish |
If using a file: path, ensure dist/ exists (pnpm build or prepare on install).
License
See LICENSE.
