blume 1.3.0 → 1.3.1
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 +9 -0
- package/dist/cli/index.js +71 -55
- package/dist/cli/index.js.map +12 -12
- package/docs/configuration/ai.mdx +2 -2
- package/package.json +1 -1
- package/src/ai/link-headers.ts +4 -3
- package/src/ai/llms.ts +4 -2
- package/src/ai/markdown.ts +34 -1
- package/src/astro/generate.ts +2 -2
- package/src/astro/integration.ts +3 -1
- package/src/astro/templates.ts +8 -3
- package/src/cli/commands/build.ts +17 -11
- package/src/deploy/vercel-negotiation.ts +30 -13
- package/src/theme/fonts.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# blume
|
|
2
2
|
|
|
3
|
+
## 1.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4d7dc87: Negotiate `Accept: text/markdown` on the homepage even when it's a landing page. A user-authored home page has no Markdown source, so agent requests for a markdown homepage previously fell through to HTML; the homepage's mirror now falls back to the `llms.txt` index — the machine-readable map of the site — served at `/index.md` and wired into the dev server, the Vercel routing config, and the homepage `Link` header's `rel="alternate"` entry.
|
|
8
|
+
- ff31ab5: Remove a polynomial-backtracking regex from the font-name slugifier's dash trim
|
|
9
|
+
- 02eb6c7: Fix the homepage `Link` header and `Vary: Accept` never being sent on Vercel deploys. The injected header routes sat after `handle: "filesystem"` in the Build Output config — the miss phase, which prerendered static responses never reach — so agent-readiness checkers saw no `Link` header on `GET /`. Both header routes now ride in the main phase, ahead of static-file matching.
|
|
10
|
+
- de62812: Stamp an `x-markdown-tokens` header (estimated token count, ~4 characters per token) on Markdown responses, following the Cloudflare Markdown for Agents convention: the raw-Markdown endpoints send it on dev and server-rendered responses, and the Vercel routing config carries it on the negotiated homepage.
|
|
11
|
+
|
|
3
12
|
## 1.3.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
package/dist/cli/index.js
CHANGED
|
@@ -3522,7 +3522,7 @@ var GOOGLE_FONTS = {
|
|
|
3522
3522
|
var FONT_SLUGS = Object.keys(GOOGLE_FONTS);
|
|
3523
3523
|
var isFontSlug = (value) => Object.hasOwn(GOOGLE_FONTS, value);
|
|
3524
3524
|
var DEFAULT_REMOTE_WEIGHTS = [400, 500, 600, 700];
|
|
3525
|
-
var slugifyFontName = (name) => name.toLowerCase().replaceAll(/[^a-z0-9]+/gu, "-").replaceAll(
|
|
3525
|
+
var slugifyFontName = (name) => name.toLowerCase().replaceAll(/[^a-z0-9]+/gu, "-").replaceAll(/^-|-$/gu, "");
|
|
3526
3526
|
var fontVar = (slug) => `--blume-ff-${slug}`;
|
|
3527
3527
|
var SLOTS = ["display", "body", "mono"];
|
|
3528
3528
|
var slotCategory = (slot) => slot === "mono" ? "mono" : "sans";
|
|
@@ -4269,8 +4269,13 @@ export function getStaticPaths() {
|
|
|
4269
4269
|
export function GET({ props }: { props: { route: string } }) {
|
|
4270
4270
|
const entries = raw as Record<string, { md?: string; mdx?: string }>;
|
|
4271
4271
|
const entry = entries[props.route];
|
|
4272
|
-
|
|
4273
|
-
|
|
4272
|
+
const body = entry ? ${kind === "md" ? '(entry.md ?? entry.mdx ?? "")' : '(entry.mdx ?? "")'} : "";
|
|
4273
|
+
return new Response(body, {
|
|
4274
|
+
headers: {
|
|
4275
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
4276
|
+
// ~4 characters per token; keep in sync with markdownTokenCount.
|
|
4277
|
+
"x-markdown-tokens": String(Math.ceil(body.length / 4)),
|
|
4278
|
+
},
|
|
4274
4279
|
});
|
|
4275
4280
|
}
|
|
4276
4281
|
`;
|
|
@@ -12593,7 +12598,7 @@ var indexedNavigations = (project) => {
|
|
|
12593
12598
|
}
|
|
12594
12599
|
return [{ nav: project.graph.navigation }];
|
|
12595
12600
|
};
|
|
12596
|
-
var
|
|
12601
|
+
var buildLlmsIndex = (project) => {
|
|
12597
12602
|
const { config } = project;
|
|
12598
12603
|
const { site } = config.deployment;
|
|
12599
12604
|
const base = normalizeBasePath(config.deployment.base);
|
|
@@ -12704,12 +12709,55 @@ ${sections.join(`
|
|
|
12704
12709
|
};
|
|
12705
12710
|
var buildLlmsFiles = async (project) => ({
|
|
12706
12711
|
full: await buildFull(project),
|
|
12707
|
-
index:
|
|
12712
|
+
index: buildLlmsIndex(project)
|
|
12708
12713
|
});
|
|
12709
12714
|
|
|
12715
|
+
// src/ai/markdown.ts
|
|
12716
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
12717
|
+
var agentMarkdown = (entry) => entry.md ?? entry.mdx;
|
|
12718
|
+
var markdownTokenCount = (text) => Math.ceil(text.length / 4);
|
|
12719
|
+
var buildRawMarkdown = async (project) => {
|
|
12720
|
+
const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
|
|
12721
|
+
const components = {
|
|
12722
|
+
...exampleComponentSerializers(project.examples ?? {}),
|
|
12723
|
+
...project.config.ai.markdownComponents
|
|
12724
|
+
};
|
|
12725
|
+
const readRoute = async (route) => {
|
|
12726
|
+
const page = pageById.get(route.id);
|
|
12727
|
+
if (page) {
|
|
12728
|
+
return await readEntryText(project, page);
|
|
12729
|
+
}
|
|
12730
|
+
return route.sourcePath ? await readFile10(route.sourcePath, "utf-8") : "";
|
|
12731
|
+
};
|
|
12732
|
+
const entries = await Promise.all(project.manifest.routes.map(async (route) => {
|
|
12733
|
+
let text = await readRoute(route);
|
|
12734
|
+
if (route.sourcePath) {
|
|
12735
|
+
text = rewriteRelativeImages({
|
|
12736
|
+
deployBase: project.config.deployment.base,
|
|
12737
|
+
projectRoot: project.context.root,
|
|
12738
|
+
source: text,
|
|
12739
|
+
sourcePath: route.sourcePath
|
|
12740
|
+
});
|
|
12741
|
+
}
|
|
12742
|
+
const source = applyAgentVisibility(text);
|
|
12743
|
+
const md = downlevelComponents(source, components, frontmatter_default(source).data);
|
|
12744
|
+
const entry = md === source ? { mdx: source } : { md, mdx: source };
|
|
12745
|
+
return [route.path, entry];
|
|
12746
|
+
}));
|
|
12747
|
+
const map = Object.fromEntries(entries);
|
|
12748
|
+
if (!map["/"]) {
|
|
12749
|
+
map["/"] = { mdx: buildLlmsIndex(project) };
|
|
12750
|
+
}
|
|
12751
|
+
return map;
|
|
12752
|
+
};
|
|
12753
|
+
var markdownRoutePaths = (project) => {
|
|
12754
|
+
const paths = project.manifest.routes.map((route) => route.path);
|
|
12755
|
+
return paths.includes("/") ? paths : [...paths, "/"];
|
|
12756
|
+
};
|
|
12757
|
+
|
|
12710
12758
|
// src/ai/skills.ts
|
|
12711
12759
|
import { createHash as createHash2 } from "node:crypto";
|
|
12712
|
-
import { readdir, readFile as
|
|
12760
|
+
import { readdir, readFile as readFile11, stat as stat2 } from "node:fs/promises";
|
|
12713
12761
|
import { join as join18 } from "pathe";
|
|
12714
12762
|
|
|
12715
12763
|
// src/ai/tar.ts
|
|
@@ -12794,7 +12842,7 @@ var collectEntries = async (dir, prefix = "") => {
|
|
|
12794
12842
|
if (item.isDirectory()) {
|
|
12795
12843
|
entries.push(...await collectEntries(path, relative12));
|
|
12796
12844
|
} else if (item.isFile()) {
|
|
12797
|
-
const [content, info] = await Promise.all([
|
|
12845
|
+
const [content, info] = await Promise.all([readFile11(path), stat2(path)]);
|
|
12798
12846
|
entries.push({
|
|
12799
12847
|
content: new Uint8Array(content),
|
|
12800
12848
|
executable: (info.mode & 64) !== 0,
|
|
@@ -12895,12 +12943,12 @@ var buildSignaturesDirectory = (config) => {
|
|
|
12895
12943
|
|
|
12896
12944
|
// src/core/gitignore.ts
|
|
12897
12945
|
import { existsSync as existsSync10 } from "node:fs";
|
|
12898
|
-
import { readFile as
|
|
12946
|
+
import { readFile as readFile12, writeFile as writeFile6 } from "node:fs/promises";
|
|
12899
12947
|
import { join as join19 } from "pathe";
|
|
12900
12948
|
var gitignoreKey = (line) => line.trim().replace(/\/+$/u, "");
|
|
12901
12949
|
var ensureGitignore = async (root, entries) => {
|
|
12902
12950
|
const path = join19(root, ".gitignore");
|
|
12903
|
-
const existing = existsSync10(path) ? await
|
|
12951
|
+
const existing = existsSync10(path) ? await readFile12(path, "utf-8") : "";
|
|
12904
12952
|
const present = new Set(existing.split(`
|
|
12905
12953
|
`).flatMap((line) => {
|
|
12906
12954
|
const key = gitignoreKey(line);
|
|
@@ -13167,7 +13215,7 @@ var chunkPatterns = (patterns) => {
|
|
|
13167
13215
|
}
|
|
13168
13216
|
return chunks;
|
|
13169
13217
|
};
|
|
13170
|
-
var buildNegotiationRoutes = (routePaths) => {
|
|
13218
|
+
var buildNegotiationRoutes = (routePaths, homeTokens) => {
|
|
13171
13219
|
const home = routePaths.includes("/");
|
|
13172
13220
|
const rest = routePaths.filter((path) => path !== "/").map((path) => routePattern(path));
|
|
13173
13221
|
const chunks = chunkPatterns(rest);
|
|
@@ -13175,7 +13223,7 @@ var buildNegotiationRoutes = (routePaths) => {
|
|
|
13175
13223
|
{
|
|
13176
13224
|
dest: "/index.md",
|
|
13177
13225
|
has: ACCEPT_MARKDOWN_CONDITION,
|
|
13178
|
-
headers: VARY_ACCEPT,
|
|
13226
|
+
headers: homeTokens === undefined ? VARY_ACCEPT : { ...VARY_ACCEPT, "x-markdown-tokens": String(homeTokens) },
|
|
13179
13227
|
src: "^/$"
|
|
13180
13228
|
}
|
|
13181
13229
|
] : [];
|
|
@@ -13197,7 +13245,7 @@ var buildNegotiationRoutes = (routePaths) => {
|
|
|
13197
13245
|
};
|
|
13198
13246
|
var HOME_SRC = "^/$";
|
|
13199
13247
|
var isNegotiationRoute = (route) => route.has?.some((condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE) === true || route.continue === true && route.headers?.vary === "Accept" && typeof route.src === "string" && Object.keys(route).length === 3 || route.continue === true && typeof route.headers?.link === "string" && route.src === HOME_SRC && Object.keys(route).length === 3;
|
|
13200
|
-
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides) => {
|
|
13248
|
+
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides, homeTokens) => {
|
|
13201
13249
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
13202
13250
|
if (routePaths.length === 0 && !homeLinkHeader && overrideEntries.length === 0) {
|
|
13203
13251
|
return null;
|
|
@@ -13219,7 +13267,7 @@ var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTy
|
|
|
13219
13267
|
if (filesystemIndex === -1) {
|
|
13220
13268
|
return null;
|
|
13221
13269
|
}
|
|
13222
|
-
const { headerRoutes, rewriteRoutes } = buildNegotiationRoutes(routePaths);
|
|
13270
|
+
const { headerRoutes, rewriteRoutes } = buildNegotiationRoutes(routePaths, homeTokens);
|
|
13223
13271
|
if (homeLinkHeader) {
|
|
13224
13272
|
headerRoutes.push({
|
|
13225
13273
|
continue: true,
|
|
@@ -13227,8 +13275,7 @@ var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTy
|
|
|
13227
13275
|
src: HOME_SRC
|
|
13228
13276
|
});
|
|
13229
13277
|
}
|
|
13230
|
-
routes.splice(filesystemIndex
|
|
13231
|
-
routes.splice(filesystemIndex, 0, ...rewriteRoutes);
|
|
13278
|
+
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes);
|
|
13232
13279
|
config.routes = routes;
|
|
13233
13280
|
return `${JSON.stringify(config, null, "\t")}
|
|
13234
13281
|
`;
|
|
@@ -13622,40 +13669,6 @@ var buildAskData = async (project) => {
|
|
|
13622
13669
|
};
|
|
13623
13670
|
};
|
|
13624
13671
|
|
|
13625
|
-
// src/ai/markdown.ts
|
|
13626
|
-
import { readFile as readFile12 } from "node:fs/promises";
|
|
13627
|
-
var agentMarkdown = (entry) => entry.md ?? entry.mdx;
|
|
13628
|
-
var buildRawMarkdown = async (project) => {
|
|
13629
|
-
const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
|
|
13630
|
-
const components = {
|
|
13631
|
-
...exampleComponentSerializers(project.examples ?? {}),
|
|
13632
|
-
...project.config.ai.markdownComponents
|
|
13633
|
-
};
|
|
13634
|
-
const readRoute = async (route) => {
|
|
13635
|
-
const page = pageById.get(route.id);
|
|
13636
|
-
if (page) {
|
|
13637
|
-
return await readEntryText(project, page);
|
|
13638
|
-
}
|
|
13639
|
-
return route.sourcePath ? await readFile12(route.sourcePath, "utf-8") : "";
|
|
13640
|
-
};
|
|
13641
|
-
const entries = await Promise.all(project.manifest.routes.map(async (route) => {
|
|
13642
|
-
let text2 = await readRoute(route);
|
|
13643
|
-
if (route.sourcePath) {
|
|
13644
|
-
text2 = rewriteRelativeImages({
|
|
13645
|
-
deployBase: project.config.deployment.base,
|
|
13646
|
-
projectRoot: project.context.root,
|
|
13647
|
-
source: text2,
|
|
13648
|
-
sourcePath: route.sourcePath
|
|
13649
|
-
});
|
|
13650
|
-
}
|
|
13651
|
-
const source = applyAgentVisibility(text2);
|
|
13652
|
-
const md = downlevelComponents(source, components, frontmatter_default(source).data);
|
|
13653
|
-
const entry = md === source ? { mdx: source } : { md, mdx: source };
|
|
13654
|
-
return [route.path, entry];
|
|
13655
|
-
}));
|
|
13656
|
-
return Object.fromEntries(entries);
|
|
13657
|
-
};
|
|
13658
|
-
|
|
13659
13672
|
// src/ai/mcp/data.ts
|
|
13660
13673
|
var buildMcpData = async (project) => {
|
|
13661
13674
|
const { config, graph, manifest } = project;
|
|
@@ -16366,7 +16379,7 @@ var generateRuntime = async (project) => {
|
|
|
16366
16379
|
aliases: resolveTsconfigAliases(context.root),
|
|
16367
16380
|
askPath,
|
|
16368
16381
|
config,
|
|
16369
|
-
contentRoutes: project
|
|
16382
|
+
contentRoutes: markdownRoutePaths(project),
|
|
16370
16383
|
contentWatchesRuntimeDir: contentWatchesRuntimeDir(hasFilesystemSource, docsCollection.base, context),
|
|
16371
16384
|
context,
|
|
16372
16385
|
dataPath,
|
|
@@ -16700,7 +16713,7 @@ var emitHeaderFiles = async (project, distDir) => {
|
|
|
16700
16713
|
if (config.deployment.output !== "static" || existsSync18(join30(distDir, "_headers"))) {
|
|
16701
16714
|
return;
|
|
16702
16715
|
}
|
|
16703
|
-
await writeFile8(join30(distDir, "_headers"), buildNetlifyHeaders(config, buildHomeLinkHeader(config, project
|
|
16716
|
+
await writeFile8(join30(distDir, "_headers"), buildNetlifyHeaders(config, buildHomeLinkHeader(config, markdownRoutePaths(project))), "utf-8");
|
|
16704
16717
|
logger.success("Emitted _headers (UTF-8 Content-Type + homepage Link header)");
|
|
16705
16718
|
};
|
|
16706
16719
|
var emitAgentSkills = async (project, distDir) => {
|
|
@@ -16756,7 +16769,8 @@ var emitWellKnownFiles = async (config, distDir) => {
|
|
|
16756
16769
|
logger.success(`Generated ${file.path.slice(1)} (${file.label})`);
|
|
16757
16770
|
}
|
|
16758
16771
|
};
|
|
16759
|
-
var emitVercelNegotiation = async (
|
|
16772
|
+
var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
16773
|
+
const { config } = project;
|
|
16760
16774
|
const configPath = join30(root, ".vercel", "output", "config.json");
|
|
16761
16775
|
if (!existsSync18(configPath)) {
|
|
16762
16776
|
return;
|
|
@@ -16765,7 +16779,9 @@ var emitVercelNegotiation = async (config, routePaths, root) => {
|
|
|
16765
16779
|
...hasApiCatalog(config) ? { [API_CATALOG_PATH.slice(1)]: API_CATALOG_TYPE } : {},
|
|
16766
16780
|
...config.ai.webBotAuth.keys.length > 0 ? { [SIGNATURES_DIRECTORY_PATH.slice(1)]: SIGNATURES_DIRECTORY_TYPE } : {}
|
|
16767
16781
|
};
|
|
16768
|
-
const
|
|
16782
|
+
const rawMarkdown = await buildRawMarkdown(project);
|
|
16783
|
+
const home = rawMarkdown["/"];
|
|
16784
|
+
const injected = injectNegotiationRoutes(await readFile17(configPath, "utf-8"), routePaths, buildHomeLinkHeader(config, routePaths), overrides, home ? markdownTokenCount(agentMarkdown(home)) : undefined);
|
|
16769
16785
|
if (injected === null) {
|
|
16770
16786
|
logger.warn("Could not wire Accept: text/markdown negotiation into .vercel/output/config.json — raw Markdown stays available at the .md URLs.");
|
|
16771
16787
|
return;
|
|
@@ -17026,7 +17042,7 @@ var buildCommand = defineCommand3({
|
|
|
17026
17042
|
logger.success(`Surfaced ${adapter} output to ${surfaced.to}`);
|
|
17027
17043
|
}
|
|
17028
17044
|
if (project.config.deployment.output === "server" && adapter === "vercel") {
|
|
17029
|
-
await emitVercelNegotiation(project
|
|
17045
|
+
await emitVercelNegotiation(project, markdownRoutePaths(project), root);
|
|
17030
17046
|
}
|
|
17031
17047
|
await publishBuildArtifacts(project, deployStaticDir(project.config, project.context), args);
|
|
17032
17048
|
}
|
|
@@ -19873,5 +19889,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
19873
19889
|
});
|
|
19874
19890
|
runMain(main);
|
|
19875
19891
|
|
|
19876
|
-
//# debugId=
|
|
19892
|
+
//# debugId=DE086E5CDF1627AA64756E2164756E21
|
|
19877
19893
|
//# sourceMappingURL=index.js.map
|