/** * Markdown-escaping helper for `llms.txt` link text. */ /** * Escape the Markdown link characters `\ [ ] ( )` in link *text* (titles and * notes) so a value like `A [beta] (x)` cannot break the surrounding * `[...](...)`. Escaping is idempotent-safe only for these characters and is * intended for the human-readable text portions — not for URLs. * * Opt-in: pass `{ escape: true }` to {@link llmsTxt} / the builders, or call * this directly. The default (unescaped) output is unchanged. * * @example * escapeLlmsText("Guide [v2] (beta)"); // "Guide \\[v2\\] \\(beta\\)" */ declare function escapeLlmsText(s: string): string; /** * Reverse {@link escapeLlmsText} — turn `\[`, `\]`, `\(`, `\)` and `\\` back * into the literal character. {@link parseLlmsTxt} needs this to read a link * whose title was escaped on the way out; without it the writer and the parser * disagree about their own format. */ declare function unescapeLlmsText(s: string): string; /** * Build `llms.txt` and `llms-full.txt` from a set of pages (title + url + * full Markdown content) — e.g. your rendered routes or docs. * * Zero dependencies · isomorphic. */ /** A single page: a link in `llms.txt` and a full section in `llms-full.txt`. */ interface LlmsPage { title: string; /** Canonical URL. Pages without a `url` are inlined into `llms-full.txt` only. */ url?: string; /** Full Markdown content for `llms-full.txt`. */ content: string; /** Short note shown after the link in `llms.txt`. */ notes?: string; /** Section heading to group under in `llms.txt`. Defaults to `defaultSection`. */ section?: string; /** Route the page into the special `## Optional` block (links an LLM may skip). */ optional?: boolean; } /** Meta / options for the page-based builders. */ interface PagesToLlmsMeta extends LlmsTxtOptions { title: string; summary?: string; details?: string; /** Section name for pages with no `section`. Default `"Docs"`. */ defaultSection?: string; } /** * Build the compact `llms.txt` from pages — a link per page (with `url`), * grouped by `section` (first-seen order preserved), and `optional` pages under * the `## Optional` block. */ declare function llmsTxtFromPages(pages: LlmsPage[], meta: PagesToLlmsMeta): string; /** * Build the expanded `llms-full.txt` from pages — every page's full Markdown * `content` inlined, in the given order, each with a `Source:` line when a `url` * is present. */ declare function llmsFullTxtFromPages(pages: LlmsPage[], meta: { title: string; summary?: string; }): string; /** * Build BOTH files from one page list in a single call. Serve `txt` at * `/llms.txt` and `full` at `/llms-full.txt`. * @example * const { txt, full } = llmsFromPages(pages, { title: "Acme" }); */ declare function llmsFromPages(pages: LlmsPage[], meta: PagesToLlmsMeta): { txt: string; full: string; }; /** * Validate an `llms.txt` string against the llmstxt.org shape. * * Zero dependencies · isomorphic. */ /** A single validation finding. */ interface LlmsIssue { level: "error" | "warning"; message: string; /** 1-based line number, when the issue is tied to a specific line. */ line?: number; } /** Result of {@link validateLlmsTxt}. `valid` is false when there is any error. */ interface LlmsValidation { valid: boolean; issues: LlmsIssue[]; } /** * Check that a string is a well-formed `llms.txt`: it must have the required H1 * title, and a one-line summary blockquote is recommended. Reports malformed * link lines, a missing/duplicate/mis-placed H1, and a missing summary. * * @returns `{ valid, issues }` — `valid` is true when there are no `error`s * (warnings do not fail validation). */ declare function validateLlmsTxt(txt: string): LlmsValidation; /** * @lacspace/llms-txt * Generate and parse llms.txt and llms-full.txt (the llmstxt.org standard). * * llms.txt is a Markdown file at your site root that gives LLMs a curated map of * your most useful content; llms-full.txt inlines the full text so a model can * read everything in one request. * * Zero dependencies · isomorphic · fully typed. */ interface LlmsLink { title: string; url: string; /** Short note shown after the link. */ notes?: string; } interface LlmsSection { title: string; links: LlmsLink[]; } interface LlmsDoc { /** The site / project name (rendered as the H1). */ title: string; /** One-line summary (rendered as a blockquote). */ summary?: string; /** Free-form Markdown shown before the sections. */ details?: string; sections: LlmsSection[]; /** * The special `## Optional` block (per the spec, links an LLM may skip). * Rendered last, after {@link sections}. Round-trips through * {@link parseLlmsTxt}. Omit or leave empty to emit nothing. */ optional?: LlmsLink[]; } /** * How to order the links inside each section. * - `"title"` / `"url"` — ascending by that field * - `"title-desc"` / `"url-desc"` — descending * - a comparator — full control * Sections keep their array order; only the links within a section are sorted. */ type LinkSort = "title" | "url" | "title-desc" | "url-desc" | ((a: LlmsLink, b: LlmsLink) => number); /** Options accepted by the rendering helpers. */ interface LlmsTxtOptions { /** Order links within each section. Omit to keep the given order. */ sort?: LinkSort; /** * Escape the Markdown link characters `\ [ ] ( )` in link titles/notes so a * title like `A [beta]` cannot break the surrounding `[...](...)`. * * **On by default.** It used to be opt-in, which meant the default output was * broken for an ordinary title: `- [Guide [v2]](/g)` is not a parseable * Markdown link, and parseLlmsTxt() read it as NO LINK AT ALL — the page * silently vanished from the file an LLM reads. Escaping only ever touches * `\ [ ] ( )`, so titles without them are byte-for-byte unchanged. * * Set `false` for the old behaviour. See {@link escapeLlmsText}. * @default true */ escape?: boolean; } /** * Render an `llms.txt` document. * @param opts optional rendering options (e.g. `{ sort: "title" }`). * @example * llmsTxt({ * title: "Lacspace", * summary: "Open-source TypeScript packages and products.", * sections: [{ title: "Docs", links: [{ title: "Packages", url: "https://lacspace.com/packages" }] }], * }); */ declare function llmsTxt(doc: LlmsDoc, opts?: LlmsTxtOptions): string; interface LlmsFullSection { title: string; /** Full Markdown content for this section. */ content: string; /** Optional source URL, added as a heading link. */ url?: string; } interface LlmsFullDoc { title: string; summary?: string; sections: LlmsFullSection[]; } /** Render an `llms-full.txt` document with the full content inlined. */ declare function llmsFullTxt(doc: LlmsFullDoc): string; /** Parse an `llms.txt` string back into a structured document. */ declare function parseLlmsTxt(txt: string): LlmsDoc; /** A sitemap-ish entry — accepts `url` or `loc`, plus optional title/section. */ interface SitemapEntryLike { url?: string; loc?: string; title?: string; section?: string; } /** Meta / options for {@link llmsTxtFromSitemap}. */ interface SitemapToLlmsMeta extends LlmsTxtOptions { title: string; summary?: string; details?: string; /** Fallback section name when an entry has none. Default `"Pages"`. */ defaultSection?: string; /** * When an entry has no explicit `section`, derive one from the first path * segment ("/docs/x" → "Docs") instead of using {@link defaultSection}. */ sectionFromPath?: boolean; } /** * Build an `llms.txt` from sitemap entries — accepts either an **array** of * `{ loc | url, title?, section? }` entries or a raw **sitemap XML string**. * Groups by `section` (or, with `sectionFromPath`, by the first path segment), * derives titles from the URL when not given, and de-duplicates repeated URLs. * Pairs with `@lacspace/sitemap`. The original array signature is unchanged. */ declare function llmsTxtFromSitemap(entries: SitemapEntryLike[] | string, meta: SitemapToLlmsMeta): string; /** A single route/page entry for {@link llmsTxtFromRoutes}. */ interface LlmsRoute { title: string; url: string; notes?: string; /** Section heading to group under. Defaults to {@link RoutesToLlmsMeta.defaultSection}. */ section?: string; } /** Meta / options for {@link llmsTxtFromRoutes}. */ interface RoutesToLlmsMeta extends LlmsTxtOptions { title: string; summary?: string; details?: string; /** Section name for routes with no `section`. Default `"Docs"`. */ defaultSection?: string; } /** * Build an `llms.txt` from a flat list of route entries, grouping by `section` * (first-seen order preserved). Titles and notes are used verbatim. * @example * llmsTxtFromRoutes( * [ * { title: "Home", url: "https://acme.com/", section: "Start" }, * { title: "API", url: "https://acme.com/api", notes: "reference", section: "Docs" }, * ], * { title: "Acme", summary: "Acme docs" }, * ); */ declare function llmsTxtFromRoutes(routes: LlmsRoute[], meta: RoutesToLlmsMeta): string; /** `llms.txt` as a Fetch/edge `Response` (text/plain) for app/llms.txt/route.ts. */ declare function llmsTxtResponse(doc: LlmsDoc, init?: ResponseInit): Response; /** `llms-full.txt` as a Fetch/edge `Response` (text/plain). */ declare function llmsFullTxtResponse(doc: LlmsFullDoc, init?: ResponseInit): Response; export { type LinkSort, type LlmsDoc, type LlmsFullDoc, type LlmsFullSection, type LlmsIssue, type LlmsLink, type LlmsPage, type LlmsRoute, type LlmsSection, type LlmsTxtOptions, type LlmsValidation, type PagesToLlmsMeta, type RoutesToLlmsMeta, type SitemapEntryLike, type SitemapToLlmsMeta, escapeLlmsText, llmsFromPages, llmsFullTxt, llmsFullTxtFromPages, llmsFullTxtResponse, llmsTxt, llmsTxtFromPages, llmsTxtFromRoutes, llmsTxtFromSitemap, llmsTxtResponse, parseLlmsTxt, unescapeLlmsText, validateLlmsTxt };