blume 1.2.1 → 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 +46 -0
- package/dist/cli/index.js +1765 -603
- package/dist/cli/index.js.map +37 -28
- package/dist/types/core/config-input.d.ts +131 -11
- package/dist/types/core/config.d.ts +9 -1
- package/dist/types/core/data.d.ts +24 -5
- package/dist/types/core/i18n-ui.d.ts +58 -799
- package/dist/types/core/schema.d.ts +534 -3305
- package/dist/types/theme/fonts.d.ts +55 -11
- package/docs/02-deployment.mdx +2 -0
- package/docs/07-faq.mdx +14 -14
- package/docs/advanced/skills.mdx +2 -2
- package/docs/configuration/ai.mdx +126 -2
- package/docs/configuration/index.mdx +19 -1
- package/docs/configuration/search.mdx +2 -0
- package/docs/configuration/seo.mdx +26 -3
- package/docs/configuration/theming.mdx +44 -2
- package/docs/content/syntax.mdx +18 -2
- package/docs/reference/cli.mdx +3 -3
- package/package.json +9 -8
- package/skills/blume/SKILL.md +6 -4
- package/skills/blume-migrate/SKILL.md +5 -3
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/monorepo.md +2 -1
- package/src/ai/agent-readability.ts +31 -1
- package/src/ai/api-catalog.ts +81 -0
- package/src/ai/link-headers.ts +53 -0
- package/src/ai/llms.ts +16 -3
- package/src/ai/markdown.ts +49 -3
- package/src/ai/mcp/discovery.ts +70 -15
- package/src/ai/mcp/server.ts +5 -4
- package/src/ai/skills.ts +193 -0
- package/src/ai/tar.ts +104 -0
- package/src/ai/web-bot-auth.ts +30 -0
- package/src/astro/generate.ts +118 -8
- package/src/astro/integration.ts +54 -14
- package/src/astro/templates.ts +184 -36
- package/src/audit/catalog.ts +20 -0
- package/src/audit/checks/dns-aid.ts +190 -0
- package/src/audit/report.ts +5 -0
- package/src/audit/run.ts +2 -0
- package/src/cli/commands/build.ts +184 -9
- package/src/cli/init/scaffold.ts +1 -1
- package/src/components/islands/ask-ai.tsx +4 -1
- package/src/components/islands/webmcp.ts +203 -0
- package/src/components/layout/PageLayout.astro +2 -0
- package/src/components/layout/ReferenceLayout.astro +2 -0
- package/src/components/layout/RootLayout.astro +62 -10
- package/src/components/layout/Search.astro +2 -2
- package/src/components/layout/WebMcp.astro +49 -0
- package/src/core/config-input.ts +143 -11
- package/src/core/config.ts +17 -1
- package/src/core/content-assets.ts +199 -0
- package/src/core/data.ts +21 -5
- package/src/core/diagnostics.ts +6 -5
- package/src/core/i18n-ui.ts +19 -28
- package/src/core/project-graph.ts +6 -0
- package/src/core/schema.ts +224 -71
- package/src/core/sources/normalize.ts +5 -5
- package/src/deploy/headers.ts +45 -3
- package/src/deploy/vercel-negotiation.ts +250 -0
- package/src/markdown/mermaid.ts +7 -1
- package/src/markdown/table-wrap.ts +33 -1
- package/src/og/card.ts +91 -22
- package/src/og/derive.ts +200 -0
- package/src/og/index.ts +6 -1
- package/src/search/orama-index.ts +98 -4
- package/src/theme/entry.ts +34 -13
- package/src/theme/fonts.ts +185 -30
- package/dist/types/og/card.d.ts +0 -63
- package/dist/types/og/dimensions.d.ts +0 -12
package/dist/cli/index.js
CHANGED
|
@@ -391,8 +391,8 @@ var diagnosticsFromIssues = (issues, options) => issues.map((issue) => {
|
|
|
391
391
|
};
|
|
392
392
|
});
|
|
393
393
|
var diagnosticsFromZod = (error, options) => diagnosticsFromIssues(error.issues.map((issue) => ({
|
|
394
|
-
message:
|
|
395
|
-
path: issue.path
|
|
394
|
+
message: issue.message,
|
|
395
|
+
path: issue.path.filter((segment) => typeof segment !== "symbol")
|
|
396
396
|
})), options);
|
|
397
397
|
var ESC = String.fromCodePoint(27);
|
|
398
398
|
var COLORS = {
|
|
@@ -1180,6 +1180,22 @@ var CHECKS = [
|
|
|
1180
1180
|
tier: "static",
|
|
1181
1181
|
title: "Indexable page missing from llms.txt"
|
|
1182
1182
|
},
|
|
1183
|
+
{
|
|
1184
|
+
category: "ai",
|
|
1185
|
+
fix: "Publish a ServiceMode SVCB or HTTPS record at `_index._agents.<host>` with your DNS provider so agents can discover the site via DNS (DNS-AID).",
|
|
1186
|
+
id: "BLUME_AUDIT_DNS_AID_MISSING",
|
|
1187
|
+
severity: "info",
|
|
1188
|
+
tier: "network",
|
|
1189
|
+
title: "No DNS-AID agent-discovery records"
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
category: "ai",
|
|
1193
|
+
fix: "Enable DNSSEC for the zone so validating resolvers return authenticated DNS-AID answers; if your DNS provider doesn't support DNSSEC, the records still work unsigned.",
|
|
1194
|
+
id: "BLUME_AUDIT_DNS_AID_UNSIGNED",
|
|
1195
|
+
severity: "info",
|
|
1196
|
+
tier: "network",
|
|
1197
|
+
title: "DNS-AID records are not DNSSEC-authenticated"
|
|
1198
|
+
},
|
|
1183
1199
|
{
|
|
1184
1200
|
category: "structured-data",
|
|
1185
1201
|
fix: "The JSON-LD block must be valid JSON.",
|
|
@@ -1376,6 +1392,9 @@ var formatReport = (result, root, options = {}) => {
|
|
|
1376
1392
|
const shown = options.verbose ? group.findings : group.findings.slice(0, PREVIEW);
|
|
1377
1393
|
for (const diagnostic of shown) {
|
|
1378
1394
|
lines.push(findingLine(diagnostic, root));
|
|
1395
|
+
if (options.verbose) {
|
|
1396
|
+
lines.push(` ${COLORS2.dim}${diagnostic.message}${COLORS2.reset}`);
|
|
1397
|
+
}
|
|
1379
1398
|
}
|
|
1380
1399
|
const hidden = group.count - shown.length;
|
|
1381
1400
|
if (hidden > 0) {
|
|
@@ -1831,6 +1850,104 @@ var contentChecks = {
|
|
|
1831
1850
|
tier: "static"
|
|
1832
1851
|
};
|
|
1833
1852
|
|
|
1853
|
+
// src/audit/checks/dns-aid.ts
|
|
1854
|
+
var RR_SVCB = 64;
|
|
1855
|
+
var RR_HTTPS = 65;
|
|
1856
|
+
var DOH_TIMEOUT_MS = 5000;
|
|
1857
|
+
var DOH_RESOLVERS = [
|
|
1858
|
+
"https://dns.google/resolve",
|
|
1859
|
+
"https://cloudflare-dns.com/dns-query"
|
|
1860
|
+
];
|
|
1861
|
+
var resolvers = () => {
|
|
1862
|
+
const override = process.env.BLUME_DOH_URL;
|
|
1863
|
+
return override ? [override] : DOH_RESOLVERS;
|
|
1864
|
+
};
|
|
1865
|
+
var fetchDohJson = async (base, name, type, fetchFn) => {
|
|
1866
|
+
try {
|
|
1867
|
+
const response = await fetchFn(`${base}?name=${encodeURIComponent(name)}&type=${type}`, {
|
|
1868
|
+
headers: { accept: "application/dns-json" },
|
|
1869
|
+
signal: AbortSignal.timeout(DOH_TIMEOUT_MS)
|
|
1870
|
+
});
|
|
1871
|
+
if (!response.ok) {
|
|
1872
|
+
return null;
|
|
1873
|
+
}
|
|
1874
|
+
return await response.json();
|
|
1875
|
+
} catch {
|
|
1876
|
+
return null;
|
|
1877
|
+
}
|
|
1878
|
+
};
|
|
1879
|
+
var queryDoh = async (name, type, fetchFn) => {
|
|
1880
|
+
for (const base of resolvers()) {
|
|
1881
|
+
const response = await fetchDohJson(base, name, type, fetchFn);
|
|
1882
|
+
if (response) {
|
|
1883
|
+
return response;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
return null;
|
|
1887
|
+
};
|
|
1888
|
+
var lookupDnsAid = async (host, fetchFn = fetch) => {
|
|
1889
|
+
const name = `_index._agents.${host}`;
|
|
1890
|
+
let answered = false;
|
|
1891
|
+
let found = false;
|
|
1892
|
+
let authenticated = true;
|
|
1893
|
+
const responses = await Promise.all([RR_HTTPS, RR_SVCB].map((type) => queryDoh(name, type, fetchFn)));
|
|
1894
|
+
for (const response of responses) {
|
|
1895
|
+
if (!response) {
|
|
1896
|
+
continue;
|
|
1897
|
+
}
|
|
1898
|
+
answered = true;
|
|
1899
|
+
const records = (response.Answer ?? []).filter((answer) => answer.type === RR_SVCB || answer.type === RR_HTTPS);
|
|
1900
|
+
if (records.length > 0) {
|
|
1901
|
+
found = true;
|
|
1902
|
+
authenticated &&= response.AD === true;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
return answered ? { authenticated, found } : null;
|
|
1906
|
+
};
|
|
1907
|
+
var dnsAidFindings = (host, result) => {
|
|
1908
|
+
if (!result.found) {
|
|
1909
|
+
return [
|
|
1910
|
+
finding("BLUME_AUDIT_DNS_AID_MISSING", { url: "/" }, `No SVCB or HTTPS records at _index._agents.${host} — agents probing DNS for AI Discovery (DNS-AID) find nothing.`, `Publish a ServiceMode record with your DNS provider, e.g. \`_index._agents.${host}. 3600 IN HTTPS 1 ${host}. alpn=h2\`, and sign the zone with DNSSEC if the provider supports it.`)
|
|
1911
|
+
];
|
|
1912
|
+
}
|
|
1913
|
+
if (!result.authenticated) {
|
|
1914
|
+
return [
|
|
1915
|
+
finding("BLUME_AUDIT_DNS_AID_UNSIGNED", { url: "/" }, `The DNS-AID records at _index._agents.${host} exist but are not DNSSEC-authenticated, so validating resolvers can't prove they're genuine.`)
|
|
1916
|
+
];
|
|
1917
|
+
}
|
|
1918
|
+
return [];
|
|
1919
|
+
};
|
|
1920
|
+
var dnsAidHost = (site) => {
|
|
1921
|
+
if (!site) {
|
|
1922
|
+
return null;
|
|
1923
|
+
}
|
|
1924
|
+
let hostname;
|
|
1925
|
+
try {
|
|
1926
|
+
({ hostname } = new URL(site));
|
|
1927
|
+
} catch {
|
|
1928
|
+
return null;
|
|
1929
|
+
}
|
|
1930
|
+
if (hostname === "localhost" || hostname.endsWith(".local") || !hostname.includes(".")) {
|
|
1931
|
+
return null;
|
|
1932
|
+
}
|
|
1933
|
+
return hostname;
|
|
1934
|
+
};
|
|
1935
|
+
var dnsAidChecks = {
|
|
1936
|
+
category: "ai",
|
|
1937
|
+
async run(context) {
|
|
1938
|
+
if (!context.origin) {
|
|
1939
|
+
return [];
|
|
1940
|
+
}
|
|
1941
|
+
const host = dnsAidHost(context.project.config.deployment.site);
|
|
1942
|
+
if (!host) {
|
|
1943
|
+
return [];
|
|
1944
|
+
}
|
|
1945
|
+
const result = await lookupDnsAid(host);
|
|
1946
|
+
return result ? dnsAidFindings(host, result) : [];
|
|
1947
|
+
},
|
|
1948
|
+
tier: "network"
|
|
1949
|
+
};
|
|
1950
|
+
|
|
1834
1951
|
// src/audit/checks/duplicates.ts
|
|
1835
1952
|
var isNonCanonical = (page, deployBase) => {
|
|
1836
1953
|
if (!page.canonical) {
|
|
@@ -3000,31 +3117,6 @@ var askBackendRuntimeDep = (ask) => {
|
|
|
3000
3117
|
return ASK_PRESETS[provider]?.runtimeDep ?? OPENAI_COMPATIBLE_DEP;
|
|
3001
3118
|
};
|
|
3002
3119
|
|
|
3003
|
-
// src/core/sources/watch.ts
|
|
3004
|
-
var BLUME_IGNORE_DIRS = [
|
|
3005
|
-
".blume",
|
|
3006
|
-
".blume-verify",
|
|
3007
|
-
".cache",
|
|
3008
|
-
".git",
|
|
3009
|
-
".next",
|
|
3010
|
-
".turbo",
|
|
3011
|
-
".vercel",
|
|
3012
|
-
"dist",
|
|
3013
|
-
"node_modules"
|
|
3014
|
-
];
|
|
3015
|
-
var baselineScanIgnore = () => BLUME_IGNORE_DIRS.map((dir) => `**/${dir}/**`);
|
|
3016
|
-
var BLUME_WATCH_IGNORE_DIRS = BLUME_IGNORE_DIRS;
|
|
3017
|
-
var excludeDirSegments = (patterns) => patterns.map((pattern) => /^(?<dir>[^*/]+)\/\*\*$/u.exec(pattern)?.groups?.dir).filter((dir) => dir !== undefined);
|
|
3018
|
-
var ignoringWatchListener = (onChange, ignoreDirs = BLUME_WATCH_IGNORE_DIRS) => {
|
|
3019
|
-
const ignore = new Set(ignoreDirs);
|
|
3020
|
-
return (_event, filename) => {
|
|
3021
|
-
if (typeof filename === "string" && filename.split(/[/\\]/u).some((segment) => ignore.has(segment))) {
|
|
3022
|
-
return;
|
|
3023
|
-
}
|
|
3024
|
-
onChange();
|
|
3025
|
-
};
|
|
3026
|
-
};
|
|
3027
|
-
|
|
3028
3120
|
// src/core/trim.ts
|
|
3029
3121
|
var trimStart = (text, char) => {
|
|
3030
3122
|
let start = 0;
|
|
@@ -3042,41 +3134,6 @@ var trimEnd = (text, char) => {
|
|
|
3042
3134
|
};
|
|
3043
3135
|
var trimChar = (text, char) => trimEnd(trimStart(text, char), char);
|
|
3044
3136
|
|
|
3045
|
-
// src/deploy/redirects.ts
|
|
3046
|
-
var applyBaseToAstroRedirects = (redirects, basePath, deployBase) => {
|
|
3047
|
-
const base = normalizeBasePath(deployBase);
|
|
3048
|
-
return basePath || base ? redirects.map((redirect) => ({
|
|
3049
|
-
...redirect,
|
|
3050
|
-
from: withBasePath(basePath, redirect.from),
|
|
3051
|
-
to: withComposedBasePath(base, basePath, redirect.to)
|
|
3052
|
-
})) : redirects;
|
|
3053
|
-
};
|
|
3054
|
-
var applyBaseToPlatformRedirects = (redirects, basePath, deployBase) => {
|
|
3055
|
-
const base = normalizeBasePath(deployBase);
|
|
3056
|
-
return basePath || base ? redirects.map((redirect) => ({
|
|
3057
|
-
...redirect,
|
|
3058
|
-
from: withComposedBasePath(base, basePath, redirect.from),
|
|
3059
|
-
to: withComposedBasePath(base, basePath, redirect.to)
|
|
3060
|
-
})) : redirects;
|
|
3061
|
-
};
|
|
3062
|
-
var buildNetlifyRedirects = (redirects) => `${redirects.map((redirect) => `${redirect.from} ${redirect.to} ${redirect.status}`).join(`
|
|
3063
|
-
`)}
|
|
3064
|
-
`;
|
|
3065
|
-
var buildVercelConfig = (redirects) => `${JSON.stringify({
|
|
3066
|
-
redirects: redirects.map((redirect) => ({
|
|
3067
|
-
destination: redirect.to,
|
|
3068
|
-
source: redirect.from,
|
|
3069
|
-
statusCode: redirect.status
|
|
3070
|
-
}))
|
|
3071
|
-
}, null, 2)}
|
|
3072
|
-
`;
|
|
3073
|
-
var buildRedirectManifest = (redirects) => `${JSON.stringify(redirects.map((redirect) => ({
|
|
3074
|
-
from: redirect.from,
|
|
3075
|
-
status: redirect.status,
|
|
3076
|
-
to: redirect.to
|
|
3077
|
-
})), null, 2)}
|
|
3078
|
-
`;
|
|
3079
|
-
|
|
3080
3137
|
// src/openapi/references.ts
|
|
3081
3138
|
var NON_SLUG = /[^a-z0-9]+/gu;
|
|
3082
3139
|
var slugify = (text) => trimChar(text.toLowerCase().replace(NON_SLUG, "-"), "-");
|
|
@@ -3176,6 +3233,129 @@ var blumeReferences = (config) => {
|
|
|
3176
3233
|
};
|
|
3177
3234
|
var hasScalarReferences = (config) => resolveReferences(config).some((ref) => ref.renderer === "scalar");
|
|
3178
3235
|
|
|
3236
|
+
// src/ai/api-catalog.ts
|
|
3237
|
+
var API_CATALOG_PATH = "/.well-known/api-catalog";
|
|
3238
|
+
var API_CATALOG_TYPE = "application/linkset+json";
|
|
3239
|
+
var HTTP_URL = /^https?:\/\//u;
|
|
3240
|
+
var linksetEntries = (config) => {
|
|
3241
|
+
const site = config.deployment.site ?? null;
|
|
3242
|
+
const deployBase = normalizeBasePath(config.deployment.base);
|
|
3243
|
+
const abs = (path) => {
|
|
3244
|
+
const based = withBasePath(deployBase, path);
|
|
3245
|
+
return site ? `${trimEnd(site, "/")}${based}` : based;
|
|
3246
|
+
};
|
|
3247
|
+
const entries = [];
|
|
3248
|
+
for (const reference of resolveReferences(config)) {
|
|
3249
|
+
const docRoute = reference.renderer === "blume" ? withBasePath(reference.basePath, reference.route) : reference.route;
|
|
3250
|
+
const entry = {
|
|
3251
|
+
anchor: abs(docRoute),
|
|
3252
|
+
"service-doc": [{ href: abs(docRoute), type: "text/html" }]
|
|
3253
|
+
};
|
|
3254
|
+
if (HTTP_URL.test(reference.spec)) {
|
|
3255
|
+
entry["service-desc"] = [{ href: reference.spec }];
|
|
3256
|
+
}
|
|
3257
|
+
entries.push(entry);
|
|
3258
|
+
}
|
|
3259
|
+
if (config.ai.mcp.enabled) {
|
|
3260
|
+
entries.push({
|
|
3261
|
+
anchor: abs(config.ai.mcp.route),
|
|
3262
|
+
"service-desc": [
|
|
3263
|
+
{ href: abs("/.well-known/mcp.json"), type: "application/json" }
|
|
3264
|
+
],
|
|
3265
|
+
"service-doc": [{ href: abs("/"), type: "text/html" }]
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
return entries;
|
|
3269
|
+
};
|
|
3270
|
+
var hasApiCatalog = (config) => linksetEntries(config).length > 0;
|
|
3271
|
+
var buildApiCatalog = (config) => {
|
|
3272
|
+
const linkset = linksetEntries(config);
|
|
3273
|
+
if (linkset.length === 0) {
|
|
3274
|
+
return null;
|
|
3275
|
+
}
|
|
3276
|
+
return `${JSON.stringify({ linkset }, null, 2)}
|
|
3277
|
+
`;
|
|
3278
|
+
};
|
|
3279
|
+
|
|
3280
|
+
// src/ai/link-headers.ts
|
|
3281
|
+
var buildHomeLinkHeader = (config, routePaths) => {
|
|
3282
|
+
const deployBase = normalizeBasePath(config.deployment.base);
|
|
3283
|
+
const links = [];
|
|
3284
|
+
if (hasApiCatalog(config)) {
|
|
3285
|
+
links.push(`<${deployBase}${API_CATALOG_PATH}>; rel="api-catalog"; type="application/linkset+json"`);
|
|
3286
|
+
}
|
|
3287
|
+
if (config.seo.agentReadability) {
|
|
3288
|
+
links.push(`<${deployBase}/agent-readability.json>; rel="describedby"; type="application/json"`);
|
|
3289
|
+
}
|
|
3290
|
+
if (config.ai.llmsTxt.enabled) {
|
|
3291
|
+
links.push(`<${deployBase}/llms.txt>; rel="describedby"; type="text/plain"`);
|
|
3292
|
+
}
|
|
3293
|
+
if (routePaths.includes("/")) {
|
|
3294
|
+
links.push(`<${deployBase}/index.md>; rel="alternate"; type="text/markdown"`);
|
|
3295
|
+
}
|
|
3296
|
+
return links.length > 0 ? links.join(", ") : null;
|
|
3297
|
+
};
|
|
3298
|
+
|
|
3299
|
+
// src/core/sources/watch.ts
|
|
3300
|
+
var BLUME_IGNORE_DIRS = [
|
|
3301
|
+
".blume",
|
|
3302
|
+
".blume-verify",
|
|
3303
|
+
".cache",
|
|
3304
|
+
".git",
|
|
3305
|
+
".next",
|
|
3306
|
+
".turbo",
|
|
3307
|
+
".vercel",
|
|
3308
|
+
"dist",
|
|
3309
|
+
"node_modules"
|
|
3310
|
+
];
|
|
3311
|
+
var baselineScanIgnore = () => BLUME_IGNORE_DIRS.map((dir) => `**/${dir}/**`);
|
|
3312
|
+
var BLUME_WATCH_IGNORE_DIRS = BLUME_IGNORE_DIRS;
|
|
3313
|
+
var excludeDirSegments = (patterns) => patterns.map((pattern) => /^(?<dir>[^*/]+)\/\*\*$/u.exec(pattern)?.groups?.dir).filter((dir) => dir !== undefined);
|
|
3314
|
+
var ignoringWatchListener = (onChange, ignoreDirs = BLUME_WATCH_IGNORE_DIRS) => {
|
|
3315
|
+
const ignore = new Set(ignoreDirs);
|
|
3316
|
+
return (_event, filename) => {
|
|
3317
|
+
if (typeof filename === "string" && filename.split(/[/\\]/u).some((segment) => ignore.has(segment))) {
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
onChange();
|
|
3321
|
+
};
|
|
3322
|
+
};
|
|
3323
|
+
|
|
3324
|
+
// src/deploy/redirects.ts
|
|
3325
|
+
var applyBaseToAstroRedirects = (redirects, basePath, deployBase) => {
|
|
3326
|
+
const base = normalizeBasePath(deployBase);
|
|
3327
|
+
return basePath || base ? redirects.map((redirect) => ({
|
|
3328
|
+
...redirect,
|
|
3329
|
+
from: withBasePath(basePath, redirect.from),
|
|
3330
|
+
to: withComposedBasePath(base, basePath, redirect.to)
|
|
3331
|
+
})) : redirects;
|
|
3332
|
+
};
|
|
3333
|
+
var applyBaseToPlatformRedirects = (redirects, basePath, deployBase) => {
|
|
3334
|
+
const base = normalizeBasePath(deployBase);
|
|
3335
|
+
return basePath || base ? redirects.map((redirect) => ({
|
|
3336
|
+
...redirect,
|
|
3337
|
+
from: withComposedBasePath(base, basePath, redirect.from),
|
|
3338
|
+
to: withComposedBasePath(base, basePath, redirect.to)
|
|
3339
|
+
})) : redirects;
|
|
3340
|
+
};
|
|
3341
|
+
var buildNetlifyRedirects = (redirects) => `${redirects.map((redirect) => `${redirect.from} ${redirect.to} ${redirect.status}`).join(`
|
|
3342
|
+
`)}
|
|
3343
|
+
`;
|
|
3344
|
+
var buildVercelConfig = (redirects) => `${JSON.stringify({
|
|
3345
|
+
redirects: redirects.map((redirect) => ({
|
|
3346
|
+
destination: redirect.to,
|
|
3347
|
+
source: redirect.from,
|
|
3348
|
+
statusCode: redirect.status
|
|
3349
|
+
}))
|
|
3350
|
+
}, null, 2)}
|
|
3351
|
+
`;
|
|
3352
|
+
var buildRedirectManifest = (redirects) => `${JSON.stringify(redirects.map((redirect) => ({
|
|
3353
|
+
from: redirect.from,
|
|
3354
|
+
status: redirect.status,
|
|
3355
|
+
to: redirect.to
|
|
3356
|
+
})), null, 2)}
|
|
3357
|
+
`;
|
|
3358
|
+
|
|
3179
3359
|
// src/search/providers.ts
|
|
3180
3360
|
var SEARCH_PROVIDERS = {
|
|
3181
3361
|
algolia: {
|
|
@@ -3341,30 +3521,94 @@ var GOOGLE_FONTS = {
|
|
|
3341
3521
|
};
|
|
3342
3522
|
var FONT_SLUGS = Object.keys(GOOGLE_FONTS);
|
|
3343
3523
|
var isFontSlug = (value) => Object.hasOwn(GOOGLE_FONTS, value);
|
|
3524
|
+
var DEFAULT_REMOTE_WEIGHTS = [400, 500, 600, 700];
|
|
3525
|
+
var slugifyFontName = (name) => name.toLowerCase().replaceAll(/[^a-z0-9]+/gu, "-").replaceAll(/^-|-$/gu, "");
|
|
3344
3526
|
var fontVar = (slug) => `--blume-ff-${slug}`;
|
|
3345
3527
|
var SLOTS = ["display", "body", "mono"];
|
|
3346
|
-
var
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
const def = GOOGLE_FONTS[
|
|
3528
|
+
var slotCategory = (slot) => slot === "mono" ? "mono" : "sans";
|
|
3529
|
+
var resolveFontValue = (slot, value) => {
|
|
3530
|
+
if (typeof value === "string") {
|
|
3531
|
+
if (!isFontSlug(value)) {
|
|
3532
|
+
return null;
|
|
3533
|
+
}
|
|
3534
|
+
const def = GOOGLE_FONTS[value];
|
|
3353
3535
|
return {
|
|
3354
|
-
cssVariable: fontVar(
|
|
3536
|
+
cssVariable: fontVar(value),
|
|
3355
3537
|
fallbacks: FALLBACKS[def.category],
|
|
3538
|
+
kind: "remote",
|
|
3356
3539
|
name: def.family,
|
|
3540
|
+
provider: "google",
|
|
3357
3541
|
weights: def.weights
|
|
3358
3542
|
};
|
|
3359
|
-
}
|
|
3543
|
+
}
|
|
3544
|
+
const slug = slugifyFontName(value.name);
|
|
3545
|
+
const fallbacks = FALLBACKS[value.fallback ?? slotCategory(slot)];
|
|
3546
|
+
if ("variants" in value) {
|
|
3547
|
+
return {
|
|
3548
|
+
cssVariable: fontVar(slug),
|
|
3549
|
+
fallbacks,
|
|
3550
|
+
kind: "local",
|
|
3551
|
+
name: value.name,
|
|
3552
|
+
variants: value.variants
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
return {
|
|
3556
|
+
cssVariable: fontVar(slug),
|
|
3557
|
+
fallbacks,
|
|
3558
|
+
kind: "remote",
|
|
3559
|
+
name: value.name,
|
|
3560
|
+
provider: value.provider ?? "google",
|
|
3561
|
+
weights: value.weights ?? DEFAULT_REMOTE_WEIGHTS
|
|
3562
|
+
};
|
|
3563
|
+
};
|
|
3564
|
+
var mergeFontEntries = (current, next) => {
|
|
3565
|
+
if (current.kind === "remote" && next.kind === "remote" && current.provider === next.provider && current.name === next.name) {
|
|
3566
|
+
return {
|
|
3567
|
+
...current,
|
|
3568
|
+
weights: [...new Set([...current.weights, ...next.weights])]
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
if (current.kind === "local" && next.kind === "local" && current.name === next.name && JSON.stringify(current.variants) === JSON.stringify(next.variants)) {
|
|
3572
|
+
return current;
|
|
3573
|
+
}
|
|
3574
|
+
throw new Error(`theme.fonts: "${next.name}" conflicts with "${current.name}" — both resolve to the CSS variable "${current.cssVariable}" with different definitions. Rename one family or align their definitions.`);
|
|
3575
|
+
};
|
|
3576
|
+
var buildFontEntries = (fonts) => {
|
|
3577
|
+
if (!fonts) {
|
|
3578
|
+
return [];
|
|
3579
|
+
}
|
|
3580
|
+
const entries = new Map;
|
|
3581
|
+
for (const slot of SLOTS) {
|
|
3582
|
+
const value = fonts[slot];
|
|
3583
|
+
if (value === undefined) {
|
|
3584
|
+
continue;
|
|
3585
|
+
}
|
|
3586
|
+
const entry = resolveFontValue(slot, value);
|
|
3587
|
+
if (!entry) {
|
|
3588
|
+
continue;
|
|
3589
|
+
}
|
|
3590
|
+
const current = entries.get(entry.cssVariable);
|
|
3591
|
+
entries.set(entry.cssVariable, current ? mergeFontEntries(current, entry) : entry);
|
|
3592
|
+
}
|
|
3593
|
+
return [...entries.values()];
|
|
3594
|
+
};
|
|
3595
|
+
var slotSlug = (value) => {
|
|
3596
|
+
if (typeof value === "string") {
|
|
3597
|
+
return isFontSlug(value) ? value : null;
|
|
3598
|
+
}
|
|
3599
|
+
return slugifyFontName(value.name);
|
|
3360
3600
|
};
|
|
3361
3601
|
var buildFontsCss = (fonts) => {
|
|
3362
3602
|
if (!fonts) {
|
|
3363
3603
|
return "";
|
|
3364
3604
|
}
|
|
3365
3605
|
const lines = SLOTS.flatMap((slot) => {
|
|
3366
|
-
const
|
|
3367
|
-
|
|
3606
|
+
const value = fonts[slot];
|
|
3607
|
+
if (value === undefined) {
|
|
3608
|
+
return [];
|
|
3609
|
+
}
|
|
3610
|
+
const slug = slotSlug(value);
|
|
3611
|
+
return slug ? [` --blume-font-${slot}-src: var(${fontVar(slug)});`] : [];
|
|
3368
3612
|
});
|
|
3369
3613
|
return lines.length > 0 ? `/* Generated by Blume from theme.fonts. */
|
|
3370
3614
|
:root {
|
|
@@ -3533,6 +3777,8 @@ const blumeConfig = await loadBlumeConfig(resolve(dirname(fileURLToPath(import.m
|
|
|
3533
3777
|
userIntegrationSpread: ", ...(blumeConfig?.integrations ?? [])"
|
|
3534
3778
|
};
|
|
3535
3779
|
};
|
|
3780
|
+
var renderImageOption = (config) => config.image.domains.length > 0 || config.image.remotePatterns.length > 0 ? `
|
|
3781
|
+
image: ${JSON.stringify(config.image)},` : "";
|
|
3536
3782
|
var astroConfigTemplate = (options) => {
|
|
3537
3783
|
const { context, config, needsReact, pages, dataPath, themePath } = options;
|
|
3538
3784
|
const {
|
|
@@ -3568,6 +3814,7 @@ var astroConfigTemplate = (options) => {
|
|
|
3568
3814
|
site: ${JSON.stringify(deployment.site)},` : "";
|
|
3569
3815
|
const baseOption = deployment.base ? `
|
|
3570
3816
|
base: ${JSON.stringify(deployment.base)},` : "";
|
|
3817
|
+
const imageOption = renderImageOption(config);
|
|
3571
3818
|
const i18nOption = config.i18n ? `
|
|
3572
3819
|
i18n: ${JSON.stringify({
|
|
3573
3820
|
defaultLocale: config.i18n.defaultLocale,
|
|
@@ -3584,7 +3831,13 @@ var astroConfigTemplate = (options) => {
|
|
|
3584
3831
|
])))},` : "";
|
|
3585
3832
|
const fontEntries = buildFontEntries(config.theme.fonts);
|
|
3586
3833
|
const fontsOption = fontEntries.length ? `
|
|
3587
|
-
fonts: [${fontEntries.map((font) => `{ provider: fontProviders.
|
|
3834
|
+
fonts: [${fontEntries.map((font) => font.kind === "local" ? `{ provider: fontProviders.local(), name: ${JSON.stringify(font.name)}, cssVariable: ${JSON.stringify(font.cssVariable)}, fallbacks: ${JSON.stringify(font.fallbacks)}, options: { variants: ${JSON.stringify(font.variants.map((variant) => ({
|
|
3835
|
+
...variant.weight === undefined ? {} : { weight: variant.weight },
|
|
3836
|
+
...variant.style === undefined ? {} : { style: variant.style },
|
|
3837
|
+
src: [
|
|
3838
|
+
isAbsolute(variant.src) ? variant.src : join8(context.root, variant.src)
|
|
3839
|
+
]
|
|
3840
|
+
})))} } }` : `{ provider: fontProviders.${font.provider}(), name: ${JSON.stringify(font.name)}, cssVariable: ${JSON.stringify(font.cssVariable)}, weights: ${JSON.stringify(font.weights)}, fallbacks: ${JSON.stringify(font.fallbacks)} }`).join(", ")}],` : "";
|
|
3588
3841
|
const defineConfigImport = astroConfigImportLine({
|
|
3589
3842
|
hasFonts: fontEntries.length > 0,
|
|
3590
3843
|
hasSession: sessionOption.length > 0
|
|
@@ -3622,7 +3875,12 @@ var astroConfigTemplate = (options) => {
|
|
|
3622
3875
|
if (needsSvelte) {
|
|
3623
3876
|
integrations.push("svelte()");
|
|
3624
3877
|
}
|
|
3625
|
-
integrations.push(`blumeIntegration(${JSON.stringify({
|
|
3878
|
+
integrations.push(`blumeIntegration(${JSON.stringify({
|
|
3879
|
+
base: deployment.base,
|
|
3880
|
+
contentRoutes,
|
|
3881
|
+
homeLinkHeader: buildHomeLinkHeader(config, contentRoutes) ?? undefined,
|
|
3882
|
+
pages
|
|
3883
|
+
})})`);
|
|
3626
3884
|
const watchOption = devWatchOption(context.outDir, options.contentWatchesRuntimeDir);
|
|
3627
3885
|
const {
|
|
3628
3886
|
configSourceMarker,
|
|
@@ -3641,7 +3899,7 @@ ${userConfigSetup}export default defineConfig({
|
|
|
3641
3899
|
srcDir: ${JSON.stringify(`${context.outDir}/src`)},
|
|
3642
3900
|
outDir: ${JSON.stringify(astroOutDir(context))},
|
|
3643
3901
|
publicDir: ${JSON.stringify(`${context.root}/public`)},
|
|
3644
|
-
output: ${JSON.stringify(deployment.output)},${adapterOption}${sessionOption}${siteOption}${baseOption}${redirectsOption}${i18nOption}${fontsOption}
|
|
3902
|
+
output: ${JSON.stringify(deployment.output)},${adapterOption}${sessionOption}${siteOption}${baseOption}${imageOption}${redirectsOption}${i18nOption}${fontsOption}
|
|
3645
3903
|
integrations: [${integrations.join(", ")}${userIntegrationSpread}],
|
|
3646
3904
|
markdown: {
|
|
3647
3905
|
processor: blumeMarkdownProcessor(${JSON.stringify({
|
|
@@ -3832,17 +4090,17 @@ const ground = createAskContext(askData);
|
|
|
3832
4090
|
const onError = ` onError({ error }) {
|
|
3833
4091
|
console.error("Ask AI provider error:", error);
|
|
3834
4092
|
},`;
|
|
3835
|
-
const stream = grounded ? ` const
|
|
4093
|
+
const stream = grounded ? ` const instructions =
|
|
3836
4094
|
(await ground(messages, body.page)) ??
|
|
3837
4095
|
"You are a helpful documentation assistant. Answer using the project's documentation.";
|
|
3838
4096
|
const result = streamText({
|
|
3839
4097
|
model: ${modelExpr},
|
|
3840
|
-
|
|
4098
|
+
instructions,
|
|
3841
4099
|
messages,
|
|
3842
4100
|
${onError}
|
|
3843
4101
|
});` : ` const result = streamText({
|
|
3844
4102
|
model: ${modelExpr},
|
|
3845
|
-
|
|
4103
|
+
instructions:
|
|
3846
4104
|
"You are a helpful documentation assistant. Answer using the project's documentation.",
|
|
3847
4105
|
messages,
|
|
3848
4106
|
${onError}
|
|
@@ -4011,11 +4269,96 @@ export function getStaticPaths() {
|
|
|
4011
4269
|
export function GET({ props }: { props: { route: string } }) {
|
|
4012
4270
|
const entries = raw as Record<string, { md?: string; mdx?: string }>;
|
|
4013
4271
|
const entry = entries[props.route];
|
|
4014
|
-
|
|
4015
|
-
|
|
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
|
+
},
|
|
4016
4279
|
});
|
|
4017
4280
|
}
|
|
4018
4281
|
`;
|
|
4282
|
+
var contentAssetsEndpointTemplate = (stagedAssetsDir) => `// Generated by Blume. Do not edit.
|
|
4283
|
+
import { existsSync } from "node:fs";
|
|
4284
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
4285
|
+
import { join, relative, resolve } from "node:path";
|
|
4286
|
+
import type { APIRoute } from "astro";
|
|
4287
|
+
import assets from "../../generated/content-assets.json";
|
|
4288
|
+
|
|
4289
|
+
export const prerender = true;
|
|
4290
|
+
|
|
4291
|
+
const files = assets as Record<string, string>;
|
|
4292
|
+
const STAGED_DIR = ${JSON.stringify(stagedAssetsDir)};
|
|
4293
|
+
|
|
4294
|
+
const TYPES: Record<string, string> = {
|
|
4295
|
+
".apng": "image/apng",
|
|
4296
|
+
".avif": "image/avif",
|
|
4297
|
+
".bmp": "image/bmp",
|
|
4298
|
+
".gif": "image/gif",
|
|
4299
|
+
".ico": "image/x-icon",
|
|
4300
|
+
".jpeg": "image/jpeg",
|
|
4301
|
+
".jpg": "image/jpeg",
|
|
4302
|
+
".png": "image/png",
|
|
4303
|
+
".svg": "image/svg+xml",
|
|
4304
|
+
".tiff": "image/tiff",
|
|
4305
|
+
".webp": "image/webp",
|
|
4306
|
+
};
|
|
4307
|
+
|
|
4308
|
+
const contentType = (path: string): string =>
|
|
4309
|
+
TYPES[path.slice(path.lastIndexOf(".")).toLowerCase()] ??
|
|
4310
|
+
"application/octet-stream";
|
|
4311
|
+
|
|
4312
|
+
const stagedParams = async (): Promise<string[]> => {
|
|
4313
|
+
if (!existsSync(STAGED_DIR)) {
|
|
4314
|
+
return [];
|
|
4315
|
+
}
|
|
4316
|
+
const entries = await readdir(STAGED_DIR, {
|
|
4317
|
+
recursive: true,
|
|
4318
|
+
withFileTypes: true,
|
|
4319
|
+
});
|
|
4320
|
+
return entries
|
|
4321
|
+
.filter((entry) => entry.isFile())
|
|
4322
|
+
.map((entry) =>
|
|
4323
|
+
relative(STAGED_DIR, join(entry.parentPath, entry.name)).replaceAll(
|
|
4324
|
+
"\\\\",
|
|
4325
|
+
"/"
|
|
4326
|
+
)
|
|
4327
|
+
);
|
|
4328
|
+
};
|
|
4329
|
+
|
|
4330
|
+
export async function getStaticPaths() {
|
|
4331
|
+
const params = [
|
|
4332
|
+
...Object.keys(files).map((key) => "content/" + key),
|
|
4333
|
+
...(await stagedParams()),
|
|
4334
|
+
];
|
|
4335
|
+
return params.map((asset) => ({ params: { asset } }));
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
const resolveAsset = (asset: string): string | null => {
|
|
4339
|
+
if (asset.startsWith("content/")) {
|
|
4340
|
+
return files[asset.slice("content/".length)] ?? null;
|
|
4341
|
+
}
|
|
4342
|
+
const abs = resolve(STAGED_DIR, asset);
|
|
4343
|
+
// Traversal guard: the dev server renders on demand, so the param is
|
|
4344
|
+
// attacker-controlled there — never step outside the staged directory.
|
|
4345
|
+
if (!abs.startsWith(STAGED_DIR + "/")) {
|
|
4346
|
+
return null;
|
|
4347
|
+
}
|
|
4348
|
+
return existsSync(abs) ? abs : null;
|
|
4349
|
+
};
|
|
4350
|
+
|
|
4351
|
+
export const GET: APIRoute = async ({ params }) => {
|
|
4352
|
+
const path = resolveAsset(String(params.asset ?? ""));
|
|
4353
|
+
if (!path) {
|
|
4354
|
+
return new Response(null, { status: 404 });
|
|
4355
|
+
}
|
|
4356
|
+
const body = await readFile(path);
|
|
4357
|
+
return new Response(new Uint8Array(body), {
|
|
4358
|
+
headers: { "Content-Type": contentType(path) },
|
|
4359
|
+
});
|
|
4360
|
+
};
|
|
4361
|
+
`;
|
|
4019
4362
|
var mcpPageFile = (route) => `${trimChar(route, "/")}.ts`;
|
|
4020
4363
|
var mcpEndpointTemplate = (route) => {
|
|
4021
4364
|
const clean = trimChar(route, "/");
|
|
@@ -4062,8 +4405,9 @@ export function GET({ props }: { props: { section: string } }) {
|
|
|
4062
4405
|
});
|
|
4063
4406
|
}
|
|
4064
4407
|
`;
|
|
4065
|
-
var ogEndpointTemplate = (customRoutes = []) => `// Generated by Blume. Do not edit.
|
|
4408
|
+
var ogEndpointTemplate = (customRoutes = [], og = {}) => `// Generated by Blume. Do not edit.
|
|
4066
4409
|
import { renderOgImage } from "blume/og";
|
|
4410
|
+
import type { OgFont, OgFontFamilies } from "blume/og";
|
|
4067
4411
|
import data from "blume:data";
|
|
4068
4412
|
|
|
4069
4413
|
export const prerender = true;
|
|
@@ -4073,6 +4417,13 @@ export const prerender = true;
|
|
|
4073
4417
|
// (ts(7034)) under a strict tsconfig.
|
|
4074
4418
|
const customRoutes: { slug: string; title: string }[] = ${JSON.stringify(customRoutes)};
|
|
4075
4419
|
|
|
4420
|
+
// Card fonts, resolved at generation time (explicit seo.og.fonts, or derived
|
|
4421
|
+
// from theme.fonts). Local entries carry absolute build-machine paths, which
|
|
4422
|
+
// is why they are baked into this build-only endpoint instead of the runtime
|
|
4423
|
+
// data that pages serialize into HTML.
|
|
4424
|
+
const fonts: OgFont[] = ${JSON.stringify(og.fonts ?? [])};
|
|
4425
|
+
const families: OgFontFamilies | undefined = ${og.families ? JSON.stringify(og.families) : "undefined"};
|
|
4426
|
+
|
|
4076
4427
|
export function getStaticPaths() {
|
|
4077
4428
|
const seen = new Set<string>();
|
|
4078
4429
|
const paths: { params: { slug: string }; props: { title: string } }[] = [];
|
|
@@ -4093,32 +4444,23 @@ export function getStaticPaths() {
|
|
|
4093
4444
|
return paths;
|
|
4094
4445
|
}
|
|
4095
4446
|
|
|
4096
|
-
// Footer branding shared by every card
|
|
4097
|
-
//
|
|
4447
|
+
// Footer branding shared by every card. The repo slug reuses the header link
|
|
4448
|
+
// URL; the site text (host plus deployment base) is resolved at generate time.
|
|
4098
4449
|
const repoSlug = data.config.repoUrl
|
|
4099
4450
|
? data.config.repoUrl.split("github.com/")[1]
|
|
4100
4451
|
: undefined;
|
|
4101
|
-
const siteHost = (() => {
|
|
4102
|
-
if (!data.config.site) {
|
|
4103
|
-
return undefined;
|
|
4104
|
-
}
|
|
4105
|
-
try {
|
|
4106
|
-
return new URL(data.config.site).host;
|
|
4107
|
-
} catch {
|
|
4108
|
-
return undefined;
|
|
4109
|
-
}
|
|
4110
|
-
})();
|
|
4111
4452
|
|
|
4112
4453
|
export async function GET({ props }: { props: { title: string } }) {
|
|
4113
4454
|
const png = await renderOgImage({
|
|
4114
4455
|
accent: data.config.og.palette?.accent ?? data.config.theme.accent.light,
|
|
4115
4456
|
brand: data.config.title,
|
|
4116
|
-
description: data.config.description,
|
|
4117
|
-
|
|
4457
|
+
description: data.config.og.description,
|
|
4458
|
+
families,
|
|
4459
|
+
fonts,
|
|
4118
4460
|
logo: data.config.og.logo,
|
|
4119
4461
|
palette: data.config.og.palette,
|
|
4120
4462
|
repo: repoSlug,
|
|
4121
|
-
site:
|
|
4463
|
+
site: data.config.og.site,
|
|
4122
4464
|
title: props.title,
|
|
4123
4465
|
});
|
|
4124
4466
|
return new Response(new Uint8Array(png), {
|
|
@@ -5274,6 +5616,7 @@ var MODULES = [
|
|
|
5274
5616
|
structuredDataChecks,
|
|
5275
5617
|
urlChecks,
|
|
5276
5618
|
networkChecks,
|
|
5619
|
+
dnsAidChecks,
|
|
5277
5620
|
externalChecks
|
|
5278
5621
|
];
|
|
5279
5622
|
|
|
@@ -7436,7 +7779,7 @@ var uiStringsObject = z.object({
|
|
|
7436
7779
|
generating: z.string().default("Generating…"),
|
|
7437
7780
|
openInChat: z.string().default("Open in chat"),
|
|
7438
7781
|
scrollToTop: z.string().default("Scroll to top")
|
|
7439
|
-
}).
|
|
7782
|
+
}).prefault({}),
|
|
7440
7783
|
ask: z.object({
|
|
7441
7784
|
ai: z.string().default("AI"),
|
|
7442
7785
|
clear: z.string().default("Clear conversation"),
|
|
@@ -7450,28 +7793,28 @@ var uiStringsObject = z.object({
|
|
|
7450
7793
|
tip: z.string().default("Tip: You can open and close chat with"),
|
|
7451
7794
|
title: z.string().default("Ask AI"),
|
|
7452
7795
|
you: z.string().default("You")
|
|
7453
|
-
}).
|
|
7796
|
+
}).prefault({}),
|
|
7454
7797
|
banner: z.object({
|
|
7455
7798
|
dismiss: z.string().default("Dismiss announcement")
|
|
7456
|
-
}).
|
|
7799
|
+
}).prefault({}),
|
|
7457
7800
|
changelog: z.object({
|
|
7458
7801
|
description: z.string().default("Product updates, new features, and fixes from every release."),
|
|
7459
7802
|
showReleases: z.string().default("Show {version} releases"),
|
|
7460
7803
|
title: z.string().default("Changelog")
|
|
7461
|
-
}).
|
|
7804
|
+
}).prefault({}),
|
|
7462
7805
|
content: z.object({
|
|
7463
7806
|
diagramError: z.string().default("Could not render this diagram.")
|
|
7464
|
-
}).
|
|
7807
|
+
}).prefault({}),
|
|
7465
7808
|
feedback: z.object({
|
|
7466
7809
|
no: z.string().default("No"),
|
|
7467
7810
|
question: z.string().default("Was this page helpful?"),
|
|
7468
7811
|
thanks: z.string().default("Thanks for your feedback!"),
|
|
7469
7812
|
yes: z.string().default("Yes")
|
|
7470
|
-
}).
|
|
7813
|
+
}).prefault({}),
|
|
7471
7814
|
languageSwitcher: z.object({
|
|
7472
7815
|
label: z.string().default("Language"),
|
|
7473
7816
|
untranslated: z.string().default("Not translated")
|
|
7474
|
-
}).
|
|
7817
|
+
}).prefault({}),
|
|
7475
7818
|
nav: z.object({
|
|
7476
7819
|
back: z.string().default("Back"),
|
|
7477
7820
|
breadcrumb: z.string().default("Breadcrumb"),
|
|
@@ -7484,19 +7827,19 @@ var uiStringsObject = z.object({
|
|
|
7484
7827
|
sections: z.string().default("Sections"),
|
|
7485
7828
|
toggleNavigation: z.string().default("Toggle navigation"),
|
|
7486
7829
|
toggleTheme: z.string().default("Toggle color theme")
|
|
7487
|
-
}).
|
|
7830
|
+
}).prefault({}),
|
|
7488
7831
|
notFound: z.object({
|
|
7489
7832
|
description: z.string().default("We couldn't find the page you're looking for."),
|
|
7490
7833
|
home: z.string().default("Back to home"),
|
|
7491
7834
|
title: z.string().default("Page not found")
|
|
7492
|
-
}).
|
|
7835
|
+
}).prefault({}),
|
|
7493
7836
|
page: z.object({
|
|
7494
7837
|
lastUpdated: z.string().default("Last updated on"),
|
|
7495
7838
|
next: z.string().default("Next"),
|
|
7496
7839
|
pagination: z.string().default("Pagination"),
|
|
7497
7840
|
previous: z.string().default("Previous"),
|
|
7498
7841
|
skipToContent: z.string().default("Skip to content")
|
|
7499
|
-
}).
|
|
7842
|
+
}).prefault({}),
|
|
7500
7843
|
search: z.object({
|
|
7501
7844
|
all: z.string().default("All"),
|
|
7502
7845
|
allLanguages: z.string().default("All languages"),
|
|
@@ -7513,14 +7856,13 @@ var uiStringsObject = z.object({
|
|
|
7513
7856
|
popular: z.string().default("Popular"),
|
|
7514
7857
|
preview: z.string().default("preview"),
|
|
7515
7858
|
results: z.string().default("Results")
|
|
7516
|
-
}).
|
|
7859
|
+
}).prefault({}),
|
|
7517
7860
|
toc: z.object({
|
|
7518
7861
|
title: z.string().default("On this page")
|
|
7519
|
-
}).
|
|
7862
|
+
}).prefault({})
|
|
7520
7863
|
});
|
|
7521
|
-
var uiStringsSchema = uiStringsObject.
|
|
7522
|
-
var
|
|
7523
|
-
var EN_UI = uiStringsObject.parse(EN_UI_INPUT);
|
|
7864
|
+
var uiStringsSchema = uiStringsObject.prefault({});
|
|
7865
|
+
var EN_UI = uiStringsObject.parse({});
|
|
7524
7866
|
var uiStringsOverrideSchema = z.record(z.string(), z.record(z.string(), z.string()));
|
|
7525
7867
|
var uiLocaleOverridesSchema = z.record(z.string(), uiStringsOverrideSchema);
|
|
7526
7868
|
var mergeUI = (base, override) => {
|
|
@@ -7570,7 +7912,7 @@ var sidebarMetaSchema = z2.strictObject({
|
|
|
7570
7912
|
});
|
|
7571
7913
|
var xHandleSchema = z2.string().transform(normalizeXHandle).optional();
|
|
7572
7914
|
var seoMetaSchema = z2.strictObject({
|
|
7573
|
-
canonical: z2.
|
|
7915
|
+
canonical: z2.url().optional(),
|
|
7574
7916
|
description: z2.string().optional(),
|
|
7575
7917
|
image: z2.string().optional(),
|
|
7576
7918
|
noindex: z2.boolean().default(false),
|
|
@@ -7600,7 +7942,7 @@ var authorSchema = z2.union([
|
|
|
7600
7942
|
}).catchall(z2.unknown())
|
|
7601
7943
|
]);
|
|
7602
7944
|
var pageMetaBaseSchema = z2.strictObject({
|
|
7603
|
-
ai: aiMetaSchema.
|
|
7945
|
+
ai: aiMetaSchema.prefault({}),
|
|
7604
7946
|
authors: z2.union([authorSchema, z2.array(authorSchema)]).optional(),
|
|
7605
7947
|
changelog: changelogMetaSchema.optional(),
|
|
7606
7948
|
date: dateSchema.optional(),
|
|
@@ -7611,9 +7953,9 @@ var pageMetaBaseSchema = z2.strictObject({
|
|
|
7611
7953
|
icon: iconName.optional(),
|
|
7612
7954
|
lastModified: dateSchema.optional(),
|
|
7613
7955
|
noindex: z2.boolean().default(false),
|
|
7614
|
-
search: searchMetaSchema.
|
|
7615
|
-
seo: seoMetaSchema.
|
|
7616
|
-
sidebar: sidebarMetaSchema.
|
|
7956
|
+
search: searchMetaSchema.prefault({}),
|
|
7957
|
+
seo: seoMetaSchema.prefault({}),
|
|
7958
|
+
sidebar: sidebarMetaSchema.prefault({}),
|
|
7617
7959
|
slug: z2.string().optional(),
|
|
7618
7960
|
title: z2.string().optional(),
|
|
7619
7961
|
type: z2.string().optional()
|
|
@@ -7773,11 +8115,31 @@ var sidebarItemSchema = z2.lazy(() => z2.union([
|
|
|
7773
8115
|
root: z2.string().optional()
|
|
7774
8116
|
})
|
|
7775
8117
|
]));
|
|
7776
|
-
var
|
|
7777
|
-
|
|
8118
|
+
var fontFallbackSchema = z2.enum(["sans", "serif", "mono"]);
|
|
8119
|
+
var remoteFontSchema = z2.strictObject({
|
|
8120
|
+
fallback: fontFallbackSchema.optional(),
|
|
8121
|
+
name: z2.string().min(1),
|
|
8122
|
+
provider: z2.enum(["google", "fontsource", "bunny", "fontshare"]).default("google"),
|
|
8123
|
+
weights: z2.array(z2.union([z2.number().int().positive(), z2.string().regex(/^\d+\.\.\d+$/u)])).nonempty().optional()
|
|
8124
|
+
});
|
|
8125
|
+
var localFontVariantSchema = z2.strictObject({
|
|
8126
|
+
src: z2.string().min(1),
|
|
8127
|
+
style: z2.enum(["normal", "italic", "oblique"]).optional(),
|
|
8128
|
+
weight: z2.union([
|
|
8129
|
+
z2.number().int().positive(),
|
|
8130
|
+
z2.string().regex(/^\d+(?:\.\.\d+)?$/u)
|
|
8131
|
+
]).optional()
|
|
8132
|
+
});
|
|
8133
|
+
var localFontSchema = z2.strictObject({
|
|
8134
|
+
fallback: fontFallbackSchema.optional(),
|
|
8135
|
+
name: z2.string().min(1),
|
|
8136
|
+
variants: z2.array(localFontVariantSchema).nonempty()
|
|
8137
|
+
});
|
|
8138
|
+
var fontValueSchema = z2.union([z2.string(), remoteFontSchema, localFontSchema]).superRefine((value, ctx) => {
|
|
8139
|
+
if (typeof value === "string" && !isFontSlug(value)) {
|
|
7778
8140
|
ctx.addIssue({
|
|
7779
8141
|
code: z2.ZodIssueCode.custom,
|
|
7780
|
-
message: `Unknown font "${value}". Supported fonts: ${FONT_SLUGS.join(", ")}.`
|
|
8142
|
+
message: `Unknown font "${value}". Supported fonts: ${FONT_SLUGS.join(", ")}. For any other family, use the object form: { name: "..." } (remote provider) or { name: "...", variants: [...] } (local files).`
|
|
7781
8143
|
});
|
|
7782
8144
|
}
|
|
7783
8145
|
});
|
|
@@ -7797,10 +8159,10 @@ var themeConfigSchema = z2.strictObject({
|
|
|
7797
8159
|
background: perModeValueSchema,
|
|
7798
8160
|
backgroundImage: perModeValueSchema,
|
|
7799
8161
|
fonts: z2.strictObject({
|
|
7800
|
-
body:
|
|
7801
|
-
display:
|
|
7802
|
-
mono:
|
|
7803
|
-
}).
|
|
8162
|
+
body: fontValueSchema.default("inter"),
|
|
8163
|
+
display: fontValueSchema.default("inter-tight"),
|
|
8164
|
+
mono: fontValueSchema.default("ibm-plex-mono")
|
|
8165
|
+
}).prefault({}),
|
|
7804
8166
|
layout: z2.enum(["sidebar"]).default("sidebar"),
|
|
7805
8167
|
mode: z2.enum(["system", "light", "dark"]).default("system"),
|
|
7806
8168
|
radius: z2.enum(["none", "sm", "md", "lg"]).default("md")
|
|
@@ -7850,7 +8212,7 @@ var searchConfigSchema = z2.strictObject({
|
|
|
7850
8212
|
algolia: algoliaSearchSchema.optional(),
|
|
7851
8213
|
indexing: z2.strictObject({
|
|
7852
8214
|
includeHiddenPages: z2.boolean().default(false)
|
|
7853
|
-
}).
|
|
8215
|
+
}).prefault({}),
|
|
7854
8216
|
mixedbread: mixedbreadSearchSchema.optional(),
|
|
7855
8217
|
oramaCloud: oramaCloudSearchSchema.optional(),
|
|
7856
8218
|
popular: z2.array(searchPopularLinkSchema).default([]),
|
|
@@ -7873,6 +8235,22 @@ var askAiProviders = [
|
|
|
7873
8235
|
"inkeep",
|
|
7874
8236
|
"openai-compatible"
|
|
7875
8237
|
];
|
|
8238
|
+
var PRIVATE_JWK_PARAMS = ["d", "p", "q", "dp", "dq", "qi", "oth", "k"];
|
|
8239
|
+
var publicJwkSchema = z2.record(z2.string(), z2.unknown()).superRefine((jwk, ctx) => {
|
|
8240
|
+
if (typeof jwk.kty !== "string" || jwk.kty.length === 0) {
|
|
8241
|
+
ctx.addIssue({
|
|
8242
|
+
code: z2.ZodIssueCode.custom,
|
|
8243
|
+
message: 'A JWK must declare its key type ("kty").'
|
|
8244
|
+
});
|
|
8245
|
+
}
|
|
8246
|
+
const leaked = PRIVATE_JWK_PARAMS.filter((param) => (param in jwk));
|
|
8247
|
+
if (leaked.length > 0) {
|
|
8248
|
+
ctx.addIssue({
|
|
8249
|
+
code: z2.ZodIssueCode.custom,
|
|
8250
|
+
message: `The JWK contains private key material ("${leaked.join('", "')}") — the signatures directory is public, so list only public keys and keep the private key where the signing agent runs.`
|
|
8251
|
+
});
|
|
8252
|
+
}
|
|
8253
|
+
});
|
|
7876
8254
|
var mcpConfigSchema = z2.strictObject({
|
|
7877
8255
|
enabled: z2.boolean().default(false),
|
|
7878
8256
|
instructions: z2.string().optional(),
|
|
@@ -7895,7 +8273,7 @@ var askEndpointSchema = z2.string().trim().min(1).refine((value) => {
|
|
|
7895
8273
|
var aiConfigSchema = z2.strictObject({
|
|
7896
8274
|
ask: z2.strictObject({
|
|
7897
8275
|
apiKeyEnv: z2.string().optional(),
|
|
7898
|
-
baseUrl: z2.
|
|
8276
|
+
baseUrl: z2.url().optional(),
|
|
7899
8277
|
enabled: z2.boolean().default(false),
|
|
7900
8278
|
endpoint: askEndpointSchema.optional(),
|
|
7901
8279
|
model: z2.string().default("openai/gpt-5.5"),
|
|
@@ -7923,7 +8301,12 @@ var aiConfigSchema = z2.strictObject({
|
|
|
7923
8301
|
markdownComponents: z2.record(z2.string(), z2.custom((value) => typeof value === "function", {
|
|
7924
8302
|
message: "Expected a serializer function."
|
|
7925
8303
|
})).default({}),
|
|
7926
|
-
mcp: mcpConfigSchema.
|
|
8304
|
+
mcp: mcpConfigSchema.prefault({}),
|
|
8305
|
+
skills: z2.string().min(1).optional(),
|
|
8306
|
+
webBotAuth: z2.strictObject({
|
|
8307
|
+
keys: z2.array(publicJwkSchema).default([])
|
|
8308
|
+
}).prefault({}),
|
|
8309
|
+
webmcp: z2.boolean().default(true)
|
|
7927
8310
|
});
|
|
7928
8311
|
var featuredLinkSchema = z2.strictObject({
|
|
7929
8312
|
href: z2.string(),
|
|
@@ -7940,7 +8323,7 @@ var navigationConfigSchema = z2.strictObject({
|
|
|
7940
8323
|
display: sidebarDisplaySchema.default("flat"),
|
|
7941
8324
|
items: z2.array(sidebarItemSchema).optional()
|
|
7942
8325
|
})
|
|
7943
|
-
]).
|
|
8326
|
+
]).prefault({}).transform((value) => Array.isArray(value) ? { display: "flat", items: value } : value),
|
|
7944
8327
|
tabs: z2.array(navTabSchema).default([])
|
|
7945
8328
|
});
|
|
7946
8329
|
var exportConfigSchema = z2.union([
|
|
@@ -7999,13 +8382,23 @@ var deploymentConfigSchema = z2.strictObject({
|
|
|
7999
8382
|
adapter: z2.enum(["vercel", "node", "netlify", "cloudflare"]).nullable().default(null),
|
|
8000
8383
|
base: z2.string().optional(),
|
|
8001
8384
|
output: z2.enum(["static", "server"]).default("static"),
|
|
8002
|
-
site: z2.
|
|
8385
|
+
site: z2.url().optional()
|
|
8003
8386
|
});
|
|
8004
8387
|
var redirectSchema = z2.strictObject({
|
|
8005
8388
|
from: z2.string(),
|
|
8006
8389
|
status: z2.union([z2.literal(301), z2.literal(302), z2.literal(307), z2.literal(308)]).default(301),
|
|
8007
8390
|
to: z2.string()
|
|
8008
8391
|
});
|
|
8392
|
+
var imageRemotePatternSchema = z2.strictObject({
|
|
8393
|
+
hostname: z2.string().optional(),
|
|
8394
|
+
pathname: z2.string().optional(),
|
|
8395
|
+
port: z2.string().optional(),
|
|
8396
|
+
protocol: z2.string().optional()
|
|
8397
|
+
});
|
|
8398
|
+
var imageConfigSchema = z2.strictObject({
|
|
8399
|
+
domains: z2.array(z2.string()).default([]),
|
|
8400
|
+
remotePatterns: z2.array(imageRemotePatternSchema).default([])
|
|
8401
|
+
});
|
|
8009
8402
|
var xConfigSchema = z2.strictObject({
|
|
8010
8403
|
creator: xHandleSchema,
|
|
8011
8404
|
handle: xHandleSchema
|
|
@@ -8030,13 +8423,21 @@ var ogFontSchema = z2.union([
|
|
|
8030
8423
|
name: z2.string(),
|
|
8031
8424
|
style: z2.union([ogFontStyleSchema, z2.array(ogFontStyleSchema)]).optional(),
|
|
8032
8425
|
weight: ogFontWeightSchema.optional()
|
|
8426
|
+
}),
|
|
8427
|
+
z2.strictObject({
|
|
8428
|
+
name: z2.string(),
|
|
8429
|
+
src: z2.string().min(1),
|
|
8430
|
+
style: ogFontStyleSchema.optional(),
|
|
8431
|
+
weight: z2.number().int().positive().optional()
|
|
8033
8432
|
})
|
|
8034
8433
|
]);
|
|
8035
8434
|
var ogConfigSchema = z2.strictObject({
|
|
8435
|
+
description: z2.union([z2.string(), z2.literal(false)]).optional(),
|
|
8036
8436
|
enabled: z2.boolean().optional(),
|
|
8037
8437
|
fonts: z2.array(ogFontSchema).optional(),
|
|
8038
|
-
logo: z2.string().optional(),
|
|
8438
|
+
logo: z2.union([z2.string(), z2.literal(false)]).optional(),
|
|
8039
8439
|
palette: ogPaletteSchema.optional(),
|
|
8440
|
+
site: z2.union([z2.string(), z2.literal(false)]).optional(),
|
|
8040
8441
|
titles: z2.record(z2.string(), z2.string()).optional()
|
|
8041
8442
|
});
|
|
8042
8443
|
var rssConfigSchema = z2.strictObject({
|
|
@@ -8060,10 +8461,10 @@ var contentSignalsSchema = z2.union([z2.boolean(), contentSignalsObjectSchema]).
|
|
|
8060
8461
|
});
|
|
8061
8462
|
var seoConfigSchema = z2.strictObject({
|
|
8062
8463
|
agentReadability: z2.boolean().default(true),
|
|
8063
|
-
contentSignals: contentSignalsSchema.
|
|
8464
|
+
contentSignals: contentSignalsSchema.prefault(true),
|
|
8064
8465
|
og: ogConfigSchema.default({}),
|
|
8065
8466
|
robots: z2.boolean().default(true),
|
|
8066
|
-
rss: rssConfigSchema.
|
|
8467
|
+
rss: rssConfigSchema.prefault({}),
|
|
8067
8468
|
sitemap: z2.boolean().default(true),
|
|
8068
8469
|
structuredData: z2.boolean().default(true),
|
|
8069
8470
|
x: xConfigSchema.default({})
|
|
@@ -8093,7 +8494,7 @@ var codeBlockThemeSchema = z2.strictObject({
|
|
|
8093
8494
|
light: codeThemeSchema.default("github-light")
|
|
8094
8495
|
});
|
|
8095
8496
|
var codeBlocksConfigSchema = z2.strictObject({
|
|
8096
|
-
theme: codeBlockThemeSchema.
|
|
8497
|
+
theme: codeBlockThemeSchema.prefault({})
|
|
8097
8498
|
});
|
|
8098
8499
|
var examplesConfigSchema = z2.union([
|
|
8099
8500
|
z2.string(),
|
|
@@ -8124,8 +8525,8 @@ var codeConfigSchema = z2.strictObject({
|
|
|
8124
8525
|
wrap: z2.boolean().default(false)
|
|
8125
8526
|
});
|
|
8126
8527
|
var markdownConfigSchema = z2.strictObject({
|
|
8127
|
-
code: codeConfigSchema.
|
|
8128
|
-
codeBlocks: codeBlocksConfigSchema.
|
|
8528
|
+
code: codeConfigSchema.prefault({}),
|
|
8529
|
+
codeBlocks: codeBlocksConfigSchema.prefault({}),
|
|
8129
8530
|
headingAnchors: z2.boolean().default(true),
|
|
8130
8531
|
imageZoom: z2.boolean().default(true)
|
|
8131
8532
|
});
|
|
@@ -8194,32 +8595,33 @@ var tocConfigSchema = z2.union([
|
|
|
8194
8595
|
message: "toc.minHeadingLevel must be less than or equal to toc.maxHeadingLevel."
|
|
8195
8596
|
});
|
|
8196
8597
|
var blumeConfigSchema = z2.strictObject({
|
|
8197
|
-
ai: aiConfigSchema.
|
|
8598
|
+
ai: aiConfigSchema.prefault({}),
|
|
8198
8599
|
analytics: analyticsConfigSchema.optional(),
|
|
8199
|
-
asyncapi: asyncapiConfigSchema.
|
|
8600
|
+
asyncapi: asyncapiConfigSchema.prefault({}),
|
|
8200
8601
|
banner: bannerConfigSchema.optional(),
|
|
8201
8602
|
basePath: z2.string().optional().transform((value) => normalizeBasePath(value)),
|
|
8202
|
-
content: contentConfigSchema.
|
|
8603
|
+
content: contentConfigSchema.prefault({}),
|
|
8203
8604
|
dateFormat: dateFormatConfigSchema.default({ dateStyle: "long" }),
|
|
8204
|
-
deployment: deploymentConfigSchema.
|
|
8605
|
+
deployment: deploymentConfigSchema.prefault({}),
|
|
8205
8606
|
description: z2.string().optional(),
|
|
8206
|
-
examples: examplesConfigSchema.
|
|
8207
|
-
export: exportConfigSchema.
|
|
8607
|
+
examples: examplesConfigSchema.prefault("examples"),
|
|
8608
|
+
export: exportConfigSchema.prefault(false),
|
|
8208
8609
|
feedback: z2.boolean().default(true),
|
|
8209
|
-
frontmatter: frontmatterConfigSchema.
|
|
8610
|
+
frontmatter: frontmatterConfigSchema.prefault({}),
|
|
8210
8611
|
github: githubConfigSchema.optional(),
|
|
8211
8612
|
i18n: i18nConfigSchema.optional(),
|
|
8613
|
+
image: imageConfigSchema.prefault({}),
|
|
8212
8614
|
integrations: z2.array(z2.custom()).default([]),
|
|
8213
8615
|
lastModified: lastModifiedConfigSchema.default(false),
|
|
8214
8616
|
logo: logoConfigSchema.optional(),
|
|
8215
|
-
markdown: markdownConfigSchema.
|
|
8216
|
-
navigation: navigationConfigSchema.
|
|
8217
|
-
openapi: openapiConfigSchema.
|
|
8218
|
-
react: reactConfigSchema.
|
|
8617
|
+
markdown: markdownConfigSchema.prefault({}),
|
|
8618
|
+
navigation: navigationConfigSchema.prefault({}),
|
|
8619
|
+
openapi: openapiConfigSchema.prefault({}),
|
|
8620
|
+
react: reactConfigSchema.prefault({}),
|
|
8219
8621
|
redirects: z2.array(redirectSchema).default([]),
|
|
8220
|
-
search: searchConfigSchema.
|
|
8221
|
-
seo: seoConfigSchema.
|
|
8222
|
-
theme: themeConfigSchema.
|
|
8622
|
+
search: searchConfigSchema.prefault({}),
|
|
8623
|
+
seo: seoConfigSchema.prefault({}),
|
|
8624
|
+
theme: themeConfigSchema.prefault({}),
|
|
8223
8625
|
title: z2.string().default("Documentation"),
|
|
8224
8626
|
toc: tocConfigSchema
|
|
8225
8627
|
});
|
|
@@ -8241,6 +8643,7 @@ var loadConfig = async (root, options = {}) => {
|
|
|
8241
8643
|
});
|
|
8242
8644
|
}
|
|
8243
8645
|
}
|
|
8646
|
+
const themeFontsConfigured = Boolean(raw && typeof raw === "object" && raw.theme?.fonts !== undefined);
|
|
8244
8647
|
const parsed = blumeConfigSchema.safeParse(raw ?? {});
|
|
8245
8648
|
if (!parsed.success) {
|
|
8246
8649
|
const source = configFile && existsSync6(configFile) ? readFileSync3(configFile, "utf-8") : undefined;
|
|
@@ -8275,7 +8678,8 @@ ${moreIssues}`
|
|
|
8275
8678
|
seo: { ...config.seo, og: { ...config.seo.og, enabled: ogEnabled } }
|
|
8276
8679
|
},
|
|
8277
8680
|
configFile,
|
|
8278
|
-
diagnostics: []
|
|
8681
|
+
diagnostics: [],
|
|
8682
|
+
themeFontsConfigured
|
|
8279
8683
|
};
|
|
8280
8684
|
};
|
|
8281
8685
|
|
|
@@ -11396,7 +11800,8 @@ var scanProject = async (root, options = {}) => {
|
|
|
11396
11800
|
graph,
|
|
11397
11801
|
manifest,
|
|
11398
11802
|
mode,
|
|
11399
|
-
sources
|
|
11803
|
+
sources,
|
|
11804
|
+
themeFontsConfigured: configResult.themeFontsConfigured
|
|
11400
11805
|
};
|
|
11401
11806
|
};
|
|
11402
11807
|
|
|
@@ -11580,11 +11985,11 @@ var auditCommand = defineCommand2({
|
|
|
11580
11985
|
});
|
|
11581
11986
|
|
|
11582
11987
|
// src/cli/commands/build.ts
|
|
11583
|
-
import { existsSync as
|
|
11584
|
-
import { readdir, stat as
|
|
11988
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
11989
|
+
import { mkdir as mkdir7, readdir as readdir2, readFile as readFile17, stat as stat3, writeFile as writeFile8 } from "node:fs/promises";
|
|
11585
11990
|
import { build } from "astro";
|
|
11586
11991
|
import { defineCommand as defineCommand3 } from "citty";
|
|
11587
|
-
import { join as
|
|
11992
|
+
import { dirname as dirname12, join as join30, resolve as resolve10 } from "pathe";
|
|
11588
11993
|
|
|
11589
11994
|
// src/deploy/xml.ts
|
|
11590
11995
|
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -11688,6 +12093,19 @@ var askApiUrl = (endpoint, site, abs) => {
|
|
|
11688
12093
|
}
|
|
11689
12094
|
return site && endpoint.startsWith("/") ? `${site.replace(/\/+$/u, "")}${endpoint}` : endpoint;
|
|
11690
12095
|
};
|
|
12096
|
+
var wellKnownArtifacts = (config, abs) => {
|
|
12097
|
+
const artifacts = {};
|
|
12098
|
+
if (config.ai.webBotAuth.keys.length > 0) {
|
|
12099
|
+
artifacts.httpMessageSignaturesDirectory = abs("/.well-known/http-message-signatures-directory");
|
|
12100
|
+
}
|
|
12101
|
+
if (hasApiCatalog(config)) {
|
|
12102
|
+
artifacts.apiCatalog = abs("/.well-known/api-catalog");
|
|
12103
|
+
}
|
|
12104
|
+
if (config.ai.skills) {
|
|
12105
|
+
artifacts.agentSkills = abs("/.well-known/agent-skills/index.json");
|
|
12106
|
+
}
|
|
12107
|
+
return artifacts;
|
|
12108
|
+
};
|
|
11691
12109
|
var buildAgentReadability = (project) => {
|
|
11692
12110
|
const { config } = project;
|
|
11693
12111
|
if (!config.seo.agentReadability) {
|
|
@@ -11699,9 +12117,10 @@ var buildAgentReadability = (project) => {
|
|
|
11699
12117
|
const based = withBasePath(deployBase, path);
|
|
11700
12118
|
return site ? `${site.replace(/\/+$/u, "")}${based}` : based;
|
|
11701
12119
|
};
|
|
12120
|
+
const negotiates = config.deployment.output === "server" && config.deployment.adapter === "vercel";
|
|
11702
12121
|
const artifacts = {
|
|
11703
12122
|
markdown: {
|
|
11704
|
-
contentNegotiation: "text/markdown",
|
|
12123
|
+
...negotiates ? { contentNegotiation: "text/markdown" } : {},
|
|
11705
12124
|
pattern: abs("/{route}.md")
|
|
11706
12125
|
}
|
|
11707
12126
|
};
|
|
@@ -11718,6 +12137,7 @@ var buildAgentReadability = (project) => {
|
|
|
11718
12137
|
if (config.ai.ask?.enabled) {
|
|
11719
12138
|
artifacts.askApi = askApiUrl(config.ai.ask.endpoint, site, abs);
|
|
11720
12139
|
}
|
|
12140
|
+
Object.assign(artifacts, wellKnownArtifacts(config, abs));
|
|
11721
12141
|
if (site && config.seo.sitemap) {
|
|
11722
12142
|
artifacts.sitemap = abs("/sitemap.xml");
|
|
11723
12143
|
}
|
|
@@ -11743,8 +12163,113 @@ var buildAgentReadability = (project) => {
|
|
|
11743
12163
|
return manifest;
|
|
11744
12164
|
};
|
|
11745
12165
|
|
|
11746
|
-
// src/core/
|
|
12166
|
+
// src/core/content-assets.ts
|
|
12167
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
11747
12168
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
12169
|
+
import { dirname as dirname7, extname as extname5, relative as relative11, resolve as resolve5 } from "pathe";
|
|
12170
|
+
var CONTENT_ASSETS_PREFIX = "/blume-assets/content";
|
|
12171
|
+
var IMAGE_EXTENSIONS = new Set([
|
|
12172
|
+
".apng",
|
|
12173
|
+
".avif",
|
|
12174
|
+
".bmp",
|
|
12175
|
+
".gif",
|
|
12176
|
+
".ico",
|
|
12177
|
+
".jpeg",
|
|
12178
|
+
".jpg",
|
|
12179
|
+
".png",
|
|
12180
|
+
".svg",
|
|
12181
|
+
".tiff",
|
|
12182
|
+
".webp"
|
|
12183
|
+
]);
|
|
12184
|
+
var isRelativeTarget = (target) => !(target.startsWith("/") || target.startsWith("#")) && !URL.canParse(target);
|
|
12185
|
+
var contentAssetParam = (projectRoot, absPath) => {
|
|
12186
|
+
const rel = relative11(projectRoot, absPath);
|
|
12187
|
+
if (rel.startsWith("..")) {
|
|
12188
|
+
return `_/${hashText(absPath)}${extname5(absPath)}`;
|
|
12189
|
+
}
|
|
12190
|
+
return rel;
|
|
12191
|
+
};
|
|
12192
|
+
var resolveRelativeImage = (sourceDir, target) => {
|
|
12193
|
+
if (!isRelativeTarget(target)) {
|
|
12194
|
+
return null;
|
|
12195
|
+
}
|
|
12196
|
+
let decoded = target;
|
|
12197
|
+
try {
|
|
12198
|
+
decoded = decodeURI(target);
|
|
12199
|
+
} catch {}
|
|
12200
|
+
if (!IMAGE_EXTENSIONS.has(extname5(decoded).toLowerCase())) {
|
|
12201
|
+
return null;
|
|
12202
|
+
}
|
|
12203
|
+
const abs = resolve5(sourceDir, decoded);
|
|
12204
|
+
return existsSync9(abs) ? abs : null;
|
|
12205
|
+
};
|
|
12206
|
+
var encodedParam = (param) => param.split("/").map(encodeURIComponent).join("/");
|
|
12207
|
+
var rewriteLine = (line, toUrl2) => {
|
|
12208
|
+
const masked = line.replaceAll(INLINE_CODE, (span) => " ".repeat(span.length));
|
|
12209
|
+
let out = "";
|
|
12210
|
+
let cursor = 0;
|
|
12211
|
+
for (const match of masked.matchAll(MD_IMAGE)) {
|
|
12212
|
+
const target = match.groups?.target ?? "";
|
|
12213
|
+
const url = toUrl2(target);
|
|
12214
|
+
if (url === null) {
|
|
12215
|
+
continue;
|
|
12216
|
+
}
|
|
12217
|
+
const offset = (match.index ?? 0) + targetOffsetIn(match[0], target, match.groups?.title);
|
|
12218
|
+
out += line.slice(cursor, offset) + url;
|
|
12219
|
+
cursor = offset + target.length;
|
|
12220
|
+
}
|
|
12221
|
+
return out + line.slice(cursor);
|
|
12222
|
+
};
|
|
12223
|
+
var rewriteRelativeImages = (options) => {
|
|
12224
|
+
const { source, sourcePath, projectRoot, deployBase, register } = options;
|
|
12225
|
+
const sourceDir = dirname7(sourcePath);
|
|
12226
|
+
const prefix = `${normalizeBasePath(deployBase)}${CONTENT_ASSETS_PREFIX}`;
|
|
12227
|
+
const toUrl2 = (target) => {
|
|
12228
|
+
const abs = resolveRelativeImage(sourceDir, target);
|
|
12229
|
+
if (abs === null) {
|
|
12230
|
+
return null;
|
|
12231
|
+
}
|
|
12232
|
+
const param = contentAssetParam(projectRoot, abs);
|
|
12233
|
+
register?.(param, abs);
|
|
12234
|
+
return `${prefix}/${encodedParam(param)}`;
|
|
12235
|
+
};
|
|
12236
|
+
let fence = null;
|
|
12237
|
+
const lines = source.split(`
|
|
12238
|
+
`).map((line) => {
|
|
12239
|
+
const next = nextFenceState(line, fence);
|
|
12240
|
+
const inFence = fence !== null || next !== null;
|
|
12241
|
+
fence = next;
|
|
12242
|
+
return inFence ? line : rewriteLine(line, toUrl2);
|
|
12243
|
+
});
|
|
12244
|
+
return lines.join(`
|
|
12245
|
+
`);
|
|
12246
|
+
};
|
|
12247
|
+
var collectContentAssets = async (project) => {
|
|
12248
|
+
const files = {};
|
|
12249
|
+
await Promise.all(project.manifest.routes.map(async (route) => {
|
|
12250
|
+
if (!route.sourcePath) {
|
|
12251
|
+
return;
|
|
12252
|
+
}
|
|
12253
|
+
let source;
|
|
12254
|
+
try {
|
|
12255
|
+
source = await readFile8(route.sourcePath, "utf-8");
|
|
12256
|
+
} catch {
|
|
12257
|
+
return;
|
|
12258
|
+
}
|
|
12259
|
+
rewriteRelativeImages({
|
|
12260
|
+
projectRoot: project.context.root,
|
|
12261
|
+
register: (param, abs) => {
|
|
12262
|
+
files[param] = abs;
|
|
12263
|
+
},
|
|
12264
|
+
source,
|
|
12265
|
+
sourcePath: route.sourcePath
|
|
12266
|
+
});
|
|
12267
|
+
}));
|
|
12268
|
+
return files;
|
|
12269
|
+
};
|
|
12270
|
+
|
|
12271
|
+
// src/core/sources/read.ts
|
|
12272
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
11748
12273
|
var readEntryText = async (ctx, page) => {
|
|
11749
12274
|
if (page.body) {
|
|
11750
12275
|
return page.body.text;
|
|
@@ -11755,7 +12280,7 @@ var readEntryText = async (ctx, page) => {
|
|
|
11755
12280
|
return await source.read(page.source.ref);
|
|
11756
12281
|
}
|
|
11757
12282
|
if (page.sourcePath) {
|
|
11758
|
-
return await
|
|
12283
|
+
return await readFile9(page.sourcePath, "utf-8");
|
|
11759
12284
|
}
|
|
11760
12285
|
return "";
|
|
11761
12286
|
};
|
|
@@ -12073,7 +12598,7 @@ var indexedNavigations = (project) => {
|
|
|
12073
12598
|
}
|
|
12074
12599
|
return [{ nav: project.graph.navigation }];
|
|
12075
12600
|
};
|
|
12076
|
-
var
|
|
12601
|
+
var buildLlmsIndex = (project) => {
|
|
12077
12602
|
const { config } = project;
|
|
12078
12603
|
const { site } = config.deployment;
|
|
12079
12604
|
const base = normalizeBasePath(config.deployment.base);
|
|
@@ -12126,67 +12651,304 @@ var buildIndex = (project) => {
|
|
|
12126
12651
|
}
|
|
12127
12652
|
continue;
|
|
12128
12653
|
}
|
|
12129
|
-
blocks.push(...renderNav(nav, 2));
|
|
12130
|
-
}
|
|
12131
|
-
const leftover = eligible.filter((page) => !seen.has(page.route)).toSorted((a, b) => a.route.localeCompare(b.route));
|
|
12132
|
-
if (leftover.length > 0) {
|
|
12133
|
-
blocks.push(blocks.length > 0 ? "## Other" : "## Docs", leftover.map(line).join(`
|
|
12134
|
-
`));
|
|
12135
|
-
}
|
|
12136
|
-
const feeds = buildRssFeeds(project);
|
|
12137
|
-
if (feeds.length > 0) {
|
|
12138
|
-
blocks.push("## RSS Feeds", feeds.map((feed) => `- [${feed.title}](${pageUrl(feed.path, site, base)})`).join(`
|
|
12139
|
-
`));
|
|
12654
|
+
blocks.push(...renderNav(nav, 2));
|
|
12655
|
+
}
|
|
12656
|
+
const leftover = eligible.filter((page) => !seen.has(page.route)).toSorted((a, b) => a.route.localeCompare(b.route));
|
|
12657
|
+
if (leftover.length > 0) {
|
|
12658
|
+
blocks.push(blocks.length > 0 ? "## Other" : "## Docs", leftover.map(line).join(`
|
|
12659
|
+
`));
|
|
12660
|
+
}
|
|
12661
|
+
const feeds = buildRssFeeds(project);
|
|
12662
|
+
if (feeds.length > 0) {
|
|
12663
|
+
blocks.push("## RSS Feeds", feeds.map((feed) => `- [${feed.title}](${pageUrl(feed.path, site, base)})`).join(`
|
|
12664
|
+
`));
|
|
12665
|
+
}
|
|
12666
|
+
const header = config.description ? `# ${config.title}
|
|
12667
|
+
|
|
12668
|
+
> ${config.description}` : `# ${config.title}`;
|
|
12669
|
+
return `${[header, ...blocks].join(`
|
|
12670
|
+
|
|
12671
|
+
`)}
|
|
12672
|
+
`;
|
|
12673
|
+
};
|
|
12674
|
+
var buildFull = async (project) => {
|
|
12675
|
+
const { config } = project;
|
|
12676
|
+
const pages = eligiblePages(project).toSorted((a, b) => a.route.localeCompare(b.route));
|
|
12677
|
+
const components = {
|
|
12678
|
+
...exampleComponentSerializers(project.examples ?? {}),
|
|
12679
|
+
...config.ai.markdownComponents
|
|
12680
|
+
};
|
|
12681
|
+
const sections = await Promise.all(pages.map(async (page) => {
|
|
12682
|
+
let raw = await readEntryText(project, page);
|
|
12683
|
+
if (page.sourcePath) {
|
|
12684
|
+
raw = rewriteRelativeImages({
|
|
12685
|
+
deployBase: config.deployment.base,
|
|
12686
|
+
projectRoot: project.context.root,
|
|
12687
|
+
source: raw,
|
|
12688
|
+
sourcePath: page.sourcePath
|
|
12689
|
+
});
|
|
12690
|
+
}
|
|
12691
|
+
const parsed = frontmatter_default(raw);
|
|
12692
|
+
const body = downlevelComponents(applyAgentVisibility(parsed.content), components, parsed.data).trim();
|
|
12693
|
+
const url = pageUrl(page.route, config.deployment.site, normalizeBasePath(config.deployment.base));
|
|
12694
|
+
return [`# ${page.title}`, `Source: ${url}`, "", body].join(`
|
|
12695
|
+
`);
|
|
12696
|
+
}));
|
|
12697
|
+
const header = config.description ? `# ${config.title}
|
|
12698
|
+
|
|
12699
|
+
> ${config.description}
|
|
12700
|
+
` : `# ${config.title}
|
|
12701
|
+
`;
|
|
12702
|
+
return `${header}
|
|
12703
|
+
${sections.join(`
|
|
12704
|
+
|
|
12705
|
+
---
|
|
12706
|
+
|
|
12707
|
+
`)}
|
|
12708
|
+
`;
|
|
12709
|
+
};
|
|
12710
|
+
var buildLlmsFiles = async (project) => ({
|
|
12711
|
+
full: await buildFull(project),
|
|
12712
|
+
index: buildLlmsIndex(project)
|
|
12713
|
+
});
|
|
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
|
+
|
|
12758
|
+
// src/ai/skills.ts
|
|
12759
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
12760
|
+
import { readdir, readFile as readFile11, stat as stat2 } from "node:fs/promises";
|
|
12761
|
+
import { join as join18 } from "pathe";
|
|
12762
|
+
|
|
12763
|
+
// src/ai/tar.ts
|
|
12764
|
+
import { gzipSync } from "node:zlib";
|
|
12765
|
+
var BLOCK = 512;
|
|
12766
|
+
var NAME_MAX = 100;
|
|
12767
|
+
var encoder = new TextEncoder;
|
|
12768
|
+
var octal = (header, offset, length, value) => {
|
|
12769
|
+
const text = value.toString(8).padStart(length - 1, "0");
|
|
12770
|
+
header.set(encoder.encode(text), offset);
|
|
12771
|
+
header[offset + length - 1] = 0;
|
|
12772
|
+
};
|
|
12773
|
+
var text = (header, offset, value) => {
|
|
12774
|
+
header.set(encoder.encode(value), offset);
|
|
12775
|
+
};
|
|
12776
|
+
var fileHeader = (entry) => {
|
|
12777
|
+
const header = new Uint8Array(BLOCK);
|
|
12778
|
+
text(header, 0, entry.path);
|
|
12779
|
+
octal(header, 100, 8, entry.executable ? 493 : 420);
|
|
12780
|
+
octal(header, 108, 8, 0);
|
|
12781
|
+
octal(header, 116, 8, 0);
|
|
12782
|
+
octal(header, 124, 12, entry.content.byteLength);
|
|
12783
|
+
octal(header, 136, 12, 0);
|
|
12784
|
+
text(header, 156, "0");
|
|
12785
|
+
text(header, 257, "ustar");
|
|
12786
|
+
header[262] = 0;
|
|
12787
|
+
text(header, 263, "00");
|
|
12788
|
+
octal(header, 329, 8, 0);
|
|
12789
|
+
octal(header, 337, 8, 0);
|
|
12790
|
+
header.fill(32, 148, 156);
|
|
12791
|
+
let sum = 0;
|
|
12792
|
+
for (const byte of header) {
|
|
12793
|
+
sum += byte;
|
|
12794
|
+
}
|
|
12795
|
+
text(header, 148, sum.toString(8).padStart(6, "0"));
|
|
12796
|
+
header[154] = 0;
|
|
12797
|
+
header[155] = 32;
|
|
12798
|
+
return header;
|
|
12799
|
+
};
|
|
12800
|
+
var buildTarGz = (entries) => {
|
|
12801
|
+
const blocks = [];
|
|
12802
|
+
for (const entry of entries) {
|
|
12803
|
+
if (encoder.encode(entry.path).byteLength > NAME_MAX) {
|
|
12804
|
+
throw new Error(`tar path exceeds ${NAME_MAX} bytes: ${entry.path}`);
|
|
12805
|
+
}
|
|
12806
|
+
if (entry.path.startsWith("/") || entry.path.split("/").includes("..")) {
|
|
12807
|
+
throw new Error(`tar path must be archive-relative: ${entry.path}`);
|
|
12808
|
+
}
|
|
12809
|
+
blocks.push(fileHeader(entry), entry.content);
|
|
12810
|
+
const overhang = entry.content.byteLength % BLOCK;
|
|
12811
|
+
if (overhang > 0) {
|
|
12812
|
+
blocks.push(new Uint8Array(BLOCK - overhang));
|
|
12813
|
+
}
|
|
12814
|
+
}
|
|
12815
|
+
blocks.push(new Uint8Array(BLOCK * 2));
|
|
12816
|
+
const total = blocks.reduce((sum, block) => sum + block.byteLength, 0);
|
|
12817
|
+
const tar = new Uint8Array(total);
|
|
12818
|
+
let offset = 0;
|
|
12819
|
+
for (const block of blocks) {
|
|
12820
|
+
tar.set(block, offset);
|
|
12821
|
+
offset += block.byteLength;
|
|
12822
|
+
}
|
|
12823
|
+
return new Uint8Array(gzipSync(tar, { level: 9 }));
|
|
12824
|
+
};
|
|
12825
|
+
|
|
12826
|
+
// src/ai/skills.ts
|
|
12827
|
+
var AGENT_SKILLS_DIR = "/.well-known/agent-skills";
|
|
12828
|
+
var AGENT_SKILLS_SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
|
|
12829
|
+
var SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
12830
|
+
var SKILL_NAME_MAX = 64;
|
|
12831
|
+
var DESCRIPTION_MAX2 = 1024;
|
|
12832
|
+
var sha256 = (content) => `sha256:${createHash2("sha256").update(content).digest("hex")}`;
|
|
12833
|
+
var collectEntries = async (dir, prefix = "") => {
|
|
12834
|
+
const entries = [];
|
|
12835
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
12836
|
+
for (const item of items.toSorted((a, b) => a.name < b.name ? -1 : 1)) {
|
|
12837
|
+
if (item.name.startsWith(".")) {
|
|
12838
|
+
continue;
|
|
12839
|
+
}
|
|
12840
|
+
const path = join18(dir, item.name);
|
|
12841
|
+
const relative12 = prefix ? `${prefix}/${item.name}` : item.name;
|
|
12842
|
+
if (item.isDirectory()) {
|
|
12843
|
+
entries.push(...await collectEntries(path, relative12));
|
|
12844
|
+
} else if (item.isFile()) {
|
|
12845
|
+
const [content, info] = await Promise.all([readFile11(path), stat2(path)]);
|
|
12846
|
+
entries.push({
|
|
12847
|
+
content: new Uint8Array(content),
|
|
12848
|
+
executable: (info.mode & 64) !== 0,
|
|
12849
|
+
path: relative12
|
|
12850
|
+
});
|
|
12851
|
+
}
|
|
12852
|
+
}
|
|
12853
|
+
return entries;
|
|
12854
|
+
};
|
|
12855
|
+
var skillMeta = (raw, dirName) => {
|
|
12856
|
+
let data;
|
|
12857
|
+
try {
|
|
12858
|
+
({ data } = frontmatter_default(raw));
|
|
12859
|
+
} catch {
|
|
12860
|
+
return {
|
|
12861
|
+
meta: null,
|
|
12862
|
+
warning: `Skill "${dirName}" has unparsable SKILL.md frontmatter; skipped.`
|
|
12863
|
+
};
|
|
12864
|
+
}
|
|
12865
|
+
const name = typeof data.name === "string" ? data.name : "";
|
|
12866
|
+
const description = typeof data.description === "string" ? data.description : "";
|
|
12867
|
+
if (!(name && description)) {
|
|
12868
|
+
return {
|
|
12869
|
+
meta: null,
|
|
12870
|
+
warning: `Skill "${dirName}" is missing the required "name"/"description" frontmatter; skipped.`
|
|
12871
|
+
};
|
|
12872
|
+
}
|
|
12873
|
+
if (!SKILL_NAME.test(name) || name.length > SKILL_NAME_MAX) {
|
|
12874
|
+
return {
|
|
12875
|
+
meta: null,
|
|
12876
|
+
warning: `Skill "${dirName}" has an invalid name "${name}" (lowercase alphanumerics and single hyphens, max 64 chars); skipped.`
|
|
12877
|
+
};
|
|
12878
|
+
}
|
|
12879
|
+
return {
|
|
12880
|
+
meta: { description: description.slice(0, DESCRIPTION_MAX2), name }
|
|
12881
|
+
};
|
|
12882
|
+
};
|
|
12883
|
+
var collectSkills = async (dir) => {
|
|
12884
|
+
const skills = [];
|
|
12885
|
+
const warnings = [];
|
|
12886
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
12887
|
+
for (const item of items.toSorted((a, b) => a.name < b.name ? -1 : 1)) {
|
|
12888
|
+
if (!item.isDirectory() || item.name.startsWith(".")) {
|
|
12889
|
+
continue;
|
|
12890
|
+
}
|
|
12891
|
+
const entries = await collectEntries(join18(dir, item.name));
|
|
12892
|
+
const skillMd = entries.find((entry) => entry.path === "SKILL.md");
|
|
12893
|
+
if (!skillMd) {
|
|
12894
|
+
continue;
|
|
12895
|
+
}
|
|
12896
|
+
const { meta, warning } = skillMeta(new TextDecoder().decode(skillMd.content), item.name);
|
|
12897
|
+
if (!meta) {
|
|
12898
|
+
if (warning) {
|
|
12899
|
+
warnings.push(warning);
|
|
12900
|
+
}
|
|
12901
|
+
continue;
|
|
12902
|
+
}
|
|
12903
|
+
const single = entries.length === 1;
|
|
12904
|
+
const content = single ? skillMd.content : buildTarGz(entries);
|
|
12905
|
+
skills.push({
|
|
12906
|
+
content,
|
|
12907
|
+
description: meta.description,
|
|
12908
|
+
digest: sha256(content),
|
|
12909
|
+
name: meta.name,
|
|
12910
|
+
path: single ? `${meta.name}/SKILL.md` : `${meta.name}.tar.gz`,
|
|
12911
|
+
type: single ? "skill-md" : "archive"
|
|
12912
|
+
});
|
|
12140
12913
|
}
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
> ${config.description}` : `# ${config.title}`;
|
|
12144
|
-
return `${[header, ...blocks].join(`
|
|
12145
|
-
|
|
12146
|
-
`)}
|
|
12147
|
-
`;
|
|
12914
|
+
return { skills, warnings };
|
|
12148
12915
|
};
|
|
12149
|
-
var
|
|
12150
|
-
const
|
|
12151
|
-
const
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12916
|
+
var buildSkillsIndex = (skills, config) => {
|
|
12917
|
+
const deployBase = normalizeBasePath(config.deployment.base);
|
|
12918
|
+
const index = {
|
|
12919
|
+
$schema: AGENT_SKILLS_SCHEMA,
|
|
12920
|
+
skills: skills.map((skill) => ({
|
|
12921
|
+
description: skill.description,
|
|
12922
|
+
digest: skill.digest,
|
|
12923
|
+
name: skill.name,
|
|
12924
|
+
type: skill.type,
|
|
12925
|
+
url: `${deployBase}${AGENT_SKILLS_DIR}/${skill.path}`
|
|
12926
|
+
}))
|
|
12155
12927
|
};
|
|
12156
|
-
|
|
12157
|
-
const raw = await readEntryText(project, page);
|
|
12158
|
-
const parsed = frontmatter_default(raw);
|
|
12159
|
-
const body = downlevelComponents(applyAgentVisibility(parsed.content), components, parsed.data).trim();
|
|
12160
|
-
const url = pageUrl(page.route, config.deployment.site, normalizeBasePath(config.deployment.base));
|
|
12161
|
-
return [`# ${page.title}`, `Source: ${url}`, "", body].join(`
|
|
12162
|
-
`);
|
|
12163
|
-
}));
|
|
12164
|
-
const header = config.description ? `# ${config.title}
|
|
12165
|
-
|
|
12166
|
-
> ${config.description}
|
|
12167
|
-
` : `# ${config.title}
|
|
12928
|
+
return `${JSON.stringify(index, null, 2)}
|
|
12168
12929
|
`;
|
|
12169
|
-
|
|
12170
|
-
${sections.join(`
|
|
12171
|
-
|
|
12172
|
-
---
|
|
12930
|
+
};
|
|
12173
12931
|
|
|
12174
|
-
|
|
12932
|
+
// src/ai/web-bot-auth.ts
|
|
12933
|
+
var SIGNATURES_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
12934
|
+
var SIGNATURES_DIRECTORY_TYPE = "application/http-message-signatures-directory+json";
|
|
12935
|
+
var buildSignaturesDirectory = (config) => {
|
|
12936
|
+
const { keys } = config.ai.webBotAuth;
|
|
12937
|
+
if (keys.length === 0) {
|
|
12938
|
+
return null;
|
|
12939
|
+
}
|
|
12940
|
+
return `${JSON.stringify({ keys }, null, 2)}
|
|
12175
12941
|
`;
|
|
12176
12942
|
};
|
|
12177
|
-
var buildLlmsFiles = async (project) => ({
|
|
12178
|
-
full: await buildFull(project),
|
|
12179
|
-
index: buildIndex(project)
|
|
12180
|
-
});
|
|
12181
12943
|
|
|
12182
12944
|
// src/core/gitignore.ts
|
|
12183
|
-
import { existsSync as
|
|
12184
|
-
import { readFile as
|
|
12185
|
-
import { join as
|
|
12945
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
12946
|
+
import { readFile as readFile12, writeFile as writeFile6 } from "node:fs/promises";
|
|
12947
|
+
import { join as join19 } from "pathe";
|
|
12186
12948
|
var gitignoreKey = (line) => line.trim().replace(/\/+$/u, "");
|
|
12187
12949
|
var ensureGitignore = async (root, entries) => {
|
|
12188
|
-
const path =
|
|
12189
|
-
const existing =
|
|
12950
|
+
const path = join19(root, ".gitignore");
|
|
12951
|
+
const existing = existsSync10(path) ? await readFile12(path, "utf-8") : "";
|
|
12190
12952
|
const present = new Set(existing.split(`
|
|
12191
12953
|
`).flatMap((line) => {
|
|
12192
12954
|
const key = gitignoreKey(line);
|
|
@@ -12238,13 +13000,31 @@ var HEADER_RULES = [
|
|
|
12238
13000
|
underBasePath: false
|
|
12239
13001
|
}
|
|
12240
13002
|
];
|
|
12241
|
-
var buildNetlifyHeaders = (config) => {
|
|
13003
|
+
var buildNetlifyHeaders = (config, homeLinkHeader) => {
|
|
12242
13004
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
12243
|
-
|
|
13005
|
+
const rules = HEADER_RULES.map((rule) => {
|
|
12244
13006
|
const prefix = rule.underBasePath ? `${deployBase}${config.basePath}` : deployBase;
|
|
12245
13007
|
return `${prefix}/*.${rule.ext}
|
|
12246
13008
|
Content-Type: ${rule.contentType}`;
|
|
12247
|
-
})
|
|
13009
|
+
});
|
|
13010
|
+
if (homeLinkHeader) {
|
|
13011
|
+
rules.push(`${deployBase}/
|
|
13012
|
+
Link: ${homeLinkHeader}`);
|
|
13013
|
+
}
|
|
13014
|
+
if (hasApiCatalog(config)) {
|
|
13015
|
+
rules.push(`${deployBase}${API_CATALOG_PATH}
|
|
13016
|
+
Content-Type: ${API_CATALOG_TYPE}`);
|
|
13017
|
+
}
|
|
13018
|
+
if (config.ai.webBotAuth.keys.length > 0) {
|
|
13019
|
+
rules.push(`${deployBase}${SIGNATURES_DIRECTORY_PATH}
|
|
13020
|
+
Content-Type: ${SIGNATURES_DIRECTORY_TYPE}`);
|
|
13021
|
+
}
|
|
13022
|
+
if (config.ai.skills) {
|
|
13023
|
+
rules.push(`${deployBase}/.well-known/agent-skills/*.md
|
|
13024
|
+
Content-Type: text/markdown; charset=utf-8`, `${deployBase}/.well-known/agent-skills/*.tar.gz
|
|
13025
|
+
Content-Type: application/gzip`);
|
|
13026
|
+
}
|
|
13027
|
+
return `${rules.join(`
|
|
12248
13028
|
`)}
|
|
12249
13029
|
`;
|
|
12250
13030
|
};
|
|
@@ -12284,7 +13064,7 @@ var buildRobots = (project) => {
|
|
|
12284
13064
|
};
|
|
12285
13065
|
|
|
12286
13066
|
// src/astro/pages.ts
|
|
12287
|
-
import { extname as
|
|
13067
|
+
import { extname as extname6, relative as relative12 } from "pathe";
|
|
12288
13068
|
import { glob as glob4, globSync } from "tinyglobby";
|
|
12289
13069
|
var PAGE_GLOB = ["**/*.astro"];
|
|
12290
13070
|
var isPrivatePage = (rel) => rel.split("/").some((segment) => segment.startsWith("_"));
|
|
@@ -12292,11 +13072,11 @@ var toPageRoutes = (pagesRoot, files) => {
|
|
|
12292
13072
|
files.sort();
|
|
12293
13073
|
const routes = [];
|
|
12294
13074
|
for (const file of files) {
|
|
12295
|
-
const rel =
|
|
13075
|
+
const rel = relative12(pagesRoot, file);
|
|
12296
13076
|
if (isPrivatePage(rel)) {
|
|
12297
13077
|
continue;
|
|
12298
13078
|
}
|
|
12299
|
-
const withoutExt = rel.slice(0, rel.length -
|
|
13079
|
+
const withoutExt = rel.slice(0, rel.length - extname6(rel).length);
|
|
12300
13080
|
const parts = withoutExt.split("/");
|
|
12301
13081
|
if (parts.at(-1) === "index") {
|
|
12302
13082
|
parts.pop();
|
|
@@ -12408,8 +13188,101 @@ ${urls.join(`
|
|
|
12408
13188
|
`;
|
|
12409
13189
|
};
|
|
12410
13190
|
|
|
13191
|
+
// src/deploy/vercel-negotiation.ts
|
|
13192
|
+
var ACCEPT_MARKDOWN_HEADER_VALUE = "(.*,)?\\s*text/(x-)?markdown(\\s*[;,].*)?$";
|
|
13193
|
+
var ACCEPT_MARKDOWN_CONDITION = [
|
|
13194
|
+
{ key: "accept", type: "header", value: ACCEPT_MARKDOWN_HEADER_VALUE }
|
|
13195
|
+
];
|
|
13196
|
+
var VARY_ACCEPT = { vary: "Accept" };
|
|
13197
|
+
var MAX_ALTERNATION_LENGTH = 3900;
|
|
13198
|
+
var REGEX_SPECIALS = /[$()*+.?[\]^{|}\\]/gu;
|
|
13199
|
+
var routePattern = (route) => encodeURI(route).replace(REGEX_SPECIALS, "\\$&");
|
|
13200
|
+
var chunkPatterns = (patterns) => {
|
|
13201
|
+
const chunks = [];
|
|
13202
|
+
let current = [];
|
|
13203
|
+
let length = 0;
|
|
13204
|
+
for (const pattern of patterns) {
|
|
13205
|
+
if (current.length > 0 && length + pattern.length + 1 > MAX_ALTERNATION_LENGTH) {
|
|
13206
|
+
chunks.push(current);
|
|
13207
|
+
current = [];
|
|
13208
|
+
length = 0;
|
|
13209
|
+
}
|
|
13210
|
+
current.push(pattern);
|
|
13211
|
+
length += pattern.length + 1;
|
|
13212
|
+
}
|
|
13213
|
+
if (current.length > 0) {
|
|
13214
|
+
chunks.push(current);
|
|
13215
|
+
}
|
|
13216
|
+
return chunks;
|
|
13217
|
+
};
|
|
13218
|
+
var buildNegotiationRoutes = (routePaths, homeTokens) => {
|
|
13219
|
+
const home = routePaths.includes("/");
|
|
13220
|
+
const rest = routePaths.filter((path) => path !== "/").map((path) => routePattern(path));
|
|
13221
|
+
const chunks = chunkPatterns(rest);
|
|
13222
|
+
const rewriteRoutes = home ? [
|
|
13223
|
+
{
|
|
13224
|
+
dest: "/index.md",
|
|
13225
|
+
has: ACCEPT_MARKDOWN_CONDITION,
|
|
13226
|
+
headers: homeTokens === undefined ? VARY_ACCEPT : { ...VARY_ACCEPT, "x-markdown-tokens": String(homeTokens) },
|
|
13227
|
+
src: "^/$"
|
|
13228
|
+
}
|
|
13229
|
+
] : [];
|
|
13230
|
+
for (const chunk of chunks) {
|
|
13231
|
+
rewriteRoutes.push({
|
|
13232
|
+
dest: "$1.md",
|
|
13233
|
+
has: ACCEPT_MARKDOWN_CONDITION,
|
|
13234
|
+
headers: VARY_ACCEPT,
|
|
13235
|
+
src: `^(${chunk.join("|")})/?$`
|
|
13236
|
+
});
|
|
13237
|
+
}
|
|
13238
|
+
const headerChunks = chunkPatterns(home ? ["/", ...rest] : rest);
|
|
13239
|
+
const headerRoutes = headerChunks.map((chunk) => ({
|
|
13240
|
+
continue: true,
|
|
13241
|
+
headers: VARY_ACCEPT,
|
|
13242
|
+
src: `^(?:${chunk.join("|")})/?$`
|
|
13243
|
+
}));
|
|
13244
|
+
return { headerRoutes, rewriteRoutes };
|
|
13245
|
+
};
|
|
13246
|
+
var HOME_SRC = "^/$";
|
|
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;
|
|
13248
|
+
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides, homeTokens) => {
|
|
13249
|
+
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
13250
|
+
if (routePaths.length === 0 && !homeLinkHeader && overrideEntries.length === 0) {
|
|
13251
|
+
return null;
|
|
13252
|
+
}
|
|
13253
|
+
let config;
|
|
13254
|
+
try {
|
|
13255
|
+
config = JSON.parse(configText);
|
|
13256
|
+
} catch {
|
|
13257
|
+
return null;
|
|
13258
|
+
}
|
|
13259
|
+
if (!Array.isArray(config.routes)) {
|
|
13260
|
+
return null;
|
|
13261
|
+
}
|
|
13262
|
+
for (const [path, contentType] of overrideEntries) {
|
|
13263
|
+
config.overrides = { ...config.overrides, [path]: { contentType } };
|
|
13264
|
+
}
|
|
13265
|
+
const routes = config.routes.filter((route) => !isNegotiationRoute(route));
|
|
13266
|
+
const filesystemIndex = routes.findIndex((route) => route.handle === "filesystem");
|
|
13267
|
+
if (filesystemIndex === -1) {
|
|
13268
|
+
return null;
|
|
13269
|
+
}
|
|
13270
|
+
const { headerRoutes, rewriteRoutes } = buildNegotiationRoutes(routePaths, homeTokens);
|
|
13271
|
+
if (homeLinkHeader) {
|
|
13272
|
+
headerRoutes.push({
|
|
13273
|
+
continue: true,
|
|
13274
|
+
headers: { link: homeLinkHeader },
|
|
13275
|
+
src: HOME_SRC
|
|
13276
|
+
});
|
|
13277
|
+
}
|
|
13278
|
+
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes);
|
|
13279
|
+
config.routes = routes;
|
|
13280
|
+
return `${JSON.stringify(config, null, "\t")}
|
|
13281
|
+
`;
|
|
13282
|
+
};
|
|
13283
|
+
|
|
12411
13284
|
// src/search/build.ts
|
|
12412
|
-
import { join as
|
|
13285
|
+
import { join as join20 } from "pathe";
|
|
12413
13286
|
var buildSearchIndex = async (outDir) => {
|
|
12414
13287
|
const pagefind = await import("pagefind");
|
|
12415
13288
|
const { index } = await pagefind.createIndex({});
|
|
@@ -12417,7 +13290,7 @@ var buildSearchIndex = async (outDir) => {
|
|
|
12417
13290
|
throw new Error("Failed to create Pagefind index.");
|
|
12418
13291
|
}
|
|
12419
13292
|
const result = await index.addDirectory({ path: outDir });
|
|
12420
|
-
await index.writeFiles({ outputPath:
|
|
13293
|
+
await index.writeFiles({ outputPath: join20(outDir, "pagefind") });
|
|
12421
13294
|
await pagefind.close();
|
|
12422
13295
|
return result.page_count;
|
|
12423
13296
|
};
|
|
@@ -12640,14 +13513,14 @@ var syncSearchProvider = async (project, reporter) => {
|
|
|
12640
13513
|
|
|
12641
13514
|
// src/cli/dev-lock.ts
|
|
12642
13515
|
import {
|
|
12643
|
-
existsSync as
|
|
13516
|
+
existsSync as existsSync11,
|
|
12644
13517
|
mkdirSync,
|
|
12645
13518
|
readFileSync as readFileSync5,
|
|
12646
13519
|
rmSync,
|
|
12647
13520
|
writeFileSync
|
|
12648
13521
|
} from "node:fs";
|
|
12649
|
-
import { join as
|
|
12650
|
-
var lockPath = (outDir) =>
|
|
13522
|
+
import { join as join21 } from "pathe";
|
|
13523
|
+
var lockPath = (outDir) => join21(outDir, "dev.lock");
|
|
12651
13524
|
var isValidPid = (pid) => typeof pid === "number" && Number.isInteger(pid) && pid > 0;
|
|
12652
13525
|
var parseLock = (raw) => {
|
|
12653
13526
|
let data;
|
|
@@ -12677,7 +13550,7 @@ var isProcessAlive = (pid) => {
|
|
|
12677
13550
|
};
|
|
12678
13551
|
var readDevLock = (outDir) => {
|
|
12679
13552
|
const path = lockPath(outDir);
|
|
12680
|
-
if (!
|
|
13553
|
+
if (!existsSync11(path)) {
|
|
12681
13554
|
return null;
|
|
12682
13555
|
}
|
|
12683
13556
|
const lock = parseLock(readFileSync5(path, "utf-8"));
|
|
@@ -12701,7 +13574,7 @@ class DevLockHeldError extends Error {
|
|
|
12701
13574
|
}
|
|
12702
13575
|
var ownsLock = (outDir) => {
|
|
12703
13576
|
const path = lockPath(outDir);
|
|
12704
|
-
if (!
|
|
13577
|
+
if (!existsSync11(path)) {
|
|
12705
13578
|
return false;
|
|
12706
13579
|
}
|
|
12707
13580
|
try {
|
|
@@ -12758,12 +13631,12 @@ var refuseIfDevRunning = (root, action, options = {}) => {
|
|
|
12758
13631
|
};
|
|
12759
13632
|
|
|
12760
13633
|
// src/astro/generate.ts
|
|
12761
|
-
import { createHash as
|
|
12762
|
-
import { existsSync as
|
|
13634
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
13635
|
+
import { existsSync as existsSync16, readFileSync as readFileSync9, realpathSync } from "node:fs";
|
|
12763
13636
|
import {
|
|
12764
13637
|
lstat,
|
|
12765
13638
|
mkdir as mkdir6,
|
|
12766
|
-
readFile as
|
|
13639
|
+
readFile as readFile16,
|
|
12767
13640
|
readlink,
|
|
12768
13641
|
realpath,
|
|
12769
13642
|
rename,
|
|
@@ -12773,7 +13646,7 @@ import {
|
|
|
12773
13646
|
} from "node:fs/promises";
|
|
12774
13647
|
import { createRequire as createRequire5 } from "node:module";
|
|
12775
13648
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
12776
|
-
import { basename as basename3, dirname as
|
|
13649
|
+
import { basename as basename3, dirname as dirname10, join as join28, normalize as normalize3, relative as relative14, resolve as resolve8 } from "pathe";
|
|
12777
13650
|
import { glob as glob7 } from "tinyglobby";
|
|
12778
13651
|
|
|
12779
13652
|
// src/ai/ask-data.ts
|
|
@@ -12796,31 +13669,6 @@ var buildAskData = async (project) => {
|
|
|
12796
13669
|
};
|
|
12797
13670
|
};
|
|
12798
13671
|
|
|
12799
|
-
// src/ai/markdown.ts
|
|
12800
|
-
import { readFile as readFile10 } from "node:fs/promises";
|
|
12801
|
-
var agentMarkdown = (entry) => entry.md ?? entry.mdx;
|
|
12802
|
-
var buildRawMarkdown = async (project) => {
|
|
12803
|
-
const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
|
|
12804
|
-
const components = {
|
|
12805
|
-
...exampleComponentSerializers(project.examples ?? {}),
|
|
12806
|
-
...project.config.ai.markdownComponents
|
|
12807
|
-
};
|
|
12808
|
-
const readRoute = async (route) => {
|
|
12809
|
-
const page = pageById.get(route.id);
|
|
12810
|
-
if (page) {
|
|
12811
|
-
return await readEntryText(project, page);
|
|
12812
|
-
}
|
|
12813
|
-
return route.sourcePath ? await readFile10(route.sourcePath, "utf-8") : "";
|
|
12814
|
-
};
|
|
12815
|
-
const entries = await Promise.all(project.manifest.routes.map(async (route) => {
|
|
12816
|
-
const source = applyAgentVisibility(await readRoute(route));
|
|
12817
|
-
const md = downlevelComponents(source, components, frontmatter_default(source).data);
|
|
12818
|
-
const entry = md === source ? { mdx: source } : { md, mdx: source };
|
|
12819
|
-
return [route.path, entry];
|
|
12820
|
-
}));
|
|
12821
|
-
return Object.fromEntries(entries);
|
|
12822
|
-
};
|
|
12823
|
-
|
|
12824
13672
|
// src/ai/mcp/data.ts
|
|
12825
13673
|
var buildMcpData = async (project) => {
|
|
12826
13674
|
const { config, graph, manifest } = project;
|
|
@@ -12912,19 +13760,42 @@ var buildMcpDiscovery = (input) => ({
|
|
|
12912
13760
|
}
|
|
12913
13761
|
]
|
|
12914
13762
|
});
|
|
12915
|
-
var
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
|
|
12920
|
-
|
|
12921
|
-
|
|
12922
|
-
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
13763
|
+
var SERVER_CARD_SCHEMA = "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json";
|
|
13764
|
+
var CARD_TEXT_MAX = 100;
|
|
13765
|
+
var truncate = (text2) => text2.length > CARD_TEXT_MAX ? `${text2.slice(0, CARD_TEXT_MAX - 1)}…` : text2;
|
|
13766
|
+
var reverseDnsName = (input) => {
|
|
13767
|
+
let namespace = "localhost";
|
|
13768
|
+
if (input.site) {
|
|
13769
|
+
try {
|
|
13770
|
+
namespace = new URL(input.site).hostname.split(".").toReversed().join(".");
|
|
13771
|
+
} catch {}
|
|
13772
|
+
}
|
|
13773
|
+
return `${namespace}/${slugify(input.name) || "docs"}`;
|
|
13774
|
+
};
|
|
13775
|
+
var HTTP_URL2 = /^https?:\/\//u;
|
|
13776
|
+
var buildMcpServerCard = (input) => {
|
|
13777
|
+
const url = serverUrl(input);
|
|
13778
|
+
return {
|
|
13779
|
+
$schema: SERVER_CARD_SCHEMA,
|
|
13780
|
+
capabilities: { tools: { listChanged: false } },
|
|
13781
|
+
description: truncate(`Model Context Protocol server for the ${input.name} documentation.`),
|
|
13782
|
+
name: reverseDnsName(input),
|
|
13783
|
+
...HTTP_URL2.test(url) ? { remotes: [{ type: "streamable-http", url }] } : {},
|
|
13784
|
+
serverInfo: { name: input.name, version: input.version },
|
|
13785
|
+
title: truncate(input.name),
|
|
13786
|
+
tools: MCP_TOOLS.map((tool) => ({
|
|
13787
|
+
annotations: tool.annotations,
|
|
13788
|
+
description: tool.description,
|
|
13789
|
+
name: tool.name,
|
|
13790
|
+
title: tool.title
|
|
13791
|
+
})),
|
|
13792
|
+
transport: "streamable-http",
|
|
13793
|
+
transports: [{ endpoint: url, type: "streamable-http" }],
|
|
13794
|
+
url,
|
|
13795
|
+
version: input.version,
|
|
13796
|
+
...input.site ? { websiteUrl: trimEnd(input.site, "/") } : {}
|
|
13797
|
+
};
|
|
13798
|
+
};
|
|
12928
13799
|
|
|
12929
13800
|
// src/core/builtin-tags.ts
|
|
12930
13801
|
var BUILTIN_MDX_TAGS = new Set([
|
|
@@ -12991,8 +13862,8 @@ var validateUsedComponents = (pages, extraTags, registryNames) => {
|
|
|
12991
13862
|
};
|
|
12992
13863
|
|
|
12993
13864
|
// src/core/component-overrides.ts
|
|
12994
|
-
import { existsSync as
|
|
12995
|
-
import { dirname as
|
|
13865
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
13866
|
+
import { dirname as dirname8, extname as extname7, isAbsolute as isAbsolute6, resolve as resolve6 } from "pathe";
|
|
12996
13867
|
import ts from "typescript";
|
|
12997
13868
|
var GROUPS = ["mdx", "layout", "islands"];
|
|
12998
13869
|
var GROUP_SET = new Set(GROUPS);
|
|
@@ -13084,24 +13955,24 @@ var findDefaultExportObject = (sourceFile) => {
|
|
|
13084
13955
|
var probeExtension = (base) => {
|
|
13085
13956
|
for (const extension of COMPONENT_EXTS) {
|
|
13086
13957
|
const candidate = `${base}.${extension}`;
|
|
13087
|
-
if (
|
|
13958
|
+
if (existsSync12(candidate)) {
|
|
13088
13959
|
return candidate;
|
|
13089
13960
|
}
|
|
13090
13961
|
}
|
|
13091
13962
|
return null;
|
|
13092
13963
|
};
|
|
13093
13964
|
var toImport = (specifier, imported, dir) => {
|
|
13094
|
-
const
|
|
13965
|
+
const relative13 = specifier.startsWith(".") || isAbsolute6(specifier);
|
|
13095
13966
|
let path = specifier;
|
|
13096
|
-
let extension =
|
|
13097
|
-
if (
|
|
13098
|
-
const absolute = isAbsolute6(specifier) ? specifier :
|
|
13967
|
+
let extension = extname7(specifier).slice(1).toLowerCase();
|
|
13968
|
+
if (relative13) {
|
|
13969
|
+
const absolute = isAbsolute6(specifier) ? specifier : resolve6(dir, specifier);
|
|
13099
13970
|
if (extension) {
|
|
13100
13971
|
path = absolute;
|
|
13101
13972
|
} else {
|
|
13102
13973
|
const probed = probeExtension(absolute);
|
|
13103
13974
|
path = probed ?? absolute;
|
|
13104
|
-
extension = probed ?
|
|
13975
|
+
extension = probed ? extname7(probed).slice(1).toLowerCase() : "";
|
|
13105
13976
|
}
|
|
13106
13977
|
}
|
|
13107
13978
|
return {
|
|
@@ -13250,7 +14121,7 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
13250
14121
|
return result;
|
|
13251
14122
|
}
|
|
13252
14123
|
const imports = collectImports(sourceFile);
|
|
13253
|
-
const dir =
|
|
14124
|
+
const dir = dirname8(filePath);
|
|
13254
14125
|
for (const property of object.properties) {
|
|
13255
14126
|
collectGroupOverrides(property, imports, dir, result);
|
|
13256
14127
|
}
|
|
@@ -13258,16 +14129,16 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
13258
14129
|
};
|
|
13259
14130
|
|
|
13260
14131
|
// src/core/tsconfig-aliases.ts
|
|
13261
|
-
import { existsSync as
|
|
14132
|
+
import { existsSync as existsSync13, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
13262
14133
|
import { createRequire as createRequire3 } from "node:module";
|
|
13263
14134
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
13264
|
-
import { dirname as
|
|
13265
|
-
var scanJsonChar = (
|
|
13266
|
-
const char =
|
|
14135
|
+
import { dirname as dirname9, isAbsolute as isAbsolute7, join as join22, resolve as resolve7 } from "pathe";
|
|
14136
|
+
var scanJsonChar = (text2, index, inString) => {
|
|
14137
|
+
const char = text2[index];
|
|
13267
14138
|
if (inString) {
|
|
13268
14139
|
if (char === "\\") {
|
|
13269
14140
|
return {
|
|
13270
|
-
append: char + (
|
|
14141
|
+
append: char + (text2[index + 1] ?? ""),
|
|
13271
14142
|
inString: true,
|
|
13272
14143
|
next: index + 2
|
|
13273
14144
|
};
|
|
@@ -13277,35 +14148,35 @@ var scanJsonChar = (text, index, inString) => {
|
|
|
13277
14148
|
if (char === '"') {
|
|
13278
14149
|
return { append: char, inString: true, next: index + 1 };
|
|
13279
14150
|
}
|
|
13280
|
-
if (char === "/" &&
|
|
13281
|
-
const newline =
|
|
14151
|
+
if (char === "/" && text2[index + 1] === "/") {
|
|
14152
|
+
const newline = text2.indexOf(`
|
|
13282
14153
|
`, index + 2);
|
|
13283
14154
|
return {
|
|
13284
14155
|
append: "",
|
|
13285
14156
|
inString: false,
|
|
13286
|
-
next: newline === -1 ?
|
|
14157
|
+
next: newline === -1 ? text2.length : newline
|
|
13287
14158
|
};
|
|
13288
14159
|
}
|
|
13289
|
-
if (char === "/" &&
|
|
13290
|
-
const end =
|
|
14160
|
+
if (char === "/" && text2[index + 1] === "*") {
|
|
14161
|
+
const end = text2.indexOf("*/", index + 2);
|
|
13291
14162
|
return {
|
|
13292
14163
|
append: "",
|
|
13293
14164
|
inString: false,
|
|
13294
|
-
next: end === -1 ?
|
|
14165
|
+
next: end === -1 ? text2.length : end + 2
|
|
13295
14166
|
};
|
|
13296
14167
|
}
|
|
13297
14168
|
return { append: char ?? "", inString: false, next: index + 1 };
|
|
13298
14169
|
};
|
|
13299
|
-
var stripJsonComments = (
|
|
14170
|
+
var stripJsonComments = (text2) => {
|
|
13300
14171
|
let out = "";
|
|
13301
14172
|
let inString = false;
|
|
13302
14173
|
let index = 0;
|
|
13303
|
-
while (index <
|
|
14174
|
+
while (index < text2.length) {
|
|
13304
14175
|
const {
|
|
13305
14176
|
append,
|
|
13306
14177
|
inString: nextInString,
|
|
13307
14178
|
next
|
|
13308
|
-
} = scanJsonChar(
|
|
14179
|
+
} = scanJsonChar(text2, index, inString);
|
|
13309
14180
|
out += append;
|
|
13310
14181
|
inString = nextInString;
|
|
13311
14182
|
index = next;
|
|
@@ -13313,9 +14184,9 @@ var stripJsonComments = (text) => {
|
|
|
13313
14184
|
return out;
|
|
13314
14185
|
};
|
|
13315
14186
|
var TRAILING_COMMA = /,(?<rest>\s*[}\]])/gu;
|
|
13316
|
-
var parseJsonc = (
|
|
14187
|
+
var parseJsonc = (text2) => {
|
|
13317
14188
|
try {
|
|
13318
|
-
const cleaned = stripJsonComments(
|
|
14189
|
+
const cleaned = stripJsonComments(text2).replaceAll(TRAILING_COMMA, "$<rest>");
|
|
13319
14190
|
const value = JSON.parse(cleaned);
|
|
13320
14191
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
13321
14192
|
} catch {
|
|
@@ -13331,15 +14202,15 @@ var isFile = (path) => {
|
|
|
13331
14202
|
};
|
|
13332
14203
|
var resolveExtends = (spec, fromDir) => {
|
|
13333
14204
|
if (spec.startsWith(".") || isAbsolute7(spec)) {
|
|
13334
|
-
const candidates = spec.endsWith(".json") ? [
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
14205
|
+
const candidates = spec.endsWith(".json") ? [resolve7(fromDir, spec)] : [
|
|
14206
|
+
resolve7(fromDir, `${spec}.json`),
|
|
14207
|
+
resolve7(fromDir, spec, "tsconfig.json"),
|
|
14208
|
+
resolve7(fromDir, spec)
|
|
13338
14209
|
];
|
|
13339
14210
|
return candidates.find(isFile) ?? null;
|
|
13340
14211
|
}
|
|
13341
14212
|
try {
|
|
13342
|
-
const requireFromDir = createRequire3(pathToFileURL2(
|
|
14213
|
+
const requireFromDir = createRequire3(pathToFileURL2(join22(fromDir, "_.js")).href);
|
|
13343
14214
|
for (const sub of [`${spec}/tsconfig.json`, spec]) {
|
|
13344
14215
|
try {
|
|
13345
14216
|
return requireFromDir.resolve(sub);
|
|
@@ -13349,7 +14220,7 @@ var resolveExtends = (spec, fromDir) => {
|
|
|
13349
14220
|
return null;
|
|
13350
14221
|
};
|
|
13351
14222
|
var loadPaths = (file, seen) => {
|
|
13352
|
-
if (seen.has(file) || !
|
|
14223
|
+
if (seen.has(file) || !existsSync13(file)) {
|
|
13353
14224
|
return null;
|
|
13354
14225
|
}
|
|
13355
14226
|
seen.add(file);
|
|
@@ -13361,7 +14232,7 @@ var loadPaths = (file, seen) => {
|
|
|
13361
14232
|
if (options.paths && typeof options.paths === "object") {
|
|
13362
14233
|
const baseUrl = typeof options.baseUrl === "string" ? options.baseUrl : ".";
|
|
13363
14234
|
return {
|
|
13364
|
-
baseDir:
|
|
14235
|
+
baseDir: resolve7(dirname9(file), baseUrl),
|
|
13365
14236
|
paths: options.paths
|
|
13366
14237
|
};
|
|
13367
14238
|
}
|
|
@@ -13370,7 +14241,7 @@ var loadPaths = (file, seen) => {
|
|
|
13370
14241
|
if (typeof base !== "string") {
|
|
13371
14242
|
continue;
|
|
13372
14243
|
}
|
|
13373
|
-
const resolved = resolveExtends(base,
|
|
14244
|
+
const resolved = resolveExtends(base, dirname9(file));
|
|
13374
14245
|
const found = resolved ? loadPaths(resolved, seen) : null;
|
|
13375
14246
|
if (found) {
|
|
13376
14247
|
return found;
|
|
@@ -13388,10 +14259,10 @@ var toAlias = (key, value, baseDir) => {
|
|
|
13388
14259
|
if (find === "" || find === "*") {
|
|
13389
14260
|
return null;
|
|
13390
14261
|
}
|
|
13391
|
-
return { find, replacement:
|
|
14262
|
+
return { find, replacement: resolve7(baseDir, target) };
|
|
13392
14263
|
};
|
|
13393
14264
|
var resolveTsconfigAliases = (root) => {
|
|
13394
|
-
const entry = ["tsconfig.json", "jsconfig.json"].map((name) =>
|
|
14265
|
+
const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join22(root, name)).find((file) => existsSync13(file));
|
|
13395
14266
|
if (!entry) {
|
|
13396
14267
|
return {};
|
|
13397
14268
|
}
|
|
@@ -13409,24 +14280,126 @@ var resolveTsconfigAliases = (root) => {
|
|
|
13409
14280
|
return aliases;
|
|
13410
14281
|
};
|
|
13411
14282
|
|
|
14283
|
+
// src/og/derive.ts
|
|
14284
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
14285
|
+
import { isAbsolute as isAbsolute8, join as join23 } from "pathe";
|
|
14286
|
+
var CARD_WEIGHTS = [400, 600];
|
|
14287
|
+
var absoluteSrc = (root, src) => isAbsolute8(src) ? src : join23(root, src);
|
|
14288
|
+
var googleWeights = (weights) => {
|
|
14289
|
+
const numbers = weights.filter((weight) => typeof weight === "number");
|
|
14290
|
+
const used = numbers.filter((weight) => CARD_WEIGHTS.includes(weight));
|
|
14291
|
+
if (used.length > 0) {
|
|
14292
|
+
return used;
|
|
14293
|
+
}
|
|
14294
|
+
if (numbers.length > 0) {
|
|
14295
|
+
return numbers;
|
|
14296
|
+
}
|
|
14297
|
+
const [first] = weights;
|
|
14298
|
+
return weights.length === 1 && typeof first === "string" ? first : undefined;
|
|
14299
|
+
};
|
|
14300
|
+
var googleOgFont = (name, weights) => {
|
|
14301
|
+
const weight = googleWeights(weights);
|
|
14302
|
+
return weight === undefined ? { name } : { name, weight };
|
|
14303
|
+
};
|
|
14304
|
+
var localOgFonts = (font, root) => font.variants.map((variant) => ({
|
|
14305
|
+
name: font.name,
|
|
14306
|
+
src: absoluteSrc(root, variant.src),
|
|
14307
|
+
...typeof variant.weight === "number" ? { weight: variant.weight } : {},
|
|
14308
|
+
...variant.style === "normal" || variant.style === "italic" ? { style: variant.style } : {}
|
|
14309
|
+
}));
|
|
14310
|
+
var roleFonts = (value, root) => {
|
|
14311
|
+
if (typeof value === "string") {
|
|
14312
|
+
if (!isFontSlug(value)) {
|
|
14313
|
+
return null;
|
|
14314
|
+
}
|
|
14315
|
+
const def = GOOGLE_FONTS[value];
|
|
14316
|
+
return [googleOgFont(def.family, def.weights)];
|
|
14317
|
+
}
|
|
14318
|
+
if ("variants" in value) {
|
|
14319
|
+
return localOgFonts(value, root);
|
|
14320
|
+
}
|
|
14321
|
+
const remote = value;
|
|
14322
|
+
if ((remote.provider ?? "google") !== "google") {
|
|
14323
|
+
return null;
|
|
14324
|
+
}
|
|
14325
|
+
return [googleOgFont(remote.name, remote.weights ?? CARD_WEIGHTS)];
|
|
14326
|
+
};
|
|
14327
|
+
var roleFamily = (value) => {
|
|
14328
|
+
if (typeof value === "string") {
|
|
14329
|
+
return isFontSlug(value) ? GOOGLE_FONTS[value].family : null;
|
|
14330
|
+
}
|
|
14331
|
+
return value.name;
|
|
14332
|
+
};
|
|
14333
|
+
var deriveOgFonts = (fonts, root) => {
|
|
14334
|
+
const derived = [];
|
|
14335
|
+
const seen = new Set;
|
|
14336
|
+
const families = {};
|
|
14337
|
+
const roles = [
|
|
14338
|
+
["title", fonts?.display],
|
|
14339
|
+
["body", fonts?.body]
|
|
14340
|
+
];
|
|
14341
|
+
for (const [role, value] of roles) {
|
|
14342
|
+
if (value === undefined) {
|
|
14343
|
+
continue;
|
|
14344
|
+
}
|
|
14345
|
+
const roleEntries = roleFonts(value, root);
|
|
14346
|
+
if (!roleEntries) {
|
|
14347
|
+
continue;
|
|
14348
|
+
}
|
|
14349
|
+
families[role] = roleFamily(value) ?? undefined;
|
|
14350
|
+
for (const entry of roleEntries) {
|
|
14351
|
+
const key = JSON.stringify(entry);
|
|
14352
|
+
if (!seen.has(key)) {
|
|
14353
|
+
seen.add(key);
|
|
14354
|
+
derived.push(entry);
|
|
14355
|
+
}
|
|
14356
|
+
}
|
|
14357
|
+
}
|
|
14358
|
+
return {
|
|
14359
|
+
fonts: derived,
|
|
14360
|
+
...families.title || families.body ? { families } : {}
|
|
14361
|
+
};
|
|
14362
|
+
};
|
|
14363
|
+
var resolveOgFontSources = (fonts, root) => fonts.map((font) => typeof font !== "string" && ("src" in font) ? { ...font, src: absoluteSrc(root, font.src) } : font);
|
|
14364
|
+
var resolveOgFonts = (options, root) => {
|
|
14365
|
+
if (options.ogFonts) {
|
|
14366
|
+
return { fonts: resolveOgFontSources(options.ogFonts, root) };
|
|
14367
|
+
}
|
|
14368
|
+
return options.themeFontsConfigured ? deriveOgFonts(options.themeFonts, root) : { fonts: [] };
|
|
14369
|
+
};
|
|
14370
|
+
var missingFontFiles = (options, root) => {
|
|
14371
|
+
const sources = [];
|
|
14372
|
+
for (const value of Object.values(options.themeFonts ?? {})) {
|
|
14373
|
+
if (typeof value !== "string" && "variants" in value) {
|
|
14374
|
+
sources.push(...value.variants.map((variant) => absoluteSrc(root, variant.src)));
|
|
14375
|
+
}
|
|
14376
|
+
}
|
|
14377
|
+
for (const font of options.ogFonts) {
|
|
14378
|
+
if (typeof font !== "string" && "src" in font) {
|
|
14379
|
+
sources.push(absoluteSrc(root, font.src));
|
|
14380
|
+
}
|
|
14381
|
+
}
|
|
14382
|
+
return [...new Set(sources)].filter((path) => !existsSync14(path));
|
|
14383
|
+
};
|
|
14384
|
+
|
|
13412
14385
|
// src/og/logo.ts
|
|
13413
|
-
import { existsSync as
|
|
13414
|
-
import { join as
|
|
14386
|
+
import { existsSync as existsSync15, readFileSync as readFileSync7 } from "node:fs";
|
|
14387
|
+
import { join as join24 } from "pathe";
|
|
13415
14388
|
var resolveOgLogo = (project, source) => {
|
|
13416
14389
|
if (!source?.toLowerCase().endsWith(".svg")) {
|
|
13417
14390
|
return;
|
|
13418
14391
|
}
|
|
13419
|
-
const
|
|
14392
|
+
const relative13 = source.replace(/^\//u, "");
|
|
13420
14393
|
const file = [
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
].find((path) =>
|
|
14394
|
+
join24(project.context.root, "public", relative13),
|
|
14395
|
+
join24(project.context.root, relative13)
|
|
14396
|
+
].find((path) => existsSync15(path));
|
|
13424
14397
|
return file ? readFileSync7(file, "utf-8") : undefined;
|
|
13425
14398
|
};
|
|
13426
14399
|
|
|
13427
14400
|
// src/openapi/scalar.ts
|
|
13428
|
-
import { readFile as
|
|
13429
|
-
import { isAbsolute as
|
|
14401
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
14402
|
+
import { isAbsolute as isAbsolute9, join as join25 } from "pathe";
|
|
13430
14403
|
|
|
13431
14404
|
// src/theme/palette.ts
|
|
13432
14405
|
var FALLBACK_ACCENT = "oklch(0.62 0.16 250)";
|
|
@@ -13532,9 +14505,9 @@ var specConfiguration = async (spec, root) => {
|
|
|
13532
14505
|
if (URL_SPEC2.test(spec)) {
|
|
13533
14506
|
return { config: { url: spec } };
|
|
13534
14507
|
}
|
|
13535
|
-
const absolute =
|
|
14508
|
+
const absolute = isAbsolute9(spec) ? spec : join25(root, spec);
|
|
13536
14509
|
try {
|
|
13537
|
-
return { config: { content: await
|
|
14510
|
+
return { config: { content: await readFile13(absolute, "utf-8") } };
|
|
13538
14511
|
} catch {
|
|
13539
14512
|
return {
|
|
13540
14513
|
config: { url: spec },
|
|
@@ -14021,19 +14994,29 @@ blume-diff {
|
|
|
14021
14994
|
even though its chrome wrapper is not-prose. */
|
|
14022
14995
|
.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
14023
14996
|
display: block;
|
|
14024
|
-
|
|
14025
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
|
|
14997
|
+
/* Tall blocks scroll vertically in place instead of taking the page. Both
|
|
14998
|
+
axes live on the code element for the same reason: the pre stays static
|
|
14999
|
+
so the header bar and copy button never drift with the scroll. */
|
|
15000
|
+
max-height: 24rem;
|
|
15001
|
+
overflow: auto;
|
|
15002
|
+
/* The small bottom inset keeps the horizontal thumb off the last line's
|
|
15003
|
+
descenders now that scrollbars are visible. */
|
|
15004
|
+
padding: 0 1.25rem 0.375rem;
|
|
15005
|
+
/* Thin theme-colored scrollbars, matching the sidebar treatment, so a
|
|
15006
|
+
height-capped block reads as scrollable instead of simply ending.
|
|
15007
|
+
Safari before 18.2 supports neither property and falls back to the
|
|
15008
|
+
platform-default scrollbar — acceptable, since macOS overlays it. */
|
|
15009
|
+
scrollbar-color: var(--blume-border) transparent;
|
|
15010
|
+
scrollbar-width: thin;
|
|
14030
15011
|
}
|
|
14031
15012
|
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
15013
|
+
/* The dark border token is too close to the page background to read as a
|
|
15014
|
+
scrollbar thumb; derive a brighter one from the muted foreground instead. */
|
|
15015
|
+
:root[data-theme="dark"]
|
|
15016
|
+
.prose
|
|
15017
|
+
:where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
15018
|
+
scrollbar-color: color-mix(in oklab, var(--blume-muted-foreground) 55%, transparent)
|
|
15019
|
+
transparent;
|
|
14037
15020
|
}
|
|
14038
15021
|
|
|
14039
15022
|
/* Word wrap (markdown.code.wrap): long lines wrap instead of scrolling. The
|
|
@@ -14101,6 +15084,10 @@ pre.blume-source {
|
|
|
14101
15084
|
|
|
14102
15085
|
pre.blume-source > code {
|
|
14103
15086
|
flex: 1;
|
|
15087
|
+
/* The pane's measured height is authoritative (it can exceed the prose
|
|
15088
|
+
24rem cap), so undo the generic prose max-height: the code must fill
|
|
15089
|
+
whatever height the tab was given. */
|
|
15090
|
+
max-height: none;
|
|
14104
15091
|
min-height: 0;
|
|
14105
15092
|
overflow: auto;
|
|
14106
15093
|
}
|
|
@@ -14282,6 +15269,13 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
14282
15269
|
display: none !important;
|
|
14283
15270
|
}
|
|
14284
15271
|
|
|
15272
|
+
/* Paper can't scroll: uncap the code scroller so long blocks print in
|
|
15273
|
+
full, same as tab panels are force-expanded below. */
|
|
15274
|
+
.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
15275
|
+
max-height: none;
|
|
15276
|
+
overflow: visible;
|
|
15277
|
+
}
|
|
15278
|
+
|
|
14285
15279
|
#blume-content {
|
|
14286
15280
|
padding: 0 !important;
|
|
14287
15281
|
}
|
|
@@ -14525,13 +15519,13 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
|
|
|
14525
15519
|
};
|
|
14526
15520
|
|
|
14527
15521
|
// src/astro/examples.ts
|
|
14528
|
-
import { readFile as
|
|
14529
|
-
import { join as
|
|
15522
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
15523
|
+
import { join as join27, relative as relative13 } from "pathe";
|
|
14530
15524
|
import { glob as glob6 } from "tinyglobby";
|
|
14531
15525
|
|
|
14532
15526
|
// src/astro/islands.ts
|
|
14533
|
-
import { readFile as
|
|
14534
|
-
import { basename as basename2, join as
|
|
15527
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
15528
|
+
import { basename as basename2, join as join26 } from "pathe";
|
|
14535
15529
|
import { glob as glob5 } from "tinyglobby";
|
|
14536
15530
|
var DEFAULT_CLIENT = "visible";
|
|
14537
15531
|
var VALID_MODES = new Set([
|
|
@@ -14560,14 +15554,14 @@ var readClientMode = (source, file, warnings) => {
|
|
|
14560
15554
|
return mode;
|
|
14561
15555
|
};
|
|
14562
15556
|
var discoverIslands = async (root) => {
|
|
14563
|
-
const dir =
|
|
15557
|
+
const dir = join26(root, "islands");
|
|
14564
15558
|
const matches2 = await glob5(["**/*.{jsx,svelte,tsx,vue}"], {
|
|
14565
15559
|
absolute: true,
|
|
14566
15560
|
cwd: dir,
|
|
14567
15561
|
onlyFiles: true
|
|
14568
15562
|
});
|
|
14569
15563
|
const files = matches2.toSorted();
|
|
14570
|
-
const sources = await Promise.all(files.map((file) =>
|
|
15564
|
+
const sources = await Promise.all(files.map((file) => readFile14(file, "utf-8")));
|
|
14571
15565
|
const islands = [];
|
|
14572
15566
|
const warnings = [];
|
|
14573
15567
|
const seen = new Map;
|
|
@@ -14623,14 +15617,14 @@ var splitGlobBase = (pattern) => {
|
|
|
14623
15617
|
};
|
|
14624
15618
|
var discoverExamples = async (root, pattern = "examples") => {
|
|
14625
15619
|
const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
|
|
14626
|
-
const dir =
|
|
15620
|
+
const dir = join27(root, base);
|
|
14627
15621
|
const matches2 = await glob6([rest], {
|
|
14628
15622
|
absolute: true,
|
|
14629
15623
|
cwd: dir,
|
|
14630
15624
|
onlyFiles: true
|
|
14631
15625
|
});
|
|
14632
15626
|
const files = matches2.toSorted();
|
|
14633
|
-
const sources = await Promise.all(files.map((file) =>
|
|
15627
|
+
const sources = await Promise.all(files.map((file) => readFile15(file, "utf-8")));
|
|
14634
15628
|
const examples = [];
|
|
14635
15629
|
const warnings = [];
|
|
14636
15630
|
const seen = new Map;
|
|
@@ -14640,7 +15634,7 @@ var discoverExamples = async (root, pattern = "examples") => {
|
|
|
14640
15634
|
if (!(ext && framework)) {
|
|
14641
15635
|
return;
|
|
14642
15636
|
}
|
|
14643
|
-
const path =
|
|
15637
|
+
const path = relative13(dir, file).slice(0, -(ext.length + 1));
|
|
14644
15638
|
const existing = seen.get(path);
|
|
14645
15639
|
if (existing) {
|
|
14646
15640
|
warnings.push(`Two examples both resolve to "${path}" ("${existing}" and "${file}"); ignoring the second. Give them distinct paths.`);
|
|
@@ -14667,10 +15661,10 @@ var exampleMarkdownLookup = (examples) => Object.fromEntries(examples.map((examp
|
|
|
14667
15661
|
]));
|
|
14668
15662
|
|
|
14669
15663
|
// src/astro/generate.ts
|
|
14670
|
-
var BLUME_SRC =
|
|
15664
|
+
var BLUME_SRC = join28(packageRoot(), "src");
|
|
14671
15665
|
var canResolveFrom = (fromDir, spec) => {
|
|
14672
15666
|
try {
|
|
14673
|
-
createRequire5(pathToFileURL3(
|
|
15667
|
+
createRequire5(pathToFileURL3(join28(fromDir, "_.js")).href).resolve(spec);
|
|
14674
15668
|
return true;
|
|
14675
15669
|
} catch {
|
|
14676
15670
|
return false;
|
|
@@ -14681,7 +15675,7 @@ var resolveReactCompiler = (config, needsReact, pkgDir = packageRoot()) => {
|
|
|
14681
15675
|
return null;
|
|
14682
15676
|
}
|
|
14683
15677
|
try {
|
|
14684
|
-
return createRequire5(pathToFileURL3(
|
|
15678
|
+
return createRequire5(pathToFileURL3(join28(pkgDir, "_.js")).href).resolve("babel-plugin-react-compiler");
|
|
14685
15679
|
} catch {
|
|
14686
15680
|
return null;
|
|
14687
15681
|
}
|
|
@@ -14691,7 +15685,7 @@ var reactCompilerWarnings = (config, needsReact, compilerPath) => needsReact &&
|
|
|
14691
15685
|
] : [];
|
|
14692
15686
|
var resolveAstroPackageJson = (modulesDir) => {
|
|
14693
15687
|
try {
|
|
14694
|
-
return realpathSync(
|
|
15688
|
+
return realpathSync(join28(modulesDir, "astro", "package.json"));
|
|
14695
15689
|
} catch {
|
|
14696
15690
|
return null;
|
|
14697
15691
|
}
|
|
@@ -14699,12 +15693,12 @@ var resolveAstroPackageJson = (modulesDir) => {
|
|
|
14699
15693
|
var resolvedAstroHit = (fromDir) => {
|
|
14700
15694
|
let dir = normalize3(fromDir);
|
|
14701
15695
|
while (true) {
|
|
14702
|
-
const modulesDir =
|
|
15696
|
+
const modulesDir = join28(dir, "node_modules");
|
|
14703
15697
|
const pkg = resolveAstroPackageJson(modulesDir);
|
|
14704
15698
|
if (pkg) {
|
|
14705
15699
|
return { modulesDir, pkg };
|
|
14706
15700
|
}
|
|
14707
|
-
const parent =
|
|
15701
|
+
const parent = dirname10(dir);
|
|
14708
15702
|
if (parent === dir) {
|
|
14709
15703
|
return null;
|
|
14710
15704
|
}
|
|
@@ -14719,10 +15713,10 @@ var sameRealDir = (a, b) => {
|
|
|
14719
15713
|
}
|
|
14720
15714
|
};
|
|
14721
15715
|
var depsCandidates = (pkgDir) => [
|
|
14722
|
-
|
|
14723
|
-
|
|
15716
|
+
join28(pkgDir, "node_modules"),
|
|
15717
|
+
dirname10(pkgDir)
|
|
14724
15718
|
];
|
|
14725
|
-
var candidateHolding = (pkgDir, ...segments) => depsCandidates(pkgDir).find((dir) =>
|
|
15719
|
+
var candidateHolding = (pkgDir, ...segments) => depsCandidates(pkgDir).find((dir) => existsSync16(join28(dir, ...segments))) ?? null;
|
|
14726
15720
|
var linkDepsJunction = async (link, depsDir) => {
|
|
14727
15721
|
let existing;
|
|
14728
15722
|
try {
|
|
@@ -14735,13 +15729,13 @@ var linkDepsJunction = async (link, depsDir) => {
|
|
|
14735
15729
|
return;
|
|
14736
15730
|
}
|
|
14737
15731
|
try {
|
|
14738
|
-
if (
|
|
15732
|
+
if (resolve8(dirname10(link), await readlink(link)) === resolve8(depsDir)) {
|
|
14739
15733
|
return;
|
|
14740
15734
|
}
|
|
14741
15735
|
} catch {}
|
|
14742
15736
|
await rm2(link, { force: true });
|
|
14743
15737
|
}
|
|
14744
|
-
await mkdir6(
|
|
15738
|
+
await mkdir6(dirname10(link), { recursive: true });
|
|
14745
15739
|
await symlink(depsDir, link, "junction");
|
|
14746
15740
|
};
|
|
14747
15741
|
var readPkgVersion = (pkgJsonPath) => {
|
|
@@ -14774,7 +15768,7 @@ var dropStaleDepsLink = async (link, pkgDir) => {
|
|
|
14774
15768
|
let linkedBlume;
|
|
14775
15769
|
let runningBlume;
|
|
14776
15770
|
try {
|
|
14777
|
-
linkedBlume = await realpath(
|
|
15771
|
+
linkedBlume = await realpath(join28(link, "blume"));
|
|
14778
15772
|
runningBlume = await realpath(pkgDir);
|
|
14779
15773
|
} catch {
|
|
14780
15774
|
return;
|
|
@@ -14790,7 +15784,7 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
|
|
|
14790
15784
|
}
|
|
14791
15785
|
const mdxDir = candidateHolding(pkgDir, "@astrojs", "mdx");
|
|
14792
15786
|
const blumeAstro = resolveAstroPackageJson(astroDir);
|
|
14793
|
-
await dropStaleDepsLink(
|
|
15787
|
+
await dropStaleDepsLink(join28(outDir, "node_modules"), pkgDir);
|
|
14794
15788
|
const outDirHit = resolvedAstroHit(outDir);
|
|
14795
15789
|
const astroCorrect = blumeAstro !== null && outDirHit?.pkg === blumeAstro;
|
|
14796
15790
|
const walkLandsInDeps = astroCorrect && outDirHit !== null && sameRealDir(outDirHit.modulesDir, astroDir);
|
|
@@ -14798,7 +15792,7 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
|
|
|
14798
15792
|
return null;
|
|
14799
15793
|
}
|
|
14800
15794
|
if (mdxDir && (mdxDir === astroDir || astroCorrect)) {
|
|
14801
|
-
await linkDepsJunction(
|
|
15795
|
+
await linkDepsJunction(join28(outDir, "node_modules"), mdxDir);
|
|
14802
15796
|
return null;
|
|
14803
15797
|
}
|
|
14804
15798
|
return astroConflictWarning(blumeAstro, outDirHit?.pkg ?? null);
|
|
@@ -14839,14 +15833,14 @@ var searchProviderWarnings = (provider, root, pkgDir = packageRoot()) => {
|
|
|
14839
15833
|
}
|
|
14840
15834
|
return warnings;
|
|
14841
15835
|
};
|
|
14842
|
-
var examplesCssFile = (root, config) => config.examples.css ?
|
|
15836
|
+
var examplesCssFile = (root, config) => config.examples.css ? join28(root, config.examples.css) : null;
|
|
14843
15837
|
var writeExamplesPreview = async (options) => {
|
|
14844
15838
|
const { config, hasExamples, root, srcDir, write } = options;
|
|
14845
15839
|
if (hasExamples) {
|
|
14846
|
-
await write(
|
|
15840
|
+
await write(join28(srcDir, "pages", ...config.basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro"), examplesPageTemplate());
|
|
14847
15841
|
}
|
|
14848
15842
|
const cssFile = examplesCssFile(root, config);
|
|
14849
|
-
return cssFile && !
|
|
15843
|
+
return cssFile && !existsSync16(cssFile) ? [
|
|
14850
15844
|
`examples.css points at "${config.examples.css}", which doesn't exist; previews render without it.`
|
|
14851
15845
|
] : [];
|
|
14852
15846
|
};
|
|
@@ -14855,7 +15849,7 @@ var readOptional = async (path) => {
|
|
|
14855
15849
|
return "";
|
|
14856
15850
|
}
|
|
14857
15851
|
try {
|
|
14858
|
-
return await
|
|
15852
|
+
return await readFile16(path, "utf-8");
|
|
14859
15853
|
} catch {
|
|
14860
15854
|
return "";
|
|
14861
15855
|
}
|
|
@@ -14875,30 +15869,30 @@ var detectUsesMath = async (root, staged = []) => {
|
|
|
14875
15869
|
ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
|
|
14876
15870
|
onlyFiles: true
|
|
14877
15871
|
});
|
|
14878
|
-
const contents = await Promise.all(files.map((file) => readOptional(
|
|
15872
|
+
const contents = await Promise.all(files.map((file) => readOptional(join28(root, file))));
|
|
14879
15873
|
return [...contents, ...staged].some(containsMath);
|
|
14880
15874
|
};
|
|
14881
|
-
var hashConfigSource = (source) =>
|
|
15875
|
+
var hashConfigSource = (source) => createHash3("sha256").update(source).digest("hex");
|
|
14882
15876
|
var loadIntegrationBridge = async (config, context) => {
|
|
14883
15877
|
if (config.integrations.length === 0 || !context.configFile) {
|
|
14884
15878
|
return;
|
|
14885
15879
|
}
|
|
14886
15880
|
return {
|
|
14887
|
-
configFile:
|
|
15881
|
+
configFile: relative14(context.outDir, context.configFile),
|
|
14888
15882
|
sourceHash: hashConfigSource(await readOptional(context.configFile))
|
|
14889
15883
|
};
|
|
14890
15884
|
};
|
|
14891
15885
|
var writeIfChanged = async (path, content) => {
|
|
14892
15886
|
let existing = null;
|
|
14893
15887
|
try {
|
|
14894
|
-
existing = await
|
|
15888
|
+
existing = await readFile16(path, "utf-8");
|
|
14895
15889
|
} catch {
|
|
14896
15890
|
existing = null;
|
|
14897
15891
|
}
|
|
14898
15892
|
if (existing === content) {
|
|
14899
15893
|
return false;
|
|
14900
15894
|
}
|
|
14901
|
-
await mkdir6(
|
|
15895
|
+
await mkdir6(dirname10(path), { recursive: true });
|
|
14902
15896
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
14903
15897
|
await writeFile7(tmp, content, "utf-8");
|
|
14904
15898
|
try {
|
|
@@ -14936,12 +15930,12 @@ var collectStaged = (project) => {
|
|
|
14936
15930
|
var writeStagedContent = async (out, staged) => {
|
|
14937
15931
|
const contentDir = stagedContentDir(out);
|
|
14938
15932
|
const written = new Set;
|
|
14939
|
-
await Promise.all([...staged].map(async ([entryId,
|
|
14940
|
-
const path =
|
|
15933
|
+
await Promise.all([...staged].map(async ([entryId, text2]) => {
|
|
15934
|
+
const path = join28(contentDir, entryId);
|
|
14941
15935
|
written.add(normalize3(path));
|
|
14942
|
-
await writeIfChanged(path,
|
|
15936
|
+
await writeIfChanged(path, text2);
|
|
14943
15937
|
}));
|
|
14944
|
-
if (
|
|
15938
|
+
if (existsSync16(contentDir)) {
|
|
14945
15939
|
await pruneOrphans(contentDir, written);
|
|
14946
15940
|
}
|
|
14947
15941
|
};
|
|
@@ -14972,9 +15966,9 @@ var readLogoSvg = (project, source) => {
|
|
|
14972
15966
|
}
|
|
14973
15967
|
const rel = source.replace(/^\//u, "");
|
|
14974
15968
|
const file = [
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
].find((path) =>
|
|
15969
|
+
join28(project.context.root, "public", rel),
|
|
15970
|
+
join28(project.context.root, rel)
|
|
15971
|
+
].find((path) => existsSync16(path));
|
|
14978
15972
|
return file ? readFileSync9(file, "utf-8") : undefined;
|
|
14979
15973
|
};
|
|
14980
15974
|
var resolveLogo = (project) => {
|
|
@@ -14983,7 +15977,7 @@ var resolveLogo = (project) => {
|
|
|
14983
15977
|
return null;
|
|
14984
15978
|
}
|
|
14985
15979
|
const config = typeof logo === "string" ? { image: logo } : logo;
|
|
14986
|
-
const { href, image: source, text } = config;
|
|
15980
|
+
const { href, image: source, text: text2 } = config;
|
|
14987
15981
|
const image = typeof source === "string" ? { light: source } : source;
|
|
14988
15982
|
const light = image?.light ?? image?.dark;
|
|
14989
15983
|
const dark = image?.dark ?? image?.light;
|
|
@@ -14992,12 +15986,12 @@ var resolveLogo = (project) => {
|
|
|
14992
15986
|
const lightSvg = readLogoSvg(project, light);
|
|
14993
15987
|
const darkSvg = dark === light ? lightSvg : readLogoSvg(project, dark);
|
|
14994
15988
|
if (light && light === dark && lightSvg) {
|
|
14995
|
-
return { alt, href: brandHref, svg: lightSvg, text };
|
|
15989
|
+
return { alt, href: brandHref, svg: lightSvg, text: text2 };
|
|
14996
15990
|
}
|
|
14997
15991
|
const lightDimensions = svgDimensions(lightSvg);
|
|
14998
15992
|
const darkDimensions = svgDimensions(darkSvg);
|
|
14999
15993
|
const dimensions = lightDimensions || darkDimensions ? { dark: darkDimensions, light: lightDimensions } : undefined;
|
|
15000
|
-
return { alt, dark, dimensions, href: brandHref, light, text };
|
|
15994
|
+
return { alt, dark, dimensions, href: brandHref, light, text: text2 };
|
|
15001
15995
|
};
|
|
15002
15996
|
var FAVICON_CANDIDATES = [
|
|
15003
15997
|
"icon.svg",
|
|
@@ -15020,7 +16014,7 @@ var faviconType = (name) => {
|
|
|
15020
16014
|
};
|
|
15021
16015
|
var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync9(file).toString("base64")}`;
|
|
15022
16016
|
var defaultFavicon = () => ({
|
|
15023
|
-
href: inlineDataUri(
|
|
16017
|
+
href: inlineDataUri(join28(BLUME_SRC, "assets", "icon.png"), "image/png"),
|
|
15024
16018
|
type: "image/png"
|
|
15025
16019
|
});
|
|
15026
16020
|
var APPLE_ICON_CANDIDATES = [
|
|
@@ -15032,13 +16026,13 @@ var APPLE_ICON_CANDIDATES = [
|
|
|
15032
16026
|
var resolveIconFile = (project, candidates) => {
|
|
15033
16027
|
const { root } = project.context;
|
|
15034
16028
|
for (const name of candidates) {
|
|
15035
|
-
if (
|
|
16029
|
+
if (existsSync16(join28(root, "public", name))) {
|
|
15036
16030
|
return { href: `/${name}`, type: faviconType(name) };
|
|
15037
16031
|
}
|
|
15038
16032
|
}
|
|
15039
16033
|
for (const name of candidates) {
|
|
15040
|
-
const file =
|
|
15041
|
-
if (
|
|
16034
|
+
const file = join28(root, name);
|
|
16035
|
+
if (existsSync16(file)) {
|
|
15042
16036
|
const type = faviconType(name);
|
|
15043
16037
|
return { href: inlineDataUri(file, type ?? "image/x-icon"), type };
|
|
15044
16038
|
}
|
|
@@ -15062,18 +16056,45 @@ var resolveBanner = (config) => {
|
|
|
15062
16056
|
link: banner.link
|
|
15063
16057
|
};
|
|
15064
16058
|
};
|
|
16059
|
+
var resolveOgMark = (project, siteLogo) => {
|
|
16060
|
+
const configured = project.config.seo.og.logo;
|
|
16061
|
+
if (configured === false) {
|
|
16062
|
+
return false;
|
|
16063
|
+
}
|
|
16064
|
+
if (configured) {
|
|
16065
|
+
return resolveOgLogo(project, configured);
|
|
16066
|
+
}
|
|
16067
|
+
return siteLogo;
|
|
16068
|
+
};
|
|
16069
|
+
var resolveOgSite = (config) => {
|
|
16070
|
+
const configured = config.seo.og.site;
|
|
16071
|
+
if (configured === false) {
|
|
16072
|
+
return;
|
|
16073
|
+
}
|
|
16074
|
+
if (configured !== undefined) {
|
|
16075
|
+
return configured;
|
|
16076
|
+
}
|
|
16077
|
+
return config.deployment.site ? `${new URL(config.deployment.site).host}${normalizeBasePath(config.deployment.base)}` : undefined;
|
|
16078
|
+
};
|
|
16079
|
+
var resolveOgDescription = (config) => {
|
|
16080
|
+
const configured = config.seo.og.description;
|
|
16081
|
+
if (configured === false) {
|
|
16082
|
+
return;
|
|
16083
|
+
}
|
|
16084
|
+
return configured ?? config.description;
|
|
16085
|
+
};
|
|
15065
16086
|
var buildRuntimeData = (project) => {
|
|
15066
16087
|
const { config, context, graph, manifest } = project;
|
|
15067
16088
|
const { github } = config;
|
|
15068
16089
|
const repoUrl = github ? `https://github.com/${github.owner}/${github.repo}` : null;
|
|
15069
16090
|
const editBase = github ? `${repoUrl}/edit/${github.branch}` : null;
|
|
15070
16091
|
const logo = resolveLogo(project);
|
|
15071
|
-
const ogLogo =
|
|
16092
|
+
const ogLogo = resolveOgMark(project, logo?.svg);
|
|
15072
16093
|
const editUrlFor = (sourcePath) => {
|
|
15073
16094
|
if (!(editBase && sourcePath)) {
|
|
15074
16095
|
return null;
|
|
15075
16096
|
}
|
|
15076
|
-
const rel =
|
|
16097
|
+
const rel = relative14(context.root, sourcePath).split("\\").join("/");
|
|
15077
16098
|
const editPath = github?.dir ? `${github.dir}/${rel}` : rel;
|
|
15078
16099
|
return `${editBase}/${editPath}`;
|
|
15079
16100
|
};
|
|
@@ -15135,10 +16156,11 @@ var buildRuntimeData = (project) => {
|
|
|
15135
16156
|
route: config.ai.mcp.route
|
|
15136
16157
|
} : null,
|
|
15137
16158
|
og: {
|
|
16159
|
+
description: resolveOgDescription(config),
|
|
15138
16160
|
enabled: config.seo.og.enabled ?? false,
|
|
15139
|
-
fonts: config.seo.og.fonts ?? [],
|
|
15140
16161
|
logo: ogLogo,
|
|
15141
|
-
palette: config.seo.og.palette
|
|
16162
|
+
palette: config.seo.og.palette,
|
|
16163
|
+
site: resolveOgSite(config)
|
|
15142
16164
|
},
|
|
15143
16165
|
repoUrl,
|
|
15144
16166
|
search: {
|
|
@@ -15151,6 +16173,10 @@ var buildRuntimeData = (project) => {
|
|
|
15151
16173
|
theme: config.theme,
|
|
15152
16174
|
title: config.title,
|
|
15153
16175
|
toc: config.toc,
|
|
16176
|
+
webmcp: {
|
|
16177
|
+
enabled: config.ai.webmcp,
|
|
16178
|
+
llms: config.ai.llmsTxt.enabled
|
|
16179
|
+
},
|
|
15154
16180
|
x: config.seo.x
|
|
15155
16181
|
},
|
|
15156
16182
|
feeds: buildRssFeeds(project).map((feed) => ({
|
|
@@ -15184,7 +16210,7 @@ var buildRuntimeData = (project) => {
|
|
|
15184
16210
|
var planMcp = (project, srcDir, userPages) => {
|
|
15185
16211
|
const { config } = project;
|
|
15186
16212
|
const { route } = config.ai.mcp;
|
|
15187
|
-
const dir =
|
|
16213
|
+
const dir = join28(srcDir, "blume-mcp");
|
|
15188
16214
|
const base = {
|
|
15189
16215
|
dir,
|
|
15190
16216
|
discoveryPages: [],
|
|
@@ -15208,11 +16234,11 @@ var planMcp = (project, srcDir, userPages) => {
|
|
|
15208
16234
|
...base,
|
|
15209
16235
|
discoveryPages: [
|
|
15210
16236
|
{
|
|
15211
|
-
entrypoint:
|
|
16237
|
+
entrypoint: join28(dir, "discovery.ts"),
|
|
15212
16238
|
pattern: "/.well-known/mcp.json"
|
|
15213
16239
|
},
|
|
15214
16240
|
{
|
|
15215
|
-
entrypoint:
|
|
16241
|
+
entrypoint: join28(dir, "server-card.ts"),
|
|
15216
16242
|
pattern: "/.well-known/mcp/server-card.json"
|
|
15217
16243
|
}
|
|
15218
16244
|
],
|
|
@@ -15232,11 +16258,11 @@ var writeMcpFiles = async (project, plan, write) => {
|
|
|
15232
16258
|
version: data.version
|
|
15233
16259
|
};
|
|
15234
16260
|
await Promise.all([
|
|
15235
|
-
write(
|
|
16261
|
+
write(join28(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
|
|
15236
16262
|
`),
|
|
15237
|
-
write(
|
|
15238
|
-
write(
|
|
15239
|
-
write(
|
|
16263
|
+
write(join28(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
|
|
16264
|
+
write(join28(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
|
|
16265
|
+
write(join28(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
|
|
15240
16266
|
]);
|
|
15241
16267
|
};
|
|
15242
16268
|
var writeAskFiles = async (project, srcDir, write) => {
|
|
@@ -15246,20 +16272,20 @@ var writeAskFiles = async (project, srcDir, write) => {
|
|
|
15246
16272
|
}
|
|
15247
16273
|
const grounded = ask.provider !== "inkeep";
|
|
15248
16274
|
if (grounded) {
|
|
15249
|
-
await write(
|
|
16275
|
+
await write(join28(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
|
|
15250
16276
|
`);
|
|
15251
16277
|
}
|
|
15252
|
-
await write(
|
|
16278
|
+
await write(join28(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
|
|
15253
16279
|
};
|
|
15254
16280
|
var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
15255
16281
|
if (routeIsTaken(pages, contentPages, "/404")) {
|
|
15256
16282
|
return;
|
|
15257
16283
|
}
|
|
15258
|
-
await write(
|
|
16284
|
+
await write(join28(srcDir, "pages", "404.astro"), notFoundPageTemplate());
|
|
15259
16285
|
};
|
|
15260
16286
|
var diagnosticWarning = (diagnostic) => diagnostic.suggestion ? `${diagnostic.message} ${diagnostic.suggestion}` : diagnostic.message;
|
|
15261
16287
|
var buildComponentSlots = async (componentsFile) => {
|
|
15262
|
-
const analysis = componentsFile ? analyzeComponentOverrides(await
|
|
16288
|
+
const analysis = componentsFile ? analyzeComponentOverrides(await readFile16(componentsFile, "utf-8"), componentsFile) : null;
|
|
15263
16289
|
return {
|
|
15264
16290
|
plan: planComponentSlots(componentsFile, analysis),
|
|
15265
16291
|
tags: analysis ? [...analysis.mdx, ...analysis.islands].map((entry) => entry.key) : [],
|
|
@@ -15267,17 +16293,34 @@ var buildComponentSlots = async (componentsFile) => {
|
|
|
15267
16293
|
};
|
|
15268
16294
|
};
|
|
15269
16295
|
var contentWatchesRuntimeDir = (hasFilesystemSource, collectionBase, context) => hasFilesystemSource && runtimeDirWithin(collectionBase, context.outDir) !== null;
|
|
16296
|
+
var projectOgFonts = (project) => resolveOgFonts({
|
|
16297
|
+
ogFonts: project.config.seo.og.fonts,
|
|
16298
|
+
themeFonts: project.config.theme.fonts,
|
|
16299
|
+
themeFontsConfigured: project.themeFontsConfigured
|
|
16300
|
+
}, project.context.root);
|
|
16301
|
+
var assertFontFilesExist = (project) => {
|
|
16302
|
+
const { config, context } = project;
|
|
16303
|
+
const missing = missingFontFiles({ ogFonts: config.seo.og.fonts ?? [], themeFonts: config.theme.fonts }, context.root);
|
|
16304
|
+
if (missing.length > 0) {
|
|
16305
|
+
throw new BlumeError({
|
|
16306
|
+
code: "BLUME_FONT_FILE_MISSING",
|
|
16307
|
+
message: `Configured font file(s) not found: ${missing.join(", ")}. Font paths resolve relative to the project root.`,
|
|
16308
|
+
severity: "error"
|
|
16309
|
+
});
|
|
16310
|
+
}
|
|
16311
|
+
};
|
|
15270
16312
|
var generateRuntime = async (project) => {
|
|
15271
16313
|
const { context, config } = project;
|
|
16314
|
+
assertFontFilesExist(project);
|
|
15272
16315
|
const out = context.outDir;
|
|
15273
|
-
const srcDir =
|
|
15274
|
-
const askPath =
|
|
15275
|
-
const dataPath =
|
|
15276
|
-
const themePath =
|
|
15277
|
-
const searchClientPath =
|
|
15278
|
-
const examplesPath =
|
|
15279
|
-
const examplesThemePath =
|
|
15280
|
-
const openapiPath =
|
|
16316
|
+
const srcDir = join28(out, "src");
|
|
16317
|
+
const askPath = join28(srcDir, "generated", "Ask.astro");
|
|
16318
|
+
const dataPath = join28(srcDir, "generated", "data.json");
|
|
16319
|
+
const themePath = join28(srcDir, "generated", "app.css");
|
|
16320
|
+
const searchClientPath = join28(srcDir, "generated", "search-client.ts");
|
|
16321
|
+
const examplesPath = join28(srcDir, "generated", "examples.ts");
|
|
16322
|
+
const examplesThemePath = join28(srcDir, "generated", "examples.css");
|
|
16323
|
+
const openapiPath = join28(srcDir, "generated", "openapi.json");
|
|
15281
16324
|
const written = new Set;
|
|
15282
16325
|
const write = (path, content) => {
|
|
15283
16326
|
written.add(normalize3(path));
|
|
@@ -15332,11 +16375,11 @@ var generateRuntime = async (project) => {
|
|
|
15332
16375
|
const docsCollection = resolveDocsCollection(config, context);
|
|
15333
16376
|
const [structural] = await Promise.all([
|
|
15334
16377
|
Promise.all([
|
|
15335
|
-
write(
|
|
16378
|
+
write(join28(out, "astro.config.mjs"), astroConfigTemplate({
|
|
15336
16379
|
aliases: resolveTsconfigAliases(context.root),
|
|
15337
16380
|
askPath,
|
|
15338
16381
|
config,
|
|
15339
|
-
contentRoutes: project
|
|
16382
|
+
contentRoutes: markdownRoutePaths(project),
|
|
15340
16383
|
contentWatchesRuntimeDir: contentWatchesRuntimeDir(hasFilesystemSource, docsCollection.base, context),
|
|
15341
16384
|
context,
|
|
15342
16385
|
dataPath,
|
|
@@ -15352,26 +16395,26 @@ var generateRuntime = async (project) => {
|
|
|
15352
16395
|
searchClientPath,
|
|
15353
16396
|
themePath
|
|
15354
16397
|
})),
|
|
15355
|
-
write(
|
|
15356
|
-
write(
|
|
15357
|
-
write(
|
|
15358
|
-
write(
|
|
16398
|
+
write(join28(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
|
|
16399
|
+
write(join28(out, "tsconfig.json"), runtimeTsconfigTemplate()),
|
|
16400
|
+
write(join28(srcDir, "env.d.ts"), envTemplate()),
|
|
16401
|
+
write(join28(srcDir, "content.config.ts"), contentConfigTemplate({
|
|
15359
16402
|
collection: docsCollection,
|
|
15360
16403
|
config,
|
|
15361
16404
|
context,
|
|
15362
16405
|
filesystem: hasFilesystemSource,
|
|
15363
16406
|
staged: hasStaged
|
|
15364
16407
|
})),
|
|
15365
|
-
write(
|
|
16408
|
+
write(join28(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
|
|
15366
16409
|
exportEpub,
|
|
15367
16410
|
exportPdf,
|
|
15368
16411
|
mathEnabled: usesMath,
|
|
15369
16412
|
needsReact
|
|
15370
16413
|
})),
|
|
15371
16414
|
write(askPath, askComponentTemplate(askEnabled)),
|
|
15372
|
-
write(
|
|
15373
|
-
write(
|
|
15374
|
-
write(
|
|
16415
|
+
write(join28(srcDir, "generated", "components.ts"), slotPlan.module),
|
|
16416
|
+
write(join28(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
|
|
16417
|
+
write(join28(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples, config.basePath)),
|
|
15375
16418
|
write(examplesThemePath, examplesEntryTemplate({
|
|
15376
16419
|
configTokens: buildThemeCss(config.theme),
|
|
15377
16420
|
sources: [`${context.root}/**/*.{astro,jsx,svelte,ts,tsx,vue}`],
|
|
@@ -15387,17 +16430,17 @@ var generateRuntime = async (project) => {
|
|
|
15387
16430
|
userTheme
|
|
15388
16431
|
}))
|
|
15389
16432
|
]),
|
|
15390
|
-
Promise.all(islandDiscovery.islands.map((island) => write(
|
|
15391
|
-
Promise.all(slotPlan.wrappers.map((wrapper) => write(
|
|
15392
|
-
Promise.all(exampleDiscovery.examples.map((example) => write(
|
|
16433
|
+
Promise.all(islandDiscovery.islands.map((island) => write(join28(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island)))),
|
|
16434
|
+
Promise.all(slotPlan.wrappers.map((wrapper) => write(join28(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content))),
|
|
16435
|
+
Promise.all(exampleDiscovery.examples.map((example) => write(join28(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example)))),
|
|
15393
16436
|
writeAskFiles(project, srcDir, write),
|
|
15394
16437
|
writeMcpFiles(project, mcp, write)
|
|
15395
16438
|
]);
|
|
15396
16439
|
if (config.seo.og.enabled) {
|
|
15397
|
-
await write(
|
|
16440
|
+
await write(join28(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes, projectOgFonts(project)));
|
|
15398
16441
|
}
|
|
15399
16442
|
if (hasGeneratedChangelog(project, pages)) {
|
|
15400
|
-
await write(
|
|
16443
|
+
await write(join28(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
|
|
15401
16444
|
exportEpub,
|
|
15402
16445
|
exportPdf,
|
|
15403
16446
|
needsReact,
|
|
@@ -15417,27 +16460,31 @@ var generateRuntime = async (project) => {
|
|
|
15417
16460
|
]);
|
|
15418
16461
|
if (servesStaticIndex(config.search.provider)) {
|
|
15419
16462
|
const documents = await buildSearchDocuments(project);
|
|
15420
|
-
await write(
|
|
16463
|
+
await write(join28(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
|
|
15421
16464
|
`);
|
|
15422
|
-
await write(
|
|
16465
|
+
await write(join28(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
|
|
15423
16466
|
}
|
|
15424
16467
|
if (config.search.provider === "mixedbread") {
|
|
15425
|
-
await write(
|
|
16468
|
+
await write(join28(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
|
|
15426
16469
|
}
|
|
15427
16470
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
16471
|
+
const contentAssets = await collectContentAssets(project);
|
|
15428
16472
|
await Promise.all([
|
|
15429
|
-
write(
|
|
16473
|
+
write(join28(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
|
|
15430
16474
|
`),
|
|
15431
|
-
write(
|
|
15432
|
-
write(
|
|
16475
|
+
write(join28(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate("md")),
|
|
16476
|
+
write(join28(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate("mdx")),
|
|
16477
|
+
write(join28(srcDir, "generated", "content-assets.json"), `${JSON.stringify(contentAssets)}
|
|
16478
|
+
`),
|
|
16479
|
+
write(join28(srcDir, "pages", "blume-assets", "[...asset].ts"), contentAssetsEndpointTemplate(join28(project.context.outDir, "public", "blume-assets")))
|
|
15433
16480
|
]);
|
|
15434
16481
|
const feeds = buildRssFeeds(project);
|
|
15435
16482
|
if (feeds.length > 0) {
|
|
15436
16483
|
const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
|
|
15437
16484
|
await Promise.all([
|
|
15438
|
-
write(
|
|
16485
|
+
write(join28(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
|
|
15439
16486
|
`),
|
|
15440
|
-
write(
|
|
16487
|
+
write(join28(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
|
|
15441
16488
|
]);
|
|
15442
16489
|
}
|
|
15443
16490
|
const warnings = [
|
|
@@ -15473,14 +16520,14 @@ var generateRuntime = async (project) => {
|
|
|
15473
16520
|
root: context.root
|
|
15474
16521
|
});
|
|
15475
16522
|
warnings.push(...references.warnings);
|
|
15476
|
-
await Promise.all(references.files.map((file) => write(
|
|
16523
|
+
await Promise.all(references.files.map((file) => write(join28(srcDir, "pages", file.pagePath), file.content)));
|
|
15477
16524
|
}
|
|
15478
16525
|
const openApiSource2 = project.sources.find(isOpenApiSource);
|
|
15479
16526
|
await Promise.all([
|
|
15480
|
-
write(
|
|
16527
|
+
write(join28(srcDir, "generated", "data.json"), buildRuntimeData(project)),
|
|
15481
16528
|
write(openapiPath, `${JSON.stringify(openApiSource2 ? openApiSource2.openApiData() : {})}
|
|
15482
16529
|
`),
|
|
15483
|
-
write(
|
|
16530
|
+
write(join28(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
|
|
15484
16531
|
`),
|
|
15485
16532
|
writeStagedContent(out, staged)
|
|
15486
16533
|
]);
|
|
@@ -15489,8 +16536,8 @@ var generateRuntime = async (project) => {
|
|
|
15489
16536
|
};
|
|
15490
16537
|
|
|
15491
16538
|
// src/cli/env.ts
|
|
15492
|
-
import { existsSync as
|
|
15493
|
-
import { dirname as
|
|
16539
|
+
import { existsSync as existsSync17, readFileSync as readFileSync10 } from "node:fs";
|
|
16540
|
+
import { dirname as dirname11, join as join29, resolve as resolve9 } from "pathe";
|
|
15494
16541
|
var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
|
|
15495
16542
|
var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
|
|
15496
16543
|
var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
|
|
@@ -15536,19 +16583,19 @@ var applyEnv = (parsed) => {
|
|
|
15536
16583
|
};
|
|
15537
16584
|
var loadFile = (path) => {
|
|
15538
16585
|
try {
|
|
15539
|
-
if (
|
|
16586
|
+
if (existsSync17(path)) {
|
|
15540
16587
|
applyEnv(parseEnv(readFileSync10(path, "utf-8")));
|
|
15541
16588
|
}
|
|
15542
16589
|
} catch {}
|
|
15543
16590
|
};
|
|
15544
16591
|
var loadEnvFiles = (startDir) => {
|
|
15545
|
-
let dir =
|
|
16592
|
+
let dir = resolve9(startDir);
|
|
15546
16593
|
let done = false;
|
|
15547
16594
|
while (!done) {
|
|
15548
|
-
loadFile(
|
|
15549
|
-
loadFile(
|
|
15550
|
-
const parent =
|
|
15551
|
-
done =
|
|
16595
|
+
loadFile(join29(dir, ".env.local"));
|
|
16596
|
+
loadFile(join29(dir, ".env"));
|
|
16597
|
+
const parent = dirname11(dir);
|
|
16598
|
+
done = existsSync17(join29(dir, ".git")) || parent === dir;
|
|
15552
16599
|
dir = parent;
|
|
15553
16600
|
}
|
|
15554
16601
|
};
|
|
@@ -15653,20 +16700,94 @@ var emitRedirectFiles = async (config, distDir) => {
|
|
|
15653
16700
|
if (redirects.length === 0 || config.deployment.output !== "static") {
|
|
15654
16701
|
return;
|
|
15655
16702
|
}
|
|
15656
|
-
await writeFile8(
|
|
16703
|
+
await writeFile8(join30(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
|
|
15657
16704
|
const platformFiles = [
|
|
15658
16705
|
{ content: buildNetlifyRedirects(redirects), name: "_redirects" },
|
|
15659
16706
|
{ content: buildVercelConfig(redirects), name: "vercel.json" }
|
|
15660
16707
|
];
|
|
15661
|
-
await Promise.all(platformFiles.map((file) =>
|
|
16708
|
+
await Promise.all(platformFiles.map((file) => existsSync18(join30(distDir, file.name)) ? Promise.resolve() : writeFile8(join30(distDir, file.name), file.content, "utf-8")));
|
|
15662
16709
|
logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
|
|
15663
16710
|
};
|
|
15664
|
-
var emitHeaderFiles = async (
|
|
15665
|
-
|
|
16711
|
+
var emitHeaderFiles = async (project, distDir) => {
|
|
16712
|
+
const { config } = project;
|
|
16713
|
+
if (config.deployment.output !== "static" || existsSync18(join30(distDir, "_headers"))) {
|
|
16714
|
+
return;
|
|
16715
|
+
}
|
|
16716
|
+
await writeFile8(join30(distDir, "_headers"), buildNetlifyHeaders(config, buildHomeLinkHeader(config, markdownRoutePaths(project))), "utf-8");
|
|
16717
|
+
logger.success("Emitted _headers (UTF-8 Content-Type + homepage Link header)");
|
|
16718
|
+
};
|
|
16719
|
+
var emitAgentSkills = async (project, distDir) => {
|
|
16720
|
+
const configured = project.config.ai.skills;
|
|
16721
|
+
if (!configured) {
|
|
15666
16722
|
return;
|
|
15667
16723
|
}
|
|
15668
|
-
|
|
15669
|
-
|
|
16724
|
+
const dir = resolve10(project.context.root, configured);
|
|
16725
|
+
if (!existsSync18(dir)) {
|
|
16726
|
+
logger.warn(`ai.skills points at "${configured}" (${dir}), which does not exist; no skills published.`);
|
|
16727
|
+
return;
|
|
16728
|
+
}
|
|
16729
|
+
const outDir = join30(distDir, AGENT_SKILLS_DIR.slice(1));
|
|
16730
|
+
if (existsSync18(join30(outDir, "index.json"))) {
|
|
16731
|
+
return;
|
|
16732
|
+
}
|
|
16733
|
+
const { skills, warnings } = await collectSkills(dir);
|
|
16734
|
+
for (const warning of warnings) {
|
|
16735
|
+
logger.warn(warning);
|
|
16736
|
+
}
|
|
16737
|
+
if (skills.length === 0) {
|
|
16738
|
+
logger.warn(`ai.skills: no publishable skills found in "${configured}".`);
|
|
16739
|
+
return;
|
|
16740
|
+
}
|
|
16741
|
+
await Promise.all(skills.map(async (skill) => {
|
|
16742
|
+
const target = join30(outDir, skill.path);
|
|
16743
|
+
await mkdir7(dirname12(target), { recursive: true });
|
|
16744
|
+
await writeFile8(target, skill.content);
|
|
16745
|
+
}));
|
|
16746
|
+
await writeFile8(join30(outDir, "index.json"), buildSkillsIndex(skills, project.config), "utf-8");
|
|
16747
|
+
logger.success(`Published ${skills.length} agent skill(s) (.well-known/agent-skills/index.json)`);
|
|
16748
|
+
};
|
|
16749
|
+
var emitWellKnownFiles = async (config, distDir) => {
|
|
16750
|
+
const files = [
|
|
16751
|
+
{
|
|
16752
|
+
content: buildSignaturesDirectory(config),
|
|
16753
|
+
label: "Web Bot Auth",
|
|
16754
|
+
path: SIGNATURES_DIRECTORY_PATH
|
|
16755
|
+
},
|
|
16756
|
+
{
|
|
16757
|
+
content: buildApiCatalog(config),
|
|
16758
|
+
label: "RFC 9727",
|
|
16759
|
+
path: API_CATALOG_PATH
|
|
16760
|
+
}
|
|
16761
|
+
];
|
|
16762
|
+
for (const file of files) {
|
|
16763
|
+
const target = join30(distDir, file.path.slice(1));
|
|
16764
|
+
if (!file.content || existsSync18(target)) {
|
|
16765
|
+
continue;
|
|
16766
|
+
}
|
|
16767
|
+
await mkdir7(join30(distDir, ".well-known"), { recursive: true });
|
|
16768
|
+
await writeFile8(target, file.content, "utf-8");
|
|
16769
|
+
logger.success(`Generated ${file.path.slice(1)} (${file.label})`);
|
|
16770
|
+
}
|
|
16771
|
+
};
|
|
16772
|
+
var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
16773
|
+
const { config } = project;
|
|
16774
|
+
const configPath = join30(root, ".vercel", "output", "config.json");
|
|
16775
|
+
if (!existsSync18(configPath)) {
|
|
16776
|
+
return;
|
|
16777
|
+
}
|
|
16778
|
+
const overrides = {
|
|
16779
|
+
...hasApiCatalog(config) ? { [API_CATALOG_PATH.slice(1)]: API_CATALOG_TYPE } : {},
|
|
16780
|
+
...config.ai.webBotAuth.keys.length > 0 ? { [SIGNATURES_DIRECTORY_PATH.slice(1)]: SIGNATURES_DIRECTORY_TYPE } : {}
|
|
16781
|
+
};
|
|
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);
|
|
16785
|
+
if (injected === null) {
|
|
16786
|
+
logger.warn("Could not wire Accept: text/markdown negotiation into .vercel/output/config.json — raw Markdown stays available at the .md URLs.");
|
|
16787
|
+
return;
|
|
16788
|
+
}
|
|
16789
|
+
await writeFile8(configPath, injected, "utf-8");
|
|
16790
|
+
logger.success("Wired Accept: text/markdown negotiation into the Vercel routing config");
|
|
15670
16791
|
};
|
|
15671
16792
|
var formatBytes2 = (bytes) => {
|
|
15672
16793
|
if (bytes < 1024) {
|
|
@@ -15676,14 +16797,14 @@ var formatBytes2 = (bytes) => {
|
|
|
15676
16797
|
return `${(bytes / 1024).toFixed(digits)} kB`;
|
|
15677
16798
|
};
|
|
15678
16799
|
var astroAssets = async (distDir, ext) => {
|
|
15679
|
-
const astroDir =
|
|
15680
|
-
if (!
|
|
16800
|
+
const astroDir = join30(distDir, "_astro");
|
|
16801
|
+
if (!existsSync18(astroDir)) {
|
|
15681
16802
|
return [];
|
|
15682
16803
|
}
|
|
15683
|
-
const entries = await
|
|
16804
|
+
const entries = await readdir2(astroDir);
|
|
15684
16805
|
const files = entries.filter((name) => name.endsWith(`.${ext}`));
|
|
15685
16806
|
const sized = await Promise.all(files.map(async (name) => {
|
|
15686
|
-
const info = await
|
|
16807
|
+
const info = await stat3(join30(astroDir, name));
|
|
15687
16808
|
return { name, size: info.size };
|
|
15688
16809
|
}));
|
|
15689
16810
|
return sized.toSorted((a, b) => b.size - a.size);
|
|
@@ -15736,26 +16857,26 @@ var runClientAssetChecks = async (staticDir, args) => {
|
|
|
15736
16857
|
var isolatedOutputDir = (config, context) => {
|
|
15737
16858
|
const { adapter, output } = config.deployment;
|
|
15738
16859
|
if (output === "server" && adapter === "vercel") {
|
|
15739
|
-
return
|
|
16860
|
+
return join30(context.outDir, ".vercel", "output");
|
|
15740
16861
|
}
|
|
15741
|
-
return context.distDir ??
|
|
16862
|
+
return context.distDir ?? join30(context.outDir, "dist");
|
|
15742
16863
|
};
|
|
15743
16864
|
var isolatedStaticDir = (config, context) => {
|
|
15744
16865
|
const { adapter, output } = config.deployment;
|
|
15745
16866
|
const outputDir = isolatedOutputDir(config, context);
|
|
15746
16867
|
if (output === "server" && adapter === "vercel") {
|
|
15747
|
-
return
|
|
16868
|
+
return join30(outputDir, "static");
|
|
15748
16869
|
}
|
|
15749
16870
|
if (output === "server" && adapter === "node") {
|
|
15750
|
-
return
|
|
16871
|
+
return join30(outputDir, "client");
|
|
15751
16872
|
}
|
|
15752
16873
|
return outputDir;
|
|
15753
16874
|
};
|
|
15754
16875
|
var publishLlmsFiles = async (project, distDir) => {
|
|
15755
|
-
const indexPath =
|
|
15756
|
-
const fullPath =
|
|
15757
|
-
const writeIndex = !
|
|
15758
|
-
const writeFull = !
|
|
16876
|
+
const indexPath = join30(distDir, "llms.txt");
|
|
16877
|
+
const fullPath = join30(distDir, "llms-full.txt");
|
|
16878
|
+
const writeIndex = !existsSync18(indexPath);
|
|
16879
|
+
const writeFull = !existsSync18(fullPath);
|
|
15759
16880
|
if (!(writeIndex || writeFull)) {
|
|
15760
16881
|
return;
|
|
15761
16882
|
}
|
|
@@ -15788,23 +16909,25 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
15788
16909
|
await publishLlmsFiles(project, distDir);
|
|
15789
16910
|
}
|
|
15790
16911
|
const sitemap = buildSitemap(project);
|
|
15791
|
-
if (sitemap && !
|
|
15792
|
-
await writeFile8(
|
|
16912
|
+
if (sitemap && !existsSync18(join30(distDir, "sitemap.xml"))) {
|
|
16913
|
+
await writeFile8(join30(distDir, "sitemap.xml"), sitemap, "utf-8");
|
|
15793
16914
|
logger.success("Generated sitemap.xml");
|
|
15794
16915
|
}
|
|
15795
16916
|
const robots = buildRobots(project);
|
|
15796
|
-
if (robots && !
|
|
15797
|
-
await writeFile8(
|
|
16917
|
+
if (robots && !existsSync18(join30(distDir, "robots.txt"))) {
|
|
16918
|
+
await writeFile8(join30(distDir, "robots.txt"), robots, "utf-8");
|
|
15798
16919
|
logger.success("Generated robots.txt");
|
|
15799
16920
|
}
|
|
15800
16921
|
const agentReadability = buildAgentReadability(project);
|
|
15801
|
-
if (agentReadability && !
|
|
15802
|
-
await writeFile8(
|
|
16922
|
+
if (agentReadability && !existsSync18(join30(distDir, "agent-readability.json"))) {
|
|
16923
|
+
await writeFile8(join30(distDir, "agent-readability.json"), `${JSON.stringify(agentReadability, null, 2)}
|
|
15803
16924
|
`, "utf-8");
|
|
15804
16925
|
logger.success("Generated agent-readability.json");
|
|
15805
16926
|
}
|
|
16927
|
+
await emitWellKnownFiles(project.config, distDir);
|
|
16928
|
+
await emitAgentSkills(project, distDir);
|
|
15806
16929
|
await emitRedirectFiles(project.config, distDir);
|
|
15807
|
-
await emitHeaderFiles(project
|
|
16930
|
+
await emitHeaderFiles(project, distDir);
|
|
15808
16931
|
const { config } = project;
|
|
15809
16932
|
const features = serverFeatures(config);
|
|
15810
16933
|
const sitemapNote = config.seo.sitemap ? "no (set deployment.site)" : "no (seo.sitemap is false)";
|
|
@@ -15918,16 +17041,19 @@ var buildCommand = defineCommand3({
|
|
|
15918
17041
|
if (surfaced.moved) {
|
|
15919
17042
|
logger.success(`Surfaced ${adapter} output to ${surfaced.to}`);
|
|
15920
17043
|
}
|
|
17044
|
+
if (project.config.deployment.output === "server" && adapter === "vercel") {
|
|
17045
|
+
await emitVercelNegotiation(project, markdownRoutePaths(project), root);
|
|
17046
|
+
}
|
|
15921
17047
|
await publishBuildArtifacts(project, deployStaticDir(project.config, project.context), args);
|
|
15922
17048
|
}
|
|
15923
17049
|
});
|
|
15924
17050
|
|
|
15925
17051
|
// src/cli/commands/check.ts
|
|
15926
|
-
import { existsSync as
|
|
17052
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
15927
17053
|
import { check } from "@astrojs/check";
|
|
15928
17054
|
import { sync } from "astro";
|
|
15929
17055
|
import { defineCommand as defineCommand4 } from "citty";
|
|
15930
|
-
import { join as
|
|
17056
|
+
import { join as join31 } from "pathe";
|
|
15931
17057
|
var checkCommand = defineCommand4({
|
|
15932
17058
|
args: {
|
|
15933
17059
|
isolated: {
|
|
@@ -15963,13 +17089,13 @@ var checkCommand = defineCommand4({
|
|
|
15963
17089
|
});
|
|
15964
17090
|
const { outDir } = project.context;
|
|
15965
17091
|
await sync({ logLevel: "warn", root: outDir });
|
|
15966
|
-
const tsconfig =
|
|
17092
|
+
const tsconfig = join31(root, "tsconfig.json");
|
|
15967
17093
|
logger.start(`Type-checking ${project.graph.pages.length} page(s)`);
|
|
15968
17094
|
const failed = await check({
|
|
15969
17095
|
minimumFailingSeverity: "error",
|
|
15970
17096
|
minimumSeverity: "hint",
|
|
15971
17097
|
root: outDir,
|
|
15972
|
-
tsconfig:
|
|
17098
|
+
tsconfig: existsSync19(tsconfig) ? tsconfig : undefined,
|
|
15973
17099
|
watch: false
|
|
15974
17100
|
});
|
|
15975
17101
|
if (failed) {
|
|
@@ -15984,7 +17110,7 @@ var checkCommand = defineCommand4({
|
|
|
15984
17110
|
import { watch } from "node:fs";
|
|
15985
17111
|
import { dev } from "astro";
|
|
15986
17112
|
import { defineCommand as defineCommand5 } from "citty";
|
|
15987
|
-
import { basename as basename4, dirname as
|
|
17113
|
+
import { basename as basename4, dirname as dirname13 } from "pathe";
|
|
15988
17114
|
|
|
15989
17115
|
// src/astro/integration.ts
|
|
15990
17116
|
var overlayServer = null;
|
|
@@ -16177,7 +17303,7 @@ var devCommand = defineCommand5({
|
|
|
16177
17303
|
}),
|
|
16178
17304
|
...fileTargets.map((target) => {
|
|
16179
17305
|
const name = basename4(target);
|
|
16180
|
-
const watcher = watch(
|
|
17306
|
+
const watcher = watch(dirname13(target), (_event, filename) => {
|
|
16181
17307
|
if (!filename || filename === name) {
|
|
16182
17308
|
regenerate();
|
|
16183
17309
|
}
|
|
@@ -16201,12 +17327,12 @@ var devCommand = defineCommand5({
|
|
|
16201
17327
|
// src/cli/commands/doctor.ts
|
|
16202
17328
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
16203
17329
|
import { defineCommand as defineCommand6 } from "citty";
|
|
16204
|
-
import { join as
|
|
17330
|
+
import { join as join32 } from "pathe";
|
|
16205
17331
|
var FALLBACK_MIN_NODE = "22.12.0";
|
|
16206
17332
|
var LEADING_RANGE = /^[^\d]*/u;
|
|
16207
17333
|
var minSupportedNode = () => {
|
|
16208
17334
|
try {
|
|
16209
|
-
const pkg = JSON.parse(readFileSync11(
|
|
17335
|
+
const pkg = JSON.parse(readFileSync11(join32(packageRoot(), "package.json"), "utf-8"));
|
|
16210
17336
|
const range = pkg.engines?.node ?? "";
|
|
16211
17337
|
return range.replace(LEADING_RANGE, "") || FALLBACK_MIN_NODE;
|
|
16212
17338
|
} catch {
|
|
@@ -16299,21 +17425,21 @@ var doctorCommand = defineCommand6({
|
|
|
16299
17425
|
|
|
16300
17426
|
// src/cli/commands/eject.ts
|
|
16301
17427
|
import { defineCommand as defineCommand7 } from "citty";
|
|
16302
|
-
import { relative as
|
|
17428
|
+
import { relative as relative17 } from "pathe";
|
|
16303
17429
|
|
|
16304
17430
|
// src/registry/eject.ts
|
|
16305
|
-
import { existsSync as
|
|
16306
|
-
import { cp as cp2, mkdir as
|
|
16307
|
-
import { join as
|
|
17431
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
17432
|
+
import { cp as cp2, mkdir as mkdir8, readFile as readFile18, rm as rm3, writeFile as writeFile9 } from "node:fs/promises";
|
|
17433
|
+
import { join as join33, relative as relative15 } from "pathe";
|
|
16308
17434
|
var toPosix = (path) => path.split("\\").join("/");
|
|
16309
17435
|
var LOCAL_BLUME_SOURCE = "../../node_modules/blume/src/**/*.{astro,ts,tsx}";
|
|
16310
17436
|
var blumeSourceGlob = (root, genDir, resolveBlumeRoot = packageRoot) => {
|
|
16311
|
-
if (
|
|
17437
|
+
if (existsSync20(join33(root, "node_modules", "blume"))) {
|
|
16312
17438
|
return LOCAL_BLUME_SOURCE;
|
|
16313
17439
|
}
|
|
16314
17440
|
try {
|
|
16315
|
-
const src =
|
|
16316
|
-
return `${toPosix(
|
|
17441
|
+
const src = join33(resolveBlumeRoot(), "src");
|
|
17442
|
+
return `${toPosix(relative15(genDir, src))}/**/*.{astro,ts,tsx}`;
|
|
16317
17443
|
} catch {
|
|
16318
17444
|
console.warn('blume: could not locate the installed blume package; src/generated/app.css keeps its default `@source "../../node_modules/blume/..."` glob. If blume is hoisted elsewhere, point that glob at its install location or Blume\'s utility classes will be missing.');
|
|
16319
17445
|
return LOCAL_BLUME_SOURCE;
|
|
@@ -16326,28 +17452,28 @@ var ejectOpenApiData = (project) => {
|
|
|
16326
17452
|
var askFiles = async (project, srcDir, genDir) => {
|
|
16327
17453
|
const { ask } = project.config.ai;
|
|
16328
17454
|
if (!(ask?.enabled && !ask.endpoint)) {
|
|
16329
|
-
const endpointPath =
|
|
16330
|
-
if (
|
|
16331
|
-
const content = await
|
|
17455
|
+
const endpointPath = join33(srcDir, "pages", "api", "ask.ts");
|
|
17456
|
+
if (existsSync20(endpointPath)) {
|
|
17457
|
+
const content = await readFile18(endpointPath, "utf-8");
|
|
16332
17458
|
if (content.startsWith("// Generated by Blume. Do not edit.")) {
|
|
16333
17459
|
await rm3(endpointPath, { force: true });
|
|
16334
17460
|
}
|
|
16335
17461
|
}
|
|
16336
|
-
await rm3(
|
|
17462
|
+
await rm3(join33(genDir, "ask-data.json"), { force: true });
|
|
16337
17463
|
return [];
|
|
16338
17464
|
}
|
|
16339
17465
|
const grounded = ask.provider !== "inkeep";
|
|
16340
17466
|
const files = [
|
|
16341
17467
|
{
|
|
16342
17468
|
content: askEndpointTemplate(resolveAskBackend(ask), grounded),
|
|
16343
|
-
path:
|
|
17469
|
+
path: join33(srcDir, "pages", "api", "ask.ts")
|
|
16344
17470
|
}
|
|
16345
17471
|
];
|
|
16346
17472
|
if (grounded) {
|
|
16347
17473
|
files.push({
|
|
16348
17474
|
content: `${JSON.stringify(await buildAskData(project))}
|
|
16349
17475
|
`,
|
|
16350
|
-
path:
|
|
17476
|
+
path: join33(genDir, "ask-data.json")
|
|
16351
17477
|
});
|
|
16352
17478
|
}
|
|
16353
17479
|
return files;
|
|
@@ -16380,19 +17506,19 @@ var mcpFiles = async (project, userPages, srcDir, genDir) => {
|
|
|
16380
17506
|
{
|
|
16381
17507
|
content: `${JSON.stringify(data)}
|
|
16382
17508
|
`,
|
|
16383
|
-
path:
|
|
17509
|
+
path: join33(genDir, "mcp-data.json")
|
|
16384
17510
|
},
|
|
16385
17511
|
{
|
|
16386
17512
|
content: mcpEndpointTemplate(route),
|
|
16387
|
-
path:
|
|
17513
|
+
path: join33(srcDir, "pages", mcpPageFile(route))
|
|
16388
17514
|
},
|
|
16389
17515
|
{
|
|
16390
17516
|
content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),
|
|
16391
|
-
path:
|
|
17517
|
+
path: join33(srcDir, "blume-mcp", "discovery.ts")
|
|
16392
17518
|
},
|
|
16393
17519
|
{
|
|
16394
17520
|
content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),
|
|
16395
|
-
path:
|
|
17521
|
+
path: join33(srcDir, "blume-mcp", "server-card.ts")
|
|
16396
17522
|
}
|
|
16397
17523
|
];
|
|
16398
17524
|
};
|
|
@@ -16405,23 +17531,23 @@ var changelogFiles = (project, userPages, srcDir, options) => {
|
|
|
16405
17531
|
return [
|
|
16406
17532
|
{
|
|
16407
17533
|
content: changelogIndexTemplate(options),
|
|
16408
|
-
path:
|
|
17534
|
+
path: join33(srcDir, "pages", "changelog.astro")
|
|
16409
17535
|
}
|
|
16410
17536
|
];
|
|
16411
17537
|
};
|
|
16412
|
-
var readExamplesCss = (root, css) => css &&
|
|
17538
|
+
var readExamplesCss = (root, css) => css && existsSync20(join33(root, css)) ? readFile18(join33(root, css), "utf-8") : Promise.resolve("");
|
|
16413
17539
|
var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
|
|
16414
17540
|
{
|
|
16415
17541
|
content: examplesPageTemplate(),
|
|
16416
|
-
path:
|
|
17542
|
+
path: join33(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
|
|
16417
17543
|
}
|
|
16418
17544
|
] : [];
|
|
16419
|
-
var ejectIntegrationBridge = (config, root, configFile) => config.integrations.length > 0 && configFile ? { configFile: toPosix(
|
|
17545
|
+
var ejectIntegrationBridge = (config, root, configFile) => config.integrations.length > 0 && configFile ? { configFile: toPosix(relative15(root, configFile)) } : undefined;
|
|
16420
17546
|
var eject = async (root) => {
|
|
16421
17547
|
const project = await scanProject(root, { mode: "build" });
|
|
16422
17548
|
const { context, config } = project;
|
|
16423
|
-
const srcDir =
|
|
16424
|
-
const genDir =
|
|
17549
|
+
const srcDir = join33(root, "src");
|
|
17550
|
+
const genDir = join33(srcDir, "generated");
|
|
16425
17551
|
const askEnabled = config.ai.ask?.enabled ?? false;
|
|
16426
17552
|
const exportPdf = config.export.pdf;
|
|
16427
17553
|
const exportEpub = config.export.epub;
|
|
@@ -16439,7 +17565,7 @@ var eject = async (root) => {
|
|
|
16439
17565
|
context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
|
|
16440
17566
|
detectNeedsReact(root),
|
|
16441
17567
|
detectUsesMath(root),
|
|
16442
|
-
context.themeFile ?
|
|
17568
|
+
context.themeFile ? readFile18(context.themeFile, "utf-8") : Promise.resolve(""),
|
|
16443
17569
|
readExamplesCss(root, config.examples.css),
|
|
16444
17570
|
buildRawMarkdown(project),
|
|
16445
17571
|
discoverIslands(root)
|
|
@@ -16453,14 +17579,14 @@ var eject = async (root) => {
|
|
|
16453
17579
|
const needsSvelte = frameworks.has("svelte");
|
|
16454
17580
|
const relContext = {
|
|
16455
17581
|
...context,
|
|
16456
|
-
contentRoot: toPosix(
|
|
17582
|
+
contentRoot: toPosix(relative15(root, context.contentRoot)),
|
|
16457
17583
|
outDir: ".",
|
|
16458
17584
|
root: "."
|
|
16459
17585
|
};
|
|
16460
|
-
const componentsImport = context.componentsFile ? `../../${toPosix(
|
|
17586
|
+
const componentsImport = context.componentsFile ? `../../${toPosix(relative15(root, context.componentsFile))}` : null;
|
|
16461
17587
|
const relPages = [
|
|
16462
17588
|
...pages.map((page) => ({
|
|
16463
|
-
entrypoint: toPosix(
|
|
17589
|
+
entrypoint: toPosix(relative15(root, page.entrypoint)),
|
|
16464
17590
|
pattern: page.pattern
|
|
16465
17591
|
})),
|
|
16466
17592
|
...mcpDiscoveryPages(project, pages)
|
|
@@ -16487,14 +17613,14 @@ var eject = async (root) => {
|
|
|
16487
17613
|
searchClientPath: "./src/generated/search-client.ts",
|
|
16488
17614
|
themePath: "./src/generated/app.css"
|
|
16489
17615
|
}),
|
|
16490
|
-
path:
|
|
17616
|
+
path: join33(root, "astro.config.mjs")
|
|
16491
17617
|
},
|
|
16492
17618
|
{
|
|
16493
17619
|
content: runtimeTsconfigTemplate(),
|
|
16494
|
-
path:
|
|
17620
|
+
path: join33(root, "tsconfig.json"),
|
|
16495
17621
|
skipIfExists: true
|
|
16496
17622
|
},
|
|
16497
|
-
{ content: envTemplate(), path:
|
|
17623
|
+
{ content: envTemplate(), path: join33(srcDir, "env.d.ts") },
|
|
16498
17624
|
{
|
|
16499
17625
|
content: contentConfigTemplate({
|
|
16500
17626
|
config,
|
|
@@ -16502,7 +17628,7 @@ var eject = async (root) => {
|
|
|
16502
17628
|
staged: hasStaged,
|
|
16503
17629
|
stagedBase: stagedDir
|
|
16504
17630
|
}),
|
|
16505
|
-
path:
|
|
17631
|
+
path: join33(srcDir, "content.config.ts")
|
|
16506
17632
|
},
|
|
16507
17633
|
{
|
|
16508
17634
|
content: catchAllPageTemplate({
|
|
@@ -16511,19 +17637,19 @@ var eject = async (root) => {
|
|
|
16511
17637
|
mathEnabled: usesMath,
|
|
16512
17638
|
needsReact
|
|
16513
17639
|
}),
|
|
16514
|
-
path:
|
|
17640
|
+
path: join33(srcDir, "pages", "[...slug].astro")
|
|
16515
17641
|
},
|
|
16516
17642
|
{
|
|
16517
17643
|
content: planComponentSlots(componentsImport, null).module,
|
|
16518
|
-
path:
|
|
17644
|
+
path: join33(genDir, "components.ts")
|
|
16519
17645
|
},
|
|
16520
17646
|
{
|
|
16521
17647
|
content: islandMapTemplate(islands.islands),
|
|
16522
|
-
path:
|
|
17648
|
+
path: join33(genDir, "islands.ts")
|
|
16523
17649
|
},
|
|
16524
17650
|
{
|
|
16525
17651
|
content: exampleMapTemplate(examples.examples, config.basePath),
|
|
16526
|
-
path:
|
|
17652
|
+
path: join33(genDir, "examples.ts")
|
|
16527
17653
|
},
|
|
16528
17654
|
{
|
|
16529
17655
|
content: examplesEntryTemplate({
|
|
@@ -16531,7 +17657,7 @@ var eject = async (root) => {
|
|
|
16531
17657
|
sources: ["../../**/*.{astro,jsx,svelte,ts,tsx,vue}"],
|
|
16532
17658
|
userCss: userExamplesCss
|
|
16533
17659
|
}),
|
|
16534
|
-
path:
|
|
17660
|
+
path: join33(genDir, "examples.css")
|
|
16535
17661
|
},
|
|
16536
17662
|
{
|
|
16537
17663
|
content: tailwindEntryTemplate({
|
|
@@ -16543,30 +17669,30 @@ var eject = async (root) => {
|
|
|
16543
17669
|
twoslashCss: twoslashCss(),
|
|
16544
17670
|
userTheme
|
|
16545
17671
|
}),
|
|
16546
|
-
path:
|
|
17672
|
+
path: join33(genDir, "app.css")
|
|
16547
17673
|
},
|
|
16548
|
-
{ content: buildRuntimeData(project), path:
|
|
17674
|
+
{ content: buildRuntimeData(project), path: join33(genDir, "data.json") },
|
|
16549
17675
|
{
|
|
16550
17676
|
content: askComponentTemplate(askEnabled),
|
|
16551
|
-
path:
|
|
17677
|
+
path: join33(genDir, "Ask.astro")
|
|
16552
17678
|
},
|
|
16553
17679
|
{
|
|
16554
17680
|
content: `${JSON.stringify(ejectOpenApiData(project))}
|
|
16555
17681
|
`,
|
|
16556
|
-
path:
|
|
17682
|
+
path: join33(genDir, "openapi.json")
|
|
16557
17683
|
},
|
|
16558
17684
|
{
|
|
16559
17685
|
content: `${JSON.stringify(rawMarkdown)}
|
|
16560
17686
|
`,
|
|
16561
|
-
path:
|
|
17687
|
+
path: join33(genDir, "raw-markdown.json")
|
|
16562
17688
|
},
|
|
16563
17689
|
{
|
|
16564
17690
|
content: rawMarkdownEndpointTemplate("md"),
|
|
16565
|
-
path:
|
|
17691
|
+
path: join33(srcDir, "pages", "[...slug].md.ts")
|
|
16566
17692
|
},
|
|
16567
17693
|
{
|
|
16568
17694
|
content: rawMarkdownEndpointTemplate("mdx"),
|
|
16569
|
-
path:
|
|
17695
|
+
path: join33(srcDir, "pages", "[...slug].mdx.ts")
|
|
16570
17696
|
}
|
|
16571
17697
|
];
|
|
16572
17698
|
if (askEnabled) {
|
|
@@ -16575,7 +17701,7 @@ var eject = async (root) => {
|
|
|
16575
17701
|
if (config.seo.og.enabled) {
|
|
16576
17702
|
files.push({
|
|
16577
17703
|
content: ogEndpointTemplate(customOgRoutes(pages, config.title, config.seo.og.titles)),
|
|
16578
|
-
path:
|
|
17704
|
+
path: join33(srcDir, "pages", "og", "[...slug].png.ts")
|
|
16579
17705
|
});
|
|
16580
17706
|
}
|
|
16581
17707
|
files.push(...await mcpFiles(project, pages, srcDir, genDir), ...changelogFiles(project, pages, srcDir, {
|
|
@@ -16587,28 +17713,28 @@ var eject = async (root) => {
|
|
|
16587
17713
|
if (!routeIsTaken(pages, project.graph.pages, "/404")) {
|
|
16588
17714
|
files.push({
|
|
16589
17715
|
content: notFoundPageTemplate(),
|
|
16590
|
-
path:
|
|
17716
|
+
path: join33(srcDir, "pages", "404.astro")
|
|
16591
17717
|
});
|
|
16592
17718
|
}
|
|
16593
17719
|
files.push({
|
|
16594
17720
|
content: searchClientTemplate(config),
|
|
16595
|
-
path:
|
|
17721
|
+
path: join33(genDir, "search-client.ts")
|
|
16596
17722
|
});
|
|
16597
17723
|
if (servesStaticIndex(config.search.provider)) {
|
|
16598
17724
|
const documents = await buildSearchDocuments(project);
|
|
16599
17725
|
files.push({
|
|
16600
17726
|
content: `${JSON.stringify(documents)}
|
|
16601
17727
|
`,
|
|
16602
|
-
path:
|
|
17728
|
+
path: join33(genDir, "search.json")
|
|
16603
17729
|
}, {
|
|
16604
17730
|
content: searchEndpointTemplate(),
|
|
16605
|
-
path:
|
|
17731
|
+
path: join33(srcDir, "pages", "blume-search.json.ts")
|
|
16606
17732
|
});
|
|
16607
17733
|
}
|
|
16608
17734
|
if (config.search.provider === "mixedbread") {
|
|
16609
17735
|
files.push({
|
|
16610
17736
|
content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
|
|
16611
|
-
path:
|
|
17737
|
+
path: join33(srcDir, "pages", "api", "search.ts")
|
|
16612
17738
|
});
|
|
16613
17739
|
}
|
|
16614
17740
|
const feeds = buildRssFeeds(project);
|
|
@@ -16617,10 +17743,10 @@ var eject = async (root) => {
|
|
|
16617
17743
|
files.push({
|
|
16618
17744
|
content: `${JSON.stringify(feedXml)}
|
|
16619
17745
|
`,
|
|
16620
|
-
path:
|
|
17746
|
+
path: join33(genDir, "rss.json")
|
|
16621
17747
|
}, {
|
|
16622
17748
|
content: rssEndpointTemplate(),
|
|
16623
|
-
path:
|
|
17749
|
+
path: join33(srcDir, "pages", "[section]", "rss.xml.ts")
|
|
16624
17750
|
});
|
|
16625
17751
|
}
|
|
16626
17752
|
const warnings = [];
|
|
@@ -16634,28 +17760,28 @@ var eject = async (root) => {
|
|
|
16634
17760
|
for (const file of references.files) {
|
|
16635
17761
|
files.push({
|
|
16636
17762
|
content: file.content,
|
|
16637
|
-
path:
|
|
17763
|
+
path: join33(srcDir, "pages", file.pagePath)
|
|
16638
17764
|
});
|
|
16639
17765
|
}
|
|
16640
17766
|
}
|
|
16641
17767
|
files.push(...islands.islands.map((island) => ({
|
|
16642
17768
|
content: islandWrapperTemplate(island),
|
|
16643
|
-
path:
|
|
17769
|
+
path: join33(genDir, "islands", `${island.name}.astro`)
|
|
16644
17770
|
})), ...examples.examples.map((example) => ({
|
|
16645
17771
|
content: exampleWrapperTemplate(example),
|
|
16646
|
-
path:
|
|
17772
|
+
path: join33(genDir, "examples", `${exampleSlug(example.path)}.astro`)
|
|
16647
17773
|
})), ...examplesPreviewFiles(srcDir, config.basePath, examples.examples.length > 0));
|
|
16648
17774
|
for (const [entryId, content] of staged) {
|
|
16649
|
-
files.push({ content, path:
|
|
17775
|
+
files.push({ content, path: join33(root, stagedDir, entryId) });
|
|
16650
17776
|
}
|
|
16651
|
-
const written = files.filter((file) => !(file.skipIfExists &&
|
|
17777
|
+
const written = files.filter((file) => !(file.skipIfExists && existsSync20(file.path)));
|
|
16652
17778
|
await Promise.all(written.map(async (file) => {
|
|
16653
|
-
await
|
|
17779
|
+
await mkdir8(join33(file.path, ".."), { recursive: true });
|
|
16654
17780
|
await writeFile9(file.path, file.content, "utf-8");
|
|
16655
17781
|
}));
|
|
16656
|
-
const assetsSrc =
|
|
16657
|
-
if (
|
|
16658
|
-
await cp2(assetsSrc,
|
|
17782
|
+
const assetsSrc = join33(context.outDir, "public", "blume-assets");
|
|
17783
|
+
if (existsSync20(assetsSrc)) {
|
|
17784
|
+
await cp2(assetsSrc, join33(root, "public", "blume-assets"), {
|
|
16659
17785
|
recursive: true
|
|
16660
17786
|
});
|
|
16661
17787
|
}
|
|
@@ -16664,8 +17790,8 @@ var eject = async (root) => {
|
|
|
16664
17790
|
};
|
|
16665
17791
|
|
|
16666
17792
|
// src/cli/eject-scripts.ts
|
|
16667
|
-
import { readFile as
|
|
16668
|
-
import { join as
|
|
17793
|
+
import { readFile as readFile19, writeFile as writeFile10 } from "node:fs/promises";
|
|
17794
|
+
import { join as join34 } from "pathe";
|
|
16669
17795
|
var droppedArtifactNotices = (config) => {
|
|
16670
17796
|
const notices = [];
|
|
16671
17797
|
if (config.search.provider === "pagefind") {
|
|
@@ -16692,10 +17818,10 @@ var droppedArtifactNotices = (config) => {
|
|
|
16692
17818
|
return notices;
|
|
16693
17819
|
};
|
|
16694
17820
|
var updatePackageScripts = async (root) => {
|
|
16695
|
-
const pkgPath =
|
|
17821
|
+
const pkgPath = join34(root, "package.json");
|
|
16696
17822
|
let pkg;
|
|
16697
17823
|
try {
|
|
16698
|
-
pkg = JSON.parse(await
|
|
17824
|
+
pkg = JSON.parse(await readFile19(pkgPath, "utf-8"));
|
|
16699
17825
|
} catch {
|
|
16700
17826
|
return;
|
|
16701
17827
|
}
|
|
@@ -16711,9 +17837,9 @@ var updatePackageScripts = async (root) => {
|
|
|
16711
17837
|
};
|
|
16712
17838
|
|
|
16713
17839
|
// src/cli/init/scaffold.ts
|
|
16714
|
-
import { existsSync as
|
|
16715
|
-
import { mkdir as
|
|
16716
|
-
import { basename as basename5, dirname as
|
|
17840
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
17841
|
+
import { mkdir as mkdir9, writeFile as writeFile11 } from "node:fs/promises";
|
|
17842
|
+
import { basename as basename5, dirname as dirname14, isAbsolute as isAbsolute10, join as join35, relative as relative16 } from "pathe";
|
|
16717
17843
|
|
|
16718
17844
|
// src/core/package-json.ts
|
|
16719
17845
|
var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
|
|
@@ -16772,7 +17898,7 @@ var STARTERS = {
|
|
|
16772
17898
|
files: (dir) => [
|
|
16773
17899
|
{
|
|
16774
17900
|
content: page("API Reference", "Explore the API.", "# API Reference\n\nYour OpenAPI spec renders at [`/api`](/api). Point `openapi.sources` at your own spec in `blume.config.ts`."),
|
|
16775
|
-
path:
|
|
17901
|
+
path: join35(dir, "index.mdx")
|
|
16776
17902
|
}
|
|
16777
17903
|
]
|
|
16778
17904
|
},
|
|
@@ -16787,7 +17913,7 @@ var STARTERS = {
|
|
|
16787
17913
|
files: (dir) => [
|
|
16788
17914
|
{
|
|
16789
17915
|
content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
|
|
16790
|
-
path:
|
|
17916
|
+
path: join35(dir, "index.mdx")
|
|
16791
17917
|
},
|
|
16792
17918
|
{
|
|
16793
17919
|
content: `---
|
|
@@ -16798,7 +17924,7 @@ date: 2026-01-01
|
|
|
16798
17924
|
|
|
16799
17925
|
The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
|
|
16800
17926
|
`,
|
|
16801
|
-
path:
|
|
17927
|
+
path: join35(dir, "changelog", "v1-0-0.mdx")
|
|
16802
17928
|
}
|
|
16803
17929
|
]
|
|
16804
17930
|
},
|
|
@@ -16811,7 +17937,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
|
|
|
16811
17937
|
Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
|
|
16812
17938
|
|
|
16813
17939
|
Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
16814
|
-
path:
|
|
17940
|
+
path: join35(dir, "index.mdx")
|
|
16815
17941
|
}
|
|
16816
17942
|
]
|
|
16817
17943
|
},
|
|
@@ -16822,11 +17948,11 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
|
16822
17948
|
content: page("Introduction", "Get started with the SDK.", `# Introduction
|
|
16823
17949
|
|
|
16824
17950
|
Install the SDK and make your first call. See [Installation](/installation).`),
|
|
16825
|
-
path:
|
|
17951
|
+
path: join35(dir, "index.mdx")
|
|
16826
17952
|
},
|
|
16827
17953
|
{
|
|
16828
17954
|
content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
|
|
16829
|
-
path:
|
|
17955
|
+
path: join35(dir, "installation.mdx")
|
|
16830
17956
|
}
|
|
16831
17957
|
]
|
|
16832
17958
|
}
|
|
@@ -16841,7 +17967,7 @@ var detectPackageManager = (userAgent) => {
|
|
|
16841
17967
|
const name = userAgent?.split("/")[0];
|
|
16842
17968
|
return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : "npm";
|
|
16843
17969
|
};
|
|
16844
|
-
var validateContentDir = (root, dir) =>
|
|
17970
|
+
var validateContentDir = (root, dir) => isAbsolute10(dir) || relative16(root, join35(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
|
|
16845
17971
|
var titleize = (raw) => {
|
|
16846
17972
|
const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
|
|
16847
17973
|
return words.length === 0 ? "My Docs" : words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
@@ -16918,27 +18044,27 @@ export default defineConfig({
|
|
|
16918
18044
|
`;
|
|
16919
18045
|
var extraDepsFor = (sources) => ({
|
|
16920
18046
|
...sources.includes("notion") && { "@notionhq/client": "^2.2.15" },
|
|
16921
|
-
...sources.includes("sanity") && { "@sanity/client": "^
|
|
18047
|
+
...sources.includes("sanity") && { "@sanity/client": "^7.25.0" }
|
|
16922
18048
|
});
|
|
16923
18049
|
var buildPlan = (root, answers) => {
|
|
16924
18050
|
const files = [
|
|
16925
18051
|
{
|
|
16926
18052
|
content: blumePackageJson(toPackageName(basename5(root)), extraDepsFor(answers.sources)),
|
|
16927
|
-
path:
|
|
18053
|
+
path: join35(root, "package.json")
|
|
16928
18054
|
},
|
|
16929
|
-
{ content: buildConfig(answers), path:
|
|
18055
|
+
{ content: buildConfig(answers), path: join35(root, "blume.config.ts") }
|
|
16930
18056
|
];
|
|
16931
18057
|
if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
|
|
16932
|
-
files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path:
|
|
18058
|
+
files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path: join35(root, file.path) })));
|
|
16933
18059
|
}
|
|
16934
18060
|
return files;
|
|
16935
18061
|
};
|
|
16936
18062
|
var writeFileSafe = async (file, log) => {
|
|
16937
|
-
if (
|
|
18063
|
+
if (existsSync21(file.path)) {
|
|
16938
18064
|
log.info(`Skipped existing ${file.path}`);
|
|
16939
18065
|
return false;
|
|
16940
18066
|
}
|
|
16941
|
-
await
|
|
18067
|
+
await mkdir9(dirname14(file.path), { recursive: true });
|
|
16942
18068
|
await writeFile11(file.path, file.content, "utf-8");
|
|
16943
18069
|
log.success(`Created ${file.path}`);
|
|
16944
18070
|
return true;
|
|
@@ -17014,7 +18140,7 @@ var ejectCommand = defineCommand7({
|
|
|
17014
18140
|
}
|
|
17015
18141
|
logger.success(`Ejected ${files.length} file(s):`);
|
|
17016
18142
|
for (const file of files) {
|
|
17017
|
-
process.stdout.write(` ${
|
|
18143
|
+
process.stdout.write(` ${relative17(root, file)}
|
|
17018
18144
|
`);
|
|
17019
18145
|
}
|
|
17020
18146
|
reportDroppedArtifacts(notices);
|
|
@@ -17030,9 +18156,9 @@ The blume package remains importable.`);
|
|
|
17030
18156
|
});
|
|
17031
18157
|
|
|
17032
18158
|
// src/cli/commands/eval.ts
|
|
17033
|
-
import { existsSync as
|
|
18159
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
17034
18160
|
import { defineCommand as defineCommand8 } from "citty";
|
|
17035
|
-
import { join as
|
|
18161
|
+
import { join as join39 } from "pathe";
|
|
17036
18162
|
|
|
17037
18163
|
// src/eval/prompts.ts
|
|
17038
18164
|
var readerPrompt = (question) => `You are evaluating whether a product's documentation can answer a user's question.
|
|
@@ -17101,7 +18227,7 @@ When you are done, print the file and suggest running \`blume eval\` to try it.`
|
|
|
17101
18227
|
// src/eval/report.ts
|
|
17102
18228
|
import { mkdtemp as mkdtemp2, writeFile as writeFile12 } from "node:fs/promises";
|
|
17103
18229
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
17104
|
-
import { join as
|
|
18230
|
+
import { join as join36, relative as relative18 } from "pathe";
|
|
17105
18231
|
var ESC4 = String.fromCodePoint(27);
|
|
17106
18232
|
var COLORS3 = {
|
|
17107
18233
|
bold: `${ESC4}[1m`,
|
|
@@ -17182,15 +18308,15 @@ var summaryLine2 = (result) => {
|
|
|
17182
18308
|
var headerLine = (total, agent) => `${COLORS3.bold}blume eval${COLORS3.reset} ${total} question(s) · ${AGENTS[agent].name}`;
|
|
17183
18309
|
var startLine = (id2, index, total) => ` ${COLORS3.dim}▸ ${id2} (${index + 1}/${total})${COLORS3.reset}`;
|
|
17184
18310
|
var fixLines = (result, root) => result.diagnostics.filter((diagnostic) => diagnostic.code !== "BLUME_EVAL_ROUTE_UNKNOWN").map((finding2) => {
|
|
17185
|
-
const site = finding2.file ? `${
|
|
18311
|
+
const site = finding2.file ? `${relative18(root, finding2.file)}${finding2.line ? `:${finding2.line}` : ""}` : "";
|
|
17186
18312
|
return ` ${COLORS3.cyan}fix:${COLORS3.reset} ${site} ${COLORS3.dim}${finding2.message}${COLORS3.reset}`;
|
|
17187
18313
|
});
|
|
17188
18314
|
var warningLines = (result, root) => result.diagnostics.filter((diagnostic) => diagnostic.code === "BLUME_EVAL_ROUTE_UNKNOWN").map((finding2) => {
|
|
17189
|
-
const site = finding2.file ? ` ${
|
|
18315
|
+
const site = finding2.file ? ` ${relative18(root, finding2.file)}${finding2.line ? `:${finding2.line}` : ""}` : "";
|
|
17190
18316
|
return ` ${COLORS3.yellow}⚠${COLORS3.reset}${site} ${COLORS3.dim}${finding2.message}${COLORS3.reset}`;
|
|
17191
18317
|
});
|
|
17192
18318
|
var evalReportJson = (result, root, threshold) => {
|
|
17193
|
-
const diagnostics = result.diagnostics.map((diagnostic) => diagnostic.file ? { ...diagnostic, file:
|
|
18319
|
+
const diagnostics = result.diagnostics.map((diagnostic) => diagnostic.file ? { ...diagnostic, file: relative18(root, diagnostic.file) } : diagnostic);
|
|
17194
18320
|
return `${JSON.stringify({
|
|
17195
18321
|
diagnostics,
|
|
17196
18322
|
eval: {
|
|
@@ -17206,21 +18332,21 @@ var evalReportJson = (result, root, threshold) => {
|
|
|
17206
18332
|
`;
|
|
17207
18333
|
};
|
|
17208
18334
|
var writeEvalReport = async (result, root, threshold) => {
|
|
17209
|
-
const dir = await mkdtemp2(
|
|
17210
|
-
const path =
|
|
18335
|
+
const dir = await mkdtemp2(join36(tmpdir2(), "blume-eval-"));
|
|
18336
|
+
const path = join36(dir, "report.json");
|
|
17211
18337
|
await writeFile12(path, evalReportJson(result, root, threshold));
|
|
17212
18338
|
return path;
|
|
17213
18339
|
};
|
|
17214
18340
|
|
|
17215
18341
|
// src/eval/run.ts
|
|
17216
|
-
import { mkdir as
|
|
18342
|
+
import { mkdir as mkdir10, mkdtemp as mkdtemp3, writeFile as writeFile14 } from "node:fs/promises";
|
|
17217
18343
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
17218
|
-
import { join as
|
|
18344
|
+
import { join as join38 } from "pathe";
|
|
17219
18345
|
|
|
17220
18346
|
// src/eval/agents.ts
|
|
17221
18347
|
import { spawn as spawn2 } from "node:child_process";
|
|
17222
|
-
import { readFile as
|
|
17223
|
-
import { join as
|
|
18348
|
+
import { readFile as readFile20, writeFile as writeFile13 } from "node:fs/promises";
|
|
18349
|
+
import { join as join37 } from "pathe";
|
|
17224
18350
|
import { z as z3 } from "zod";
|
|
17225
18351
|
var KILL_GRACE_MS = 5000;
|
|
17226
18352
|
var MCP_TOOL_NAMES = [
|
|
@@ -17242,7 +18368,7 @@ var DISALLOWED_TOOLS = [
|
|
|
17242
18368
|
"WebSearch",
|
|
17243
18369
|
"Task"
|
|
17244
18370
|
];
|
|
17245
|
-
var runAgentHeadless = (bin, args, options) => new Promise((
|
|
18371
|
+
var runAgentHeadless = (bin, args, options) => new Promise((resolve11, reject) => {
|
|
17246
18372
|
const platform = options.platform ?? process.platform;
|
|
17247
18373
|
const child = spawn2(bin, args, {
|
|
17248
18374
|
cwd: options.cwd,
|
|
@@ -17271,12 +18397,12 @@ var runAgentHeadless = (bin, args, options) => new Promise((resolve9, reject) =>
|
|
|
17271
18397
|
});
|
|
17272
18398
|
child.once("close", (code) => {
|
|
17273
18399
|
clearTimeout(deadline);
|
|
17274
|
-
|
|
18400
|
+
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
17275
18401
|
});
|
|
17276
18402
|
child.once("exit", (code) => {
|
|
17277
18403
|
if (timedOut) {
|
|
17278
18404
|
clearTimeout(deadline);
|
|
17279
|
-
|
|
18405
|
+
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
17280
18406
|
}
|
|
17281
18407
|
});
|
|
17282
18408
|
child.stdin.end(options.prompt);
|
|
@@ -17286,7 +18412,7 @@ var writeMcpConfig = async (dir, snapshotPath, launcher) => {
|
|
|
17286
18412
|
args: [process.argv[1] ?? "", "mcp-stdio", "--data", snapshotPath],
|
|
17287
18413
|
command: process.execPath
|
|
17288
18414
|
};
|
|
17289
|
-
const configPath =
|
|
18415
|
+
const configPath = join37(dir, "mcp-config.json");
|
|
17290
18416
|
const config = {
|
|
17291
18417
|
mcpServers: {
|
|
17292
18418
|
[MCP_SERVER_NAME]: { args: resolved.args, command: resolved.command }
|
|
@@ -17394,17 +18520,17 @@ var readAgentOutput = async (kind, result, lastMessagePath) => {
|
|
|
17394
18520
|
text: payload.data.result
|
|
17395
18521
|
};
|
|
17396
18522
|
}
|
|
17397
|
-
let
|
|
18523
|
+
let text2;
|
|
17398
18524
|
try {
|
|
17399
|
-
const raw = await
|
|
17400
|
-
|
|
18525
|
+
const raw = await readFile20(lastMessagePath, "utf-8");
|
|
18526
|
+
text2 = raw.trim();
|
|
17401
18527
|
} catch {
|
|
17402
18528
|
return { detail: "no last message written", isError: true, text: "" };
|
|
17403
18529
|
}
|
|
17404
|
-
if (
|
|
18530
|
+
if (text2 === "") {
|
|
17405
18531
|
return { detail: "empty last message", isError: true, text: "" };
|
|
17406
18532
|
}
|
|
17407
|
-
return { isError: false, text };
|
|
18533
|
+
return { isError: false, text: text2 };
|
|
17408
18534
|
};
|
|
17409
18535
|
var verdictSchema = z3.object({
|
|
17410
18536
|
missing: z3.array(z3.string()).default([]),
|
|
@@ -17412,15 +18538,15 @@ var verdictSchema = z3.object({
|
|
|
17412
18538
|
pass: z3.boolean(),
|
|
17413
18539
|
score: z3.number().min(0).max(1).optional()
|
|
17414
18540
|
});
|
|
17415
|
-
var parseVerdict = (
|
|
17416
|
-
const start =
|
|
17417
|
-
const end =
|
|
18541
|
+
var parseVerdict = (text2) => {
|
|
18542
|
+
const start = text2.indexOf("{");
|
|
18543
|
+
const end = text2.lastIndexOf("}");
|
|
17418
18544
|
if (start === -1 || end <= start) {
|
|
17419
18545
|
return;
|
|
17420
18546
|
}
|
|
17421
18547
|
let parsed;
|
|
17422
18548
|
try {
|
|
17423
|
-
parsed = JSON.parse(
|
|
18549
|
+
parsed = JSON.parse(text2.slice(start, end + 1));
|
|
17424
18550
|
} catch {
|
|
17425
18551
|
return;
|
|
17426
18552
|
}
|
|
@@ -17429,7 +18555,7 @@ var parseVerdict = (text) => {
|
|
|
17429
18555
|
};
|
|
17430
18556
|
|
|
17431
18557
|
// src/eval/schema.ts
|
|
17432
|
-
import { readFile as
|
|
18558
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
17433
18559
|
import { load as load2 } from "js-yaml";
|
|
17434
18560
|
import { z as z4 } from "zod";
|
|
17435
18561
|
var ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/u;
|
|
@@ -17474,7 +18600,7 @@ var describeIssues = (error) => error.issues.map((issue) => {
|
|
|
17474
18600
|
var loadEvalsFile = async (path) => {
|
|
17475
18601
|
let raw;
|
|
17476
18602
|
try {
|
|
17477
|
-
raw = await
|
|
18603
|
+
raw = await readFile21(path, "utf-8");
|
|
17478
18604
|
} catch {
|
|
17479
18605
|
throw new EvalsFileError(path, `No evals file found at ${path}. Run \`blume eval init\` to draft one.`);
|
|
17480
18606
|
}
|
|
@@ -17566,9 +18692,9 @@ var errored = (question, detail, durationMs) => ({
|
|
|
17566
18692
|
var runQuestion = async (question, index, context) => {
|
|
17567
18693
|
const started = performance.now();
|
|
17568
18694
|
const elapsed = () => Math.round(performance.now() - started);
|
|
17569
|
-
const workDir =
|
|
17570
|
-
await
|
|
17571
|
-
const answerPath =
|
|
18695
|
+
const workDir = join38(context.dir, `work-${index}`);
|
|
18696
|
+
await mkdir10(workDir, { recursive: true });
|
|
18697
|
+
const answerPath = join38(workDir, "answer.txt");
|
|
17572
18698
|
const reader = await context.run(context.bin, agentArgs(context.kind, { lastMessagePath: answerPath, mcp: context.mcp }), {
|
|
17573
18699
|
cwd: workDir,
|
|
17574
18700
|
prompt: readerPrompt(question),
|
|
@@ -17581,7 +18707,7 @@ var runQuestion = async (question, index, context) => {
|
|
|
17581
18707
|
costUsd: answer.costUsd
|
|
17582
18708
|
};
|
|
17583
18709
|
}
|
|
17584
|
-
const verdictPath =
|
|
18710
|
+
const verdictPath = join38(workDir, "verdict.txt");
|
|
17585
18711
|
const judge = await context.run(context.bin, agentArgs(context.kind, { lastMessagePath: verdictPath }), {
|
|
17586
18712
|
cwd: workDir,
|
|
17587
18713
|
prompt: judgePrompt(question, answer.text),
|
|
@@ -17623,8 +18749,8 @@ var runEval = async (options) => {
|
|
|
17623
18749
|
const kind = options.agent;
|
|
17624
18750
|
const run = options.run ?? runAgentHeadless;
|
|
17625
18751
|
const anchor = { path: options.evalsPath, raw: options.rawEvals };
|
|
17626
|
-
const dir = await mkdtemp3(
|
|
17627
|
-
const snapshotPath =
|
|
18752
|
+
const dir = await mkdtemp3(join38(tmpdir3(), "blume-eval-"));
|
|
18753
|
+
const snapshotPath = join38(dir, "mcp-data.json");
|
|
17628
18754
|
await writeFile14(snapshotPath, JSON.stringify(await buildMcpData(options.project)));
|
|
17629
18755
|
const mcp = await writeMcpConfig(dir, snapshotPath);
|
|
17630
18756
|
const context = {
|
|
@@ -17762,8 +18888,8 @@ var runFixHandoff = async (agent, result, root, threshold) => {
|
|
|
17762
18888
|
}
|
|
17763
18889
|
};
|
|
17764
18890
|
var runInit = async (agent, file) => {
|
|
17765
|
-
const path =
|
|
17766
|
-
if (
|
|
18891
|
+
const path = join39(process.cwd(), file);
|
|
18892
|
+
if (existsSync22(path)) {
|
|
17767
18893
|
logger.error(`${file} already exists — edit it directly, or pass --file to draft elsewhere.`);
|
|
17768
18894
|
process.exit(1);
|
|
17769
18895
|
}
|
|
@@ -17827,7 +18953,7 @@ var evalCommand = defineCommand8({
|
|
|
17827
18953
|
let result;
|
|
17828
18954
|
try {
|
|
17829
18955
|
const project = await scanProject(root, { mode: "build" });
|
|
17830
|
-
const evalsPath =
|
|
18956
|
+
const evalsPath = join39(root, args.file);
|
|
17831
18957
|
const { evals, raw } = await loadEvalsFile(evalsPath);
|
|
17832
18958
|
process.stderr.write(`${headerLine(evals.questions.length, agent)}
|
|
17833
18959
|
|
|
@@ -17901,10 +19027,10 @@ var evalCommand = defineCommand8({
|
|
|
17901
19027
|
// src/cli/commands/init.ts
|
|
17902
19028
|
import * as clack from "@clack/prompts";
|
|
17903
19029
|
import { defineCommand as defineCommand9 } from "citty";
|
|
17904
|
-
import { resolve as
|
|
19030
|
+
import { resolve as resolve12 } from "pathe";
|
|
17905
19031
|
|
|
17906
19032
|
// src/cli/init/questions.ts
|
|
17907
|
-
import { basename as basename6, resolve as
|
|
19033
|
+
import { basename as basename6, resolve as resolve11 } from "pathe";
|
|
17908
19034
|
var cancelled = (value) => typeof value === "symbol";
|
|
17909
19035
|
var collectAnswers = async (prompter, flags, defaults) => {
|
|
17910
19036
|
const directory = flags.directory ?? await prompter.text({
|
|
@@ -17915,7 +19041,7 @@ var collectAnswers = async (prompter, flags, defaults) => {
|
|
|
17915
19041
|
if (cancelled(directory)) {
|
|
17916
19042
|
return null;
|
|
17917
19043
|
}
|
|
17918
|
-
const root =
|
|
19044
|
+
const root = resolve11(defaults.cwd, directory);
|
|
17919
19045
|
const title = await prompter.text({
|
|
17920
19046
|
initialValue: titleize(basename6(root)),
|
|
17921
19047
|
message: "What's your docs site called?",
|
|
@@ -18084,7 +19210,7 @@ var initCommand = defineCommand9({
|
|
|
18084
19210
|
title: "My Docs"
|
|
18085
19211
|
};
|
|
18086
19212
|
}
|
|
18087
|
-
const root =
|
|
19213
|
+
const root = resolve12(cwd, answers.directory);
|
|
18088
19214
|
if (validateContentDir(root, answers.contentDir) !== undefined) {
|
|
18089
19215
|
logger.error(`Invalid --content-dir "${answers.contentDir}" (must be a path inside the project).`);
|
|
18090
19216
|
process.exit(1);
|
|
@@ -18114,7 +19240,7 @@ var initCommand = defineCommand9({
|
|
|
18114
19240
|
});
|
|
18115
19241
|
|
|
18116
19242
|
// src/cli/commands/mcp-stdio.ts
|
|
18117
|
-
import { readFile as
|
|
19243
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
18118
19244
|
import { defineCommand as defineCommand10 } from "citty";
|
|
18119
19245
|
|
|
18120
19246
|
// src/ai/mcp/stdio.ts
|
|
@@ -18140,6 +19266,22 @@ var SCHEMA = {
|
|
|
18140
19266
|
};
|
|
18141
19267
|
var BOOST = { description: 2, title: 3 };
|
|
18142
19268
|
var SEGMENTED_LANGUAGES = new Set(["ja", "ko", "th", "zh"]);
|
|
19269
|
+
var BIGRAM_LANGUAGES = new Set(["ja", "zh"]);
|
|
19270
|
+
var BIGRAM_SCRIPTS = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}々〆〇ー゙゚]+$/u;
|
|
19271
|
+
var addBigrams = (run, tokens) => {
|
|
19272
|
+
const characters = [...run];
|
|
19273
|
+
if (characters.length === 1) {
|
|
19274
|
+
tokens.add(run);
|
|
19275
|
+
return;
|
|
19276
|
+
}
|
|
19277
|
+
let previous = "";
|
|
19278
|
+
for (const character of characters) {
|
|
19279
|
+
if (previous) {
|
|
19280
|
+
tokens.add(previous + character);
|
|
19281
|
+
}
|
|
19282
|
+
previous = character;
|
|
19283
|
+
}
|
|
19284
|
+
};
|
|
18143
19285
|
var segmentingTokenizer = (locale) => {
|
|
18144
19286
|
const language = locale?.toLowerCase().split(/[-_]/u)[0] ?? "";
|
|
18145
19287
|
if (!SEGMENTED_LANGUAGES.has(language)) {
|
|
@@ -18149,16 +19291,32 @@ var segmentingTokenizer = (locale) => {
|
|
|
18149
19291
|
return;
|
|
18150
19292
|
}
|
|
18151
19293
|
const segmenter = new Intl.Segmenter(language, { granularity: "word" });
|
|
19294
|
+
const bigram = BIGRAM_LANGUAGES.has(language);
|
|
18152
19295
|
return {
|
|
18153
19296
|
language,
|
|
18154
19297
|
normalizationCache: new Map,
|
|
18155
19298
|
tokenize: (raw) => {
|
|
18156
19299
|
const tokens = new Set;
|
|
19300
|
+
let run = "";
|
|
19301
|
+
const flush = () => {
|
|
19302
|
+
if (run) {
|
|
19303
|
+
addBigrams(run, tokens);
|
|
19304
|
+
run = "";
|
|
19305
|
+
}
|
|
19306
|
+
};
|
|
18157
19307
|
for (const segment of segmenter.segment(raw.toLowerCase())) {
|
|
18158
|
-
if (segment.isWordLike) {
|
|
18159
|
-
|
|
19308
|
+
if (!segment.isWordLike) {
|
|
19309
|
+
flush();
|
|
19310
|
+
continue;
|
|
19311
|
+
}
|
|
19312
|
+
if (bigram && BIGRAM_SCRIPTS.test(segment.segment)) {
|
|
19313
|
+
run += segment.segment;
|
|
19314
|
+
continue;
|
|
18160
19315
|
}
|
|
19316
|
+
flush();
|
|
19317
|
+
tokens.add(segment.segment);
|
|
18161
19318
|
}
|
|
19319
|
+
flush();
|
|
18162
19320
|
return [...tokens];
|
|
18163
19321
|
}
|
|
18164
19322
|
};
|
|
@@ -18172,14 +19330,18 @@ var buildOramaIndex = async (documents, locale) => {
|
|
|
18172
19330
|
await insertMultiple(db, documents);
|
|
18173
19331
|
return db;
|
|
18174
19332
|
};
|
|
19333
|
+
var ALL_TOKENS = 0;
|
|
18175
19334
|
var queryOramaIndex = async (db, term, limit, locale) => {
|
|
18176
|
-
const
|
|
19335
|
+
const params = {
|
|
18177
19336
|
boost: BOOST,
|
|
18178
19337
|
limit,
|
|
18179
19338
|
properties: ["title", "description", "content"],
|
|
18180
19339
|
term,
|
|
18181
19340
|
...locale ? { where: { locale: { eq: locale } } } : {}
|
|
18182
|
-
}
|
|
19341
|
+
};
|
|
19342
|
+
const bigrammed = BIGRAM_LANGUAGES.has(db.tokenizer?.language ?? "");
|
|
19343
|
+
const strict = bigrammed ? await search(db, { ...params, threshold: ALL_TOKENS }) : undefined;
|
|
19344
|
+
const found = strict && strict.hits.length > 0 ? strict : await search(db, params);
|
|
18183
19345
|
return found.hits.map((hit) => hit.document);
|
|
18184
19346
|
};
|
|
18185
19347
|
|
|
@@ -18256,13 +19418,13 @@ var excerptFor = (doc) => {
|
|
|
18256
19418
|
const head = doc.content.slice(0, EXCERPT_LENGTH).trim();
|
|
18257
19419
|
return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;
|
|
18258
19420
|
};
|
|
18259
|
-
var
|
|
19421
|
+
var text2 = (value, isError = false) => ({
|
|
18260
19422
|
content: [{ text: value, type: "text" }],
|
|
18261
19423
|
...isError ? { isError: true } : {}
|
|
18262
19424
|
});
|
|
18263
19425
|
var createIndexProvider = (documents, locale) => {
|
|
18264
19426
|
let dbPromise = null;
|
|
18265
|
-
return ()
|
|
19427
|
+
return function provideIndex() {
|
|
18266
19428
|
dbPromise ??= buildOramaIndex(documents, locale);
|
|
18267
19429
|
return dbPromise;
|
|
18268
19430
|
};
|
|
@@ -18286,18 +19448,18 @@ var buildServer = (data, index) => {
|
|
|
18286
19448
|
title: doc.title,
|
|
18287
19449
|
url: urlFor(doc.route, data)
|
|
18288
19450
|
}));
|
|
18289
|
-
return
|
|
19451
|
+
return text2(JSON.stringify(results, null, 2));
|
|
18290
19452
|
}
|
|
18291
19453
|
if (name === "get_page") {
|
|
18292
19454
|
const key = normalizeRoute2(asString2(args.route), data);
|
|
18293
19455
|
const markdown = data.pages[key];
|
|
18294
19456
|
if (markdown === undefined) {
|
|
18295
|
-
return
|
|
19457
|
+
return text2(`No page found at "${key}". Use list_pages or search_docs to find valid routes.`, true);
|
|
18296
19458
|
}
|
|
18297
|
-
return
|
|
19459
|
+
return text2(markdown);
|
|
18298
19460
|
}
|
|
18299
19461
|
if (name === "list_pages") {
|
|
18300
|
-
return
|
|
19462
|
+
return text2(JSON.stringify(data.routes.map((route) => ({
|
|
18301
19463
|
contentType: route.contentType,
|
|
18302
19464
|
description: route.description,
|
|
18303
19465
|
lastModified: route.lastModified,
|
|
@@ -18307,9 +19469,9 @@ var buildServer = (data, index) => {
|
|
|
18307
19469
|
})), null, 2));
|
|
18308
19470
|
}
|
|
18309
19471
|
if (name === "get_navigation") {
|
|
18310
|
-
return
|
|
19472
|
+
return text2(JSON.stringify(data.navigation, null, 2));
|
|
18311
19473
|
}
|
|
18312
|
-
return
|
|
19474
|
+
return text2(`Unknown tool: ${name}`, true);
|
|
18313
19475
|
});
|
|
18314
19476
|
return server;
|
|
18315
19477
|
};
|
|
@@ -18341,7 +19503,7 @@ var mcpStdioCommand = defineCommand10({
|
|
|
18341
19503
|
async run({ args }) {
|
|
18342
19504
|
let data;
|
|
18343
19505
|
try {
|
|
18344
|
-
data = JSON.parse(await
|
|
19506
|
+
data = JSON.parse(await readFile22(args.data, "utf-8"));
|
|
18345
19507
|
} catch (error) {
|
|
18346
19508
|
const detail = error instanceof Error ? error.message : String(error);
|
|
18347
19509
|
process.stderr.write(`blume mcp-stdio: cannot load the snapshot at ${args.data}: ${detail}
|
|
@@ -18353,10 +19515,10 @@ var mcpStdioCommand = defineCommand10({
|
|
|
18353
19515
|
});
|
|
18354
19516
|
|
|
18355
19517
|
// src/cli/commands/preview.ts
|
|
18356
|
-
import { existsSync as
|
|
19518
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
18357
19519
|
import { preview } from "astro";
|
|
18358
19520
|
import { defineCommand as defineCommand11 } from "citty";
|
|
18359
|
-
import { join as
|
|
19521
|
+
import { join as join40 } from "pathe";
|
|
18360
19522
|
var previewCommand = defineCommand11({
|
|
18361
19523
|
args: {
|
|
18362
19524
|
host: { description: "Network host to bind.", type: "string" },
|
|
@@ -18370,7 +19532,7 @@ var previewCommand = defineCommand11({
|
|
|
18370
19532
|
const root = process.cwd();
|
|
18371
19533
|
const { config } = await loadConfig(root);
|
|
18372
19534
|
const context = resolveProjectContext(root, config);
|
|
18373
|
-
if (!
|
|
19535
|
+
if (!existsSync23(join40(context.outDir, "astro.config.mjs"))) {
|
|
18374
19536
|
logger.error("No build found. Run `blume build` first.");
|
|
18375
19537
|
process.exit(1);
|
|
18376
19538
|
}
|
|
@@ -18388,7 +19550,7 @@ var previewCommand = defineCommand11({
|
|
|
18388
19550
|
// src/cli/commands/sync.ts
|
|
18389
19551
|
import { rm as rm4 } from "node:fs/promises";
|
|
18390
19552
|
import { defineCommand as defineCommand12 } from "citty";
|
|
18391
|
-
import { join as
|
|
19553
|
+
import { join as join41 } from "pathe";
|
|
18392
19554
|
var syncCommand = defineCommand12({
|
|
18393
19555
|
args: {
|
|
18394
19556
|
force: {
|
|
@@ -18410,7 +19572,7 @@ var syncCommand = defineCommand12({
|
|
|
18410
19572
|
if (args.force) {
|
|
18411
19573
|
const { config } = await loadConfig(root);
|
|
18412
19574
|
const context = resolveProjectContext(root, config);
|
|
18413
|
-
await rm4(
|
|
19575
|
+
await rm4(join41(context.outDir, "cache"), { force: true, recursive: true });
|
|
18414
19576
|
logger.info("Cleared source cache.");
|
|
18415
19577
|
}
|
|
18416
19578
|
const lock = readDevLock(resolveRuntimeDir(root));
|
|
@@ -18428,13 +19590,13 @@ var syncCommand = defineCommand12({
|
|
|
18428
19590
|
});
|
|
18429
19591
|
|
|
18430
19592
|
// src/cli/commands/validate.ts
|
|
18431
|
-
import { existsSync as
|
|
19593
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
18432
19594
|
import { defineCommand as defineCommand13 } from "citty";
|
|
18433
|
-
import { join as
|
|
19595
|
+
import { join as join43 } from "pathe";
|
|
18434
19596
|
|
|
18435
19597
|
// src/core/links.ts
|
|
18436
|
-
import { existsSync as
|
|
18437
|
-
import { basename as basename7, join as
|
|
19598
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
19599
|
+
import { basename as basename7, join as join42 } from "pathe";
|
|
18438
19600
|
var HTTP = /^https?:\/\//iu;
|
|
18439
19601
|
var PROTOCOL_RELATIVE = /^\/\//u;
|
|
18440
19602
|
var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
|
|
@@ -18447,7 +19609,7 @@ var decodePercent = (value) => {
|
|
|
18447
19609
|
};
|
|
18448
19610
|
var DOC_EXT = /\.(?:md|mdx)$/iu;
|
|
18449
19611
|
var FILE_EXT = /\.[a-z0-9]+$/iu;
|
|
18450
|
-
var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null &&
|
|
19612
|
+
var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null && existsSync24(join42(ctx.publicDir, resolved));
|
|
18451
19613
|
var NUMERIC_PREFIX3 = /^\d+[-_.]/u;
|
|
18452
19614
|
var isIndexPage = (page2) => /^index\.(?:md|mdx)$/iu.test(basename7(page2.navPath).replace(NUMERIC_PREFIX3, ""));
|
|
18453
19615
|
var applyRelativePart = (segments, part) => {
|
|
@@ -18658,12 +19820,12 @@ var validateCommand = defineCommand13({
|
|
|
18658
19820
|
});
|
|
18659
19821
|
extraRoutes.push(...manifest.routes.flatMap((route) => route.fallback ? [route.path] : []));
|
|
18660
19822
|
}
|
|
18661
|
-
const publicDir =
|
|
19823
|
+
const publicDir = join43(root, "public");
|
|
18662
19824
|
diagnostics.push(...await validateLinks(project.graph, {
|
|
18663
19825
|
basePath: project.config.basePath,
|
|
18664
19826
|
checkExternal: Boolean(args.external),
|
|
18665
19827
|
extraRoutes,
|
|
18666
|
-
publicDir:
|
|
19828
|
+
publicDir: existsSync25(publicDir) ? publicDir : null,
|
|
18667
19829
|
redirects: project.config.redirects
|
|
18668
19830
|
}));
|
|
18669
19831
|
} catch (error) {
|
|
@@ -18727,5 +19889,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
18727
19889
|
});
|
|
18728
19890
|
runMain(main);
|
|
18729
19891
|
|
|
18730
|
-
//# debugId=
|
|
19892
|
+
//# debugId=DE086E5CDF1627AA64756E2164756E21
|
|
18731
19893
|
//# sourceMappingURL=index.js.map
|