blume 1.4.1 → 1.4.2
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.
- package/CHANGELOG.md +10 -0
- package/dist/cli/index.js +7 -1
- package/dist/cli/index.js.map +5 -5
- package/dist/types/core/data.d.ts +10 -0
- package/docs/configuration/ai.mdx +15 -1
- package/package.json +7 -7
- package/src/ai/link-headers.ts +7 -2
- package/src/astro/generate.ts +4 -0
- package/src/astro/templates.ts +2 -0
- package/src/components/layout/RootLayout.astro +41 -0
- package/src/core/data.ts +7 -0
|
@@ -107,6 +107,16 @@ export interface BlumeDataConfig {
|
|
|
107
107
|
/** `dateFormat`: `Intl.DateTimeFormat` options for the date stamps. */
|
|
108
108
|
dateFormat: ResolvedConfig["dateFormat"];
|
|
109
109
|
description: string | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Which agent-discovery resources exist for the layout to advertise in every
|
|
112
|
+
* page's `<head>` (`seo.agentReadability`, `ai.llmsTxt.enabled`) — the HTML
|
|
113
|
+
* counterpart of the homepage-only HTTP `Link` header, for agents that enter
|
|
114
|
+
* on a deep page (see `ai/link-headers.ts`).
|
|
115
|
+
*/
|
|
116
|
+
discovery: {
|
|
117
|
+
agentReadability: boolean;
|
|
118
|
+
llmsTxt: boolean;
|
|
119
|
+
};
|
|
110
120
|
favicon: BlumeFavicon;
|
|
111
121
|
feedback: boolean;
|
|
112
122
|
i18n: BlumeDataI18n | null;
|
|
@@ -338,7 +338,21 @@ Link: </agent-readability.json>; rel="describedby"; type="application/json",
|
|
|
338
338
|
</index.md>; rel="alternate"; type="text/markdown"
|
|
339
339
|
```
|
|
340
340
|
|
|
341
|
-
Each entry appears only when its feature is on. The `alternate` link points at the homepage's Markdown mirror — the page's own [raw Markdown](#raw-markdown) when the home route is a content page, or the synthesized `llms.txt` fallback when it's a landing page. Sites that publish APIs also get a `rel="api-catalog"` entry pointing at the [generated API catalog](#api-catalog). The header rides on every surface Blume controls: the dev server (check it with `curl -I localhost:4321`), static builds via the emitted `_headers` file (Netlify and Cloudflare), and Vercel server builds via the deploy's routing rules.
|
|
341
|
+
Each entry appears only when its feature is on. The `alternate` link points at the homepage's Markdown mirror — the page's own [raw Markdown](#raw-markdown) when the home route is a content page, or the synthesized `llms.txt` fallback when it's a landing page. Sites that publish APIs also get a `rel="api-catalog"` entry pointing at the [generated API catalog](#api-catalog). The header rides on every surface Blume controls: the dev server (check it with `curl -I localhost:4321`), static builds via the emitted `_headers` file (Netlify and Cloudflare), and Vercel server builds via the deploy's routing rules.
|
|
342
|
+
|
|
343
|
+
Not every agent enters through the root, though — one following a search result or a shared link lands on a deep page and never sees the homepage header. So every rendered page also carries the same discovery links in its HTML `<head>`, using the same IANA-registered relations:
|
|
344
|
+
|
|
345
|
+
```html
|
|
346
|
+
<link
|
|
347
|
+
rel="describedby"
|
|
348
|
+
href="/agent-readability.json"
|
|
349
|
+
type="application/json"
|
|
350
|
+
/>
|
|
351
|
+
<link rel="describedby" href="/llms.txt" type="text/plain" />
|
|
352
|
+
<link rel="alternate" href="/docs/example.md" type="text/markdown" />
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Here the `alternate` link points at _that page's own_ [raw-Markdown mirror](#raw-markdown), so an agent can jump straight from the HTML it landed on to the token-efficient version. Because the head links travel with the prerendered HTML, they also work on hosts that ignore `_headers` and can't send custom response headers at all (GitHub Pages, S3) — no matter which page the agent enters on.
|
|
342
356
|
|
|
343
357
|
### API catalog
|
|
344
358
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blume",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "Documentation that's fast, AI-ready, and zero-config.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@astrojs/mdx": "^7.0.0",
|
|
72
72
|
"@astrojs/node": "^11.0.0",
|
|
73
73
|
"@astrojs/react": "^6.0.0",
|
|
74
|
-
"@astrojs/vercel": "^11.0.
|
|
74
|
+
"@astrojs/vercel": "^11.0.3",
|
|
75
75
|
"@clack/prompts": "^1.7.0",
|
|
76
76
|
"@iconify-json/lucide": "^1.2.115",
|
|
77
77
|
"@iconify/types": "^2.0.0",
|
|
@@ -88,12 +88,12 @@
|
|
|
88
88
|
"@tailwindcss/vite": "^4",
|
|
89
89
|
"@vercel/analytics": "^2.0.1",
|
|
90
90
|
"ai": "^7.0.42",
|
|
91
|
-
"astro": "^7.0
|
|
91
|
+
"astro": "^7.1.0",
|
|
92
92
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
93
93
|
"chokidar": "^5.0.0",
|
|
94
94
|
"citty": "^0.1.6",
|
|
95
95
|
"consola": "^3.4.0",
|
|
96
|
-
"dompurify": "^3.4.
|
|
96
|
+
"dompurify": "^3.4.13",
|
|
97
97
|
"dotenv": "^17.4.2",
|
|
98
98
|
"epub-gen-memory": "^1.1.2",
|
|
99
99
|
"fast-xml-parser": "^5.10.1",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"html-escaper": "^3.0.3",
|
|
104
104
|
"image-size": "^2.0.2",
|
|
105
105
|
"jiti": "^2.4.0",
|
|
106
|
-
"js-yaml": "^4.1
|
|
106
|
+
"js-yaml": "^4.3.1",
|
|
107
107
|
"katex": "^0.18.1",
|
|
108
108
|
"markdown-table": "^3.0.4",
|
|
109
109
|
"marked": "^18.0.5",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"mdast-util-gfm": "^3.1.0",
|
|
112
112
|
"mdast-util-to-string": "^4.0.0",
|
|
113
113
|
"medium-zoom": "^1.1.0",
|
|
114
|
-
"mermaid": "^11.
|
|
114
|
+
"mermaid": "^11.16.1",
|
|
115
115
|
"micromark-extension-gfm": "^3.0.0",
|
|
116
116
|
"node-html-parser": "^9.0.0",
|
|
117
117
|
"openapi-sampler": "^1.7.4",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"tinyglobby": "^0.2.10",
|
|
132
132
|
"twoslash": "^0.3.9",
|
|
133
133
|
"typescript": "^6.0.3",
|
|
134
|
-
"undici": "^8.
|
|
134
|
+
"undici": "^8.9.0",
|
|
135
135
|
"write-file-atomic": "^8.0.0",
|
|
136
136
|
"zod": "^4.3.6"
|
|
137
137
|
},
|
package/src/ai/link-headers.ts
CHANGED
|
@@ -14,8 +14,13 @@ import { API_CATALOG_PATH, hasApiCatalog } from "./api-catalog.ts";
|
|
|
14
14
|
* The header is homepage-only by design: the root response is what agents
|
|
15
15
|
* probe, and `agent-readability.json` indexes the rest of the surface (the
|
|
16
16
|
* per-route Markdown pattern, MCP, feeds) far better than per-page headers
|
|
17
|
-
* could.
|
|
18
|
-
*
|
|
17
|
+
* could. An agent that enters on a deep page (a search result, a shared link)
|
|
18
|
+
* never sees this header at all — that path is covered in the HTML instead:
|
|
19
|
+
* every page's `<head>` carries the same `describedby` links plus its own
|
|
20
|
+
* Markdown mirror as an `alternate` (see `RootLayout.astro`), which also
|
|
21
|
+
* reaches hosts where Blume can't set response headers. Targets are
|
|
22
|
+
* root-relative under `deployment.base` — RFC 8288 resolves them against the
|
|
23
|
+
* request URL. Returns null when nothing is advertisable.
|
|
19
24
|
*/
|
|
20
25
|
export const buildHomeLinkHeader = (
|
|
21
26
|
config: ResolvedConfig,
|
package/src/astro/generate.ts
CHANGED
|
@@ -1160,6 +1160,10 @@ export const buildRuntimeData = (project: BlumeProject): string => {
|
|
|
1160
1160
|
codeWrap: config.markdown.code.wrap,
|
|
1161
1161
|
dateFormat: config.dateFormat,
|
|
1162
1162
|
description: config.description,
|
|
1163
|
+
discovery: {
|
|
1164
|
+
agentReadability: config.seo.agentReadability,
|
|
1165
|
+
llmsTxt: config.ai.llmsTxt.enabled,
|
|
1166
|
+
},
|
|
1163
1167
|
favicon: resolveFavicon(project),
|
|
1164
1168
|
feedback: config.feedback,
|
|
1165
1169
|
i18n: i18n
|
package/src/astro/templates.ts
CHANGED
|
@@ -1801,6 +1801,7 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
1801
1801
|
exportPdf={${options.exportPdf}}
|
|
1802
1802
|
exportEpub={${options.exportEpub}}
|
|
1803
1803
|
feeds={data.feeds}
|
|
1804
|
+
discovery={data.config.discovery}
|
|
1804
1805
|
siteUrl={data.config.site}
|
|
1805
1806
|
pageType={frontmatter.type}
|
|
1806
1807
|
published={frontmatter.date ?? frontmatter.changelog?.date ?? null}
|
|
@@ -2022,6 +2023,7 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
2022
2023
|
exportPdf={${options.exportPdf}}
|
|
2023
2024
|
exportEpub={${options.exportEpub}}
|
|
2024
2025
|
feeds={data.feeds}
|
|
2026
|
+
discovery={data.config.discovery}
|
|
2025
2027
|
siteUrl={data.config.site}
|
|
2026
2028
|
noindex={false}
|
|
2027
2029
|
structuredDataEnabled={data.config.structuredData}
|
|
@@ -117,6 +117,14 @@ interface Props {
|
|
|
117
117
|
exportPdf?: boolean;
|
|
118
118
|
exportEpub?: boolean;
|
|
119
119
|
feeds?: { title: string; href: string }[];
|
|
120
|
+
/**
|
|
121
|
+
* Which agent-discovery resources exist, advertised as `describedby` head
|
|
122
|
+
* links on every page — so an agent entering on a deep page (a search
|
|
123
|
+
* result, a shared link) finds the machine-readable surface without probing
|
|
124
|
+
* the site root. The HTML counterpart of the homepage-only HTTP `Link`
|
|
125
|
+
* header (see `ai/link-headers.ts`).
|
|
126
|
+
*/
|
|
127
|
+
discovery?: { agentReadability: boolean; llmsTxt: boolean } | null;
|
|
120
128
|
siteUrl?: string | null;
|
|
121
129
|
pageType?: string;
|
|
122
130
|
published?: string | Date | null;
|
|
@@ -198,6 +206,7 @@ const {
|
|
|
198
206
|
exportPdf,
|
|
199
207
|
exportEpub,
|
|
200
208
|
feeds,
|
|
209
|
+
discovery,
|
|
201
210
|
siteUrl,
|
|
202
211
|
pageType,
|
|
203
212
|
published,
|
|
@@ -312,6 +321,15 @@ const formattedLastModified =
|
|
|
312
321
|
).format(lastModifiedDate)
|
|
313
322
|
: null;
|
|
314
323
|
|
|
324
|
+
// This page's raw-Markdown mirror, advertised as a `text/markdown` alternate
|
|
325
|
+
// in the head. Content routes always have one (see `markdownRoutePaths`, which
|
|
326
|
+
// serves the same `route === "/" ? "/index.md" : "<route>.md"` mapping as the
|
|
327
|
+
// PageActions menu); the generated changelog index — the only "bare" page — is
|
|
328
|
+
// not a content route and has none.
|
|
329
|
+
const markdownMirror = isBare
|
|
330
|
+
? null
|
|
331
|
+
: withBase(page.route === "/" ? "/index.md" : `${page.route}.md`);
|
|
332
|
+
|
|
315
333
|
// The hosted MCP server's absolute URL, used by the page-actions install menu.
|
|
316
334
|
// Needs a configured site to be useful, so the menu is hidden without one.
|
|
317
335
|
const mcpUrl =
|
|
@@ -428,6 +446,29 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
428
446
|
/>
|
|
429
447
|
))
|
|
430
448
|
}
|
|
449
|
+
{/* Agent discovery on every page, not just the root: an agent that enters
|
|
450
|
+
on a deep page (a search result, a shared link) never sees the homepage
|
|
451
|
+
HTTP Link header, so the head carries the same describedby links plus
|
|
452
|
+
this page's own raw-Markdown mirror. Both rels are IANA-registered. */}
|
|
453
|
+
{
|
|
454
|
+
discovery?.agentReadability && (
|
|
455
|
+
<link
|
|
456
|
+
href={withBase("/agent-readability.json")}
|
|
457
|
+
rel="describedby"
|
|
458
|
+
type="application/json"
|
|
459
|
+
/>
|
|
460
|
+
)
|
|
461
|
+
}
|
|
462
|
+
{
|
|
463
|
+
discovery?.llmsTxt && (
|
|
464
|
+
<link href={withBase("/llms.txt")} rel="describedby" type="text/plain" />
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
{
|
|
468
|
+
markdownMirror && (
|
|
469
|
+
<link href={markdownMirror} rel="alternate" type="text/markdown" />
|
|
470
|
+
)
|
|
471
|
+
}
|
|
431
472
|
{
|
|
432
473
|
structuredDataJson && (
|
|
433
474
|
<script
|
package/src/core/data.ts
CHANGED
|
@@ -107,6 +107,13 @@ export interface BlumeDataConfig {
|
|
|
107
107
|
/** `dateFormat`: `Intl.DateTimeFormat` options for the date stamps. */
|
|
108
108
|
dateFormat: ResolvedConfig["dateFormat"];
|
|
109
109
|
description: string | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Which agent-discovery resources exist for the layout to advertise in every
|
|
112
|
+
* page's `<head>` (`seo.agentReadability`, `ai.llmsTxt.enabled`) — the HTML
|
|
113
|
+
* counterpart of the homepage-only HTTP `Link` header, for agents that enter
|
|
114
|
+
* on a deep page (see `ai/link-headers.ts`).
|
|
115
|
+
*/
|
|
116
|
+
discovery: { agentReadability: boolean; llmsTxt: boolean };
|
|
110
117
|
favicon: BlumeFavicon;
|
|
111
118
|
feedback: boolean;
|
|
112
119
|
i18n: BlumeDataI18n | null;
|