blume 1.2.0 → 1.3.0
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 +45 -0
- package/dist/cli/index.js +1715 -539
- package/dist/cli/index.js.map +40 -31
- 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 +17 -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/ask-context.ts +7 -1
- package/src/ai/ask-data.ts +1 -0
- package/src/ai/link-headers.ts +52 -0
- package/src/ai/llms.ts +12 -1
- package/src/ai/markdown.ts +15 -2
- package/src/ai/mcp/data.ts +7 -0
- package/src/ai/mcp/discovery.ts +70 -15
- package/src/ai/mcp/server.ts +14 -8
- package/src/ai/mcp/stdio.ts +4 -1
- 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 +116 -6
- package/src/astro/integration.ts +52 -14
- package/src/astro/templates.ts +191 -37
- 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 +178 -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/NavTree.astro +4 -4
- package/src/components/layout/PageLayout.astro +2 -0
- package/src/components/layout/ReferenceLayout.astro +2 -0
- package/src/components/layout/RootLayout.astro +63 -11
- package/src/components/layout/Search.astro +2 -2
- package/src/components/layout/WebMcp.astro +49 -0
- package/src/components/layout/search/orama.ts +5 -2
- 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 +233 -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 +151 -7
- package/src/theme/entry.ts +34 -13
- package/src/theme/fonts.ts +183 -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}
|
|
@@ -3903,10 +4161,10 @@ var searchClientImport = (module) => `import { createSearch as create } from "bl
|
|
|
3903
4161
|
`;
|
|
3904
4162
|
var SEARCH_BASE_IMPORT = `import { joinBase } from "blume/components/islands/base-path.ts";
|
|
3905
4163
|
`;
|
|
3906
|
-
var staticSearchClient = (module) => `${SEARCH_CLIENT_HEADER}${searchClientImport(module)}${SEARCH_BASE_IMPORT}
|
|
4164
|
+
var staticSearchClient = (module, locale) => `${SEARCH_CLIENT_HEADER}${searchClientImport(module)}${SEARCH_BASE_IMPORT}
|
|
3907
4165
|
const indexUrl = joinBase(import.meta.env.BASE_URL, "blume-search.json");
|
|
3908
4166
|
|
|
3909
|
-
export const createSearch = () => create({ indexUrl });
|
|
4167
|
+
export const createSearch = () => create({ indexUrl${locale ? `, locale: ${JSON.stringify(locale)}` : ""} });
|
|
3910
4168
|
`;
|
|
3911
4169
|
var hostedSearchClient = (module, options) => `${SEARCH_CLIENT_HEADER}${searchClientImport(module)}
|
|
3912
4170
|
export const createSearch = () => create(${JSON.stringify(options)});
|
|
@@ -3936,7 +4194,7 @@ var hostedSearchOptions = (search) => {
|
|
|
3936
4194
|
var searchClientTemplate = (config) => {
|
|
3937
4195
|
const { search } = config;
|
|
3938
4196
|
if (search.provider === "orama" || search.provider === "flexsearch") {
|
|
3939
|
-
return staticSearchClient(search.provider);
|
|
4197
|
+
return staticSearchClient(search.provider, search.provider === "orama" ? config.i18n?.defaultLocale : undefined);
|
|
3940
4198
|
}
|
|
3941
4199
|
const hosted = hostedSearchOptions(search);
|
|
3942
4200
|
if (hosted) {
|
|
@@ -4016,6 +4274,86 @@ export function GET({ props }: { props: { route: string } }) {
|
|
|
4016
4274
|
});
|
|
4017
4275
|
}
|
|
4018
4276
|
`;
|
|
4277
|
+
var contentAssetsEndpointTemplate = (stagedAssetsDir) => `// Generated by Blume. Do not edit.
|
|
4278
|
+
import { existsSync } from "node:fs";
|
|
4279
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
4280
|
+
import { join, relative, resolve } from "node:path";
|
|
4281
|
+
import type { APIRoute } from "astro";
|
|
4282
|
+
import assets from "../../generated/content-assets.json";
|
|
4283
|
+
|
|
4284
|
+
export const prerender = true;
|
|
4285
|
+
|
|
4286
|
+
const files = assets as Record<string, string>;
|
|
4287
|
+
const STAGED_DIR = ${JSON.stringify(stagedAssetsDir)};
|
|
4288
|
+
|
|
4289
|
+
const TYPES: Record<string, string> = {
|
|
4290
|
+
".apng": "image/apng",
|
|
4291
|
+
".avif": "image/avif",
|
|
4292
|
+
".bmp": "image/bmp",
|
|
4293
|
+
".gif": "image/gif",
|
|
4294
|
+
".ico": "image/x-icon",
|
|
4295
|
+
".jpeg": "image/jpeg",
|
|
4296
|
+
".jpg": "image/jpeg",
|
|
4297
|
+
".png": "image/png",
|
|
4298
|
+
".svg": "image/svg+xml",
|
|
4299
|
+
".tiff": "image/tiff",
|
|
4300
|
+
".webp": "image/webp",
|
|
4301
|
+
};
|
|
4302
|
+
|
|
4303
|
+
const contentType = (path: string): string =>
|
|
4304
|
+
TYPES[path.slice(path.lastIndexOf(".")).toLowerCase()] ??
|
|
4305
|
+
"application/octet-stream";
|
|
4306
|
+
|
|
4307
|
+
const stagedParams = async (): Promise<string[]> => {
|
|
4308
|
+
if (!existsSync(STAGED_DIR)) {
|
|
4309
|
+
return [];
|
|
4310
|
+
}
|
|
4311
|
+
const entries = await readdir(STAGED_DIR, {
|
|
4312
|
+
recursive: true,
|
|
4313
|
+
withFileTypes: true,
|
|
4314
|
+
});
|
|
4315
|
+
return entries
|
|
4316
|
+
.filter((entry) => entry.isFile())
|
|
4317
|
+
.map((entry) =>
|
|
4318
|
+
relative(STAGED_DIR, join(entry.parentPath, entry.name)).replaceAll(
|
|
4319
|
+
"\\\\",
|
|
4320
|
+
"/"
|
|
4321
|
+
)
|
|
4322
|
+
);
|
|
4323
|
+
};
|
|
4324
|
+
|
|
4325
|
+
export async function getStaticPaths() {
|
|
4326
|
+
const params = [
|
|
4327
|
+
...Object.keys(files).map((key) => "content/" + key),
|
|
4328
|
+
...(await stagedParams()),
|
|
4329
|
+
];
|
|
4330
|
+
return params.map((asset) => ({ params: { asset } }));
|
|
4331
|
+
}
|
|
4332
|
+
|
|
4333
|
+
const resolveAsset = (asset: string): string | null => {
|
|
4334
|
+
if (asset.startsWith("content/")) {
|
|
4335
|
+
return files[asset.slice("content/".length)] ?? null;
|
|
4336
|
+
}
|
|
4337
|
+
const abs = resolve(STAGED_DIR, asset);
|
|
4338
|
+
// Traversal guard: the dev server renders on demand, so the param is
|
|
4339
|
+
// attacker-controlled there — never step outside the staged directory.
|
|
4340
|
+
if (!abs.startsWith(STAGED_DIR + "/")) {
|
|
4341
|
+
return null;
|
|
4342
|
+
}
|
|
4343
|
+
return existsSync(abs) ? abs : null;
|
|
4344
|
+
};
|
|
4345
|
+
|
|
4346
|
+
export const GET: APIRoute = async ({ params }) => {
|
|
4347
|
+
const path = resolveAsset(String(params.asset ?? ""));
|
|
4348
|
+
if (!path) {
|
|
4349
|
+
return new Response(null, { status: 404 });
|
|
4350
|
+
}
|
|
4351
|
+
const body = await readFile(path);
|
|
4352
|
+
return new Response(new Uint8Array(body), {
|
|
4353
|
+
headers: { "Content-Type": contentType(path) },
|
|
4354
|
+
});
|
|
4355
|
+
};
|
|
4356
|
+
`;
|
|
4019
4357
|
var mcpPageFile = (route) => `${trimChar(route, "/")}.ts`;
|
|
4020
4358
|
var mcpEndpointTemplate = (route) => {
|
|
4021
4359
|
const clean = trimChar(route, "/");
|
|
@@ -4062,8 +4400,9 @@ export function GET({ props }: { props: { section: string } }) {
|
|
|
4062
4400
|
});
|
|
4063
4401
|
}
|
|
4064
4402
|
`;
|
|
4065
|
-
var ogEndpointTemplate = (customRoutes = []) => `// Generated by Blume. Do not edit.
|
|
4403
|
+
var ogEndpointTemplate = (customRoutes = [], og = {}) => `// Generated by Blume. Do not edit.
|
|
4066
4404
|
import { renderOgImage } from "blume/og";
|
|
4405
|
+
import type { OgFont, OgFontFamilies } from "blume/og";
|
|
4067
4406
|
import data from "blume:data";
|
|
4068
4407
|
|
|
4069
4408
|
export const prerender = true;
|
|
@@ -4073,6 +4412,13 @@ export const prerender = true;
|
|
|
4073
4412
|
// (ts(7034)) under a strict tsconfig.
|
|
4074
4413
|
const customRoutes: { slug: string; title: string }[] = ${JSON.stringify(customRoutes)};
|
|
4075
4414
|
|
|
4415
|
+
// Card fonts, resolved at generation time (explicit seo.og.fonts, or derived
|
|
4416
|
+
// from theme.fonts). Local entries carry absolute build-machine paths, which
|
|
4417
|
+
// is why they are baked into this build-only endpoint instead of the runtime
|
|
4418
|
+
// data that pages serialize into HTML.
|
|
4419
|
+
const fonts: OgFont[] = ${JSON.stringify(og.fonts ?? [])};
|
|
4420
|
+
const families: OgFontFamilies | undefined = ${og.families ? JSON.stringify(og.families) : "undefined"};
|
|
4421
|
+
|
|
4076
4422
|
export function getStaticPaths() {
|
|
4077
4423
|
const seen = new Set<string>();
|
|
4078
4424
|
const paths: { params: { slug: string }; props: { title: string } }[] = [];
|
|
@@ -4093,32 +4439,23 @@ export function getStaticPaths() {
|
|
|
4093
4439
|
return paths;
|
|
4094
4440
|
}
|
|
4095
4441
|
|
|
4096
|
-
// Footer branding shared by every card
|
|
4097
|
-
//
|
|
4442
|
+
// Footer branding shared by every card. The repo slug reuses the header link
|
|
4443
|
+
// URL; the site text (host plus deployment base) is resolved at generate time.
|
|
4098
4444
|
const repoSlug = data.config.repoUrl
|
|
4099
4445
|
? data.config.repoUrl.split("github.com/")[1]
|
|
4100
4446
|
: 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
4447
|
|
|
4112
4448
|
export async function GET({ props }: { props: { title: string } }) {
|
|
4113
4449
|
const png = await renderOgImage({
|
|
4114
4450
|
accent: data.config.og.palette?.accent ?? data.config.theme.accent.light,
|
|
4115
4451
|
brand: data.config.title,
|
|
4116
|
-
description: data.config.description,
|
|
4117
|
-
|
|
4452
|
+
description: data.config.og.description,
|
|
4453
|
+
families,
|
|
4454
|
+
fonts,
|
|
4118
4455
|
logo: data.config.og.logo,
|
|
4119
4456
|
palette: data.config.og.palette,
|
|
4120
4457
|
repo: repoSlug,
|
|
4121
|
-
site:
|
|
4458
|
+
site: data.config.og.site,
|
|
4122
4459
|
title: props.title,
|
|
4123
4460
|
});
|
|
4124
4461
|
return new Response(new Uint8Array(png), {
|
|
@@ -5274,6 +5611,7 @@ var MODULES = [
|
|
|
5274
5611
|
structuredDataChecks,
|
|
5275
5612
|
urlChecks,
|
|
5276
5613
|
networkChecks,
|
|
5614
|
+
dnsAidChecks,
|
|
5277
5615
|
externalChecks
|
|
5278
5616
|
];
|
|
5279
5617
|
|
|
@@ -7436,7 +7774,7 @@ var uiStringsObject = z.object({
|
|
|
7436
7774
|
generating: z.string().default("Generating…"),
|
|
7437
7775
|
openInChat: z.string().default("Open in chat"),
|
|
7438
7776
|
scrollToTop: z.string().default("Scroll to top")
|
|
7439
|
-
}).
|
|
7777
|
+
}).prefault({}),
|
|
7440
7778
|
ask: z.object({
|
|
7441
7779
|
ai: z.string().default("AI"),
|
|
7442
7780
|
clear: z.string().default("Clear conversation"),
|
|
@@ -7450,28 +7788,28 @@ var uiStringsObject = z.object({
|
|
|
7450
7788
|
tip: z.string().default("Tip: You can open and close chat with"),
|
|
7451
7789
|
title: z.string().default("Ask AI"),
|
|
7452
7790
|
you: z.string().default("You")
|
|
7453
|
-
}).
|
|
7791
|
+
}).prefault({}),
|
|
7454
7792
|
banner: z.object({
|
|
7455
7793
|
dismiss: z.string().default("Dismiss announcement")
|
|
7456
|
-
}).
|
|
7794
|
+
}).prefault({}),
|
|
7457
7795
|
changelog: z.object({
|
|
7458
7796
|
description: z.string().default("Product updates, new features, and fixes from every release."),
|
|
7459
7797
|
showReleases: z.string().default("Show {version} releases"),
|
|
7460
7798
|
title: z.string().default("Changelog")
|
|
7461
|
-
}).
|
|
7799
|
+
}).prefault({}),
|
|
7462
7800
|
content: z.object({
|
|
7463
7801
|
diagramError: z.string().default("Could not render this diagram.")
|
|
7464
|
-
}).
|
|
7802
|
+
}).prefault({}),
|
|
7465
7803
|
feedback: z.object({
|
|
7466
7804
|
no: z.string().default("No"),
|
|
7467
7805
|
question: z.string().default("Was this page helpful?"),
|
|
7468
7806
|
thanks: z.string().default("Thanks for your feedback!"),
|
|
7469
7807
|
yes: z.string().default("Yes")
|
|
7470
|
-
}).
|
|
7808
|
+
}).prefault({}),
|
|
7471
7809
|
languageSwitcher: z.object({
|
|
7472
7810
|
label: z.string().default("Language"),
|
|
7473
7811
|
untranslated: z.string().default("Not translated")
|
|
7474
|
-
}).
|
|
7812
|
+
}).prefault({}),
|
|
7475
7813
|
nav: z.object({
|
|
7476
7814
|
back: z.string().default("Back"),
|
|
7477
7815
|
breadcrumb: z.string().default("Breadcrumb"),
|
|
@@ -7484,19 +7822,19 @@ var uiStringsObject = z.object({
|
|
|
7484
7822
|
sections: z.string().default("Sections"),
|
|
7485
7823
|
toggleNavigation: z.string().default("Toggle navigation"),
|
|
7486
7824
|
toggleTheme: z.string().default("Toggle color theme")
|
|
7487
|
-
}).
|
|
7825
|
+
}).prefault({}),
|
|
7488
7826
|
notFound: z.object({
|
|
7489
7827
|
description: z.string().default("We couldn't find the page you're looking for."),
|
|
7490
7828
|
home: z.string().default("Back to home"),
|
|
7491
7829
|
title: z.string().default("Page not found")
|
|
7492
|
-
}).
|
|
7830
|
+
}).prefault({}),
|
|
7493
7831
|
page: z.object({
|
|
7494
7832
|
lastUpdated: z.string().default("Last updated on"),
|
|
7495
7833
|
next: z.string().default("Next"),
|
|
7496
7834
|
pagination: z.string().default("Pagination"),
|
|
7497
7835
|
previous: z.string().default("Previous"),
|
|
7498
7836
|
skipToContent: z.string().default("Skip to content")
|
|
7499
|
-
}).
|
|
7837
|
+
}).prefault({}),
|
|
7500
7838
|
search: z.object({
|
|
7501
7839
|
all: z.string().default("All"),
|
|
7502
7840
|
allLanguages: z.string().default("All languages"),
|
|
@@ -7513,14 +7851,13 @@ var uiStringsObject = z.object({
|
|
|
7513
7851
|
popular: z.string().default("Popular"),
|
|
7514
7852
|
preview: z.string().default("preview"),
|
|
7515
7853
|
results: z.string().default("Results")
|
|
7516
|
-
}).
|
|
7854
|
+
}).prefault({}),
|
|
7517
7855
|
toc: z.object({
|
|
7518
7856
|
title: z.string().default("On this page")
|
|
7519
|
-
}).
|
|
7857
|
+
}).prefault({})
|
|
7520
7858
|
});
|
|
7521
|
-
var uiStringsSchema = uiStringsObject.
|
|
7522
|
-
var
|
|
7523
|
-
var EN_UI = uiStringsObject.parse(EN_UI_INPUT);
|
|
7859
|
+
var uiStringsSchema = uiStringsObject.prefault({});
|
|
7860
|
+
var EN_UI = uiStringsObject.parse({});
|
|
7524
7861
|
var uiStringsOverrideSchema = z.record(z.string(), z.record(z.string(), z.string()));
|
|
7525
7862
|
var uiLocaleOverridesSchema = z.record(z.string(), uiStringsOverrideSchema);
|
|
7526
7863
|
var mergeUI = (base, override) => {
|
|
@@ -7570,7 +7907,7 @@ var sidebarMetaSchema = z2.strictObject({
|
|
|
7570
7907
|
});
|
|
7571
7908
|
var xHandleSchema = z2.string().transform(normalizeXHandle).optional();
|
|
7572
7909
|
var seoMetaSchema = z2.strictObject({
|
|
7573
|
-
canonical: z2.
|
|
7910
|
+
canonical: z2.url().optional(),
|
|
7574
7911
|
description: z2.string().optional(),
|
|
7575
7912
|
image: z2.string().optional(),
|
|
7576
7913
|
noindex: z2.boolean().default(false),
|
|
@@ -7600,7 +7937,7 @@ var authorSchema = z2.union([
|
|
|
7600
7937
|
}).catchall(z2.unknown())
|
|
7601
7938
|
]);
|
|
7602
7939
|
var pageMetaBaseSchema = z2.strictObject({
|
|
7603
|
-
ai: aiMetaSchema.
|
|
7940
|
+
ai: aiMetaSchema.prefault({}),
|
|
7604
7941
|
authors: z2.union([authorSchema, z2.array(authorSchema)]).optional(),
|
|
7605
7942
|
changelog: changelogMetaSchema.optional(),
|
|
7606
7943
|
date: dateSchema.optional(),
|
|
@@ -7611,9 +7948,9 @@ var pageMetaBaseSchema = z2.strictObject({
|
|
|
7611
7948
|
icon: iconName.optional(),
|
|
7612
7949
|
lastModified: dateSchema.optional(),
|
|
7613
7950
|
noindex: z2.boolean().default(false),
|
|
7614
|
-
search: searchMetaSchema.
|
|
7615
|
-
seo: seoMetaSchema.
|
|
7616
|
-
sidebar: sidebarMetaSchema.
|
|
7951
|
+
search: searchMetaSchema.prefault({}),
|
|
7952
|
+
seo: seoMetaSchema.prefault({}),
|
|
7953
|
+
sidebar: sidebarMetaSchema.prefault({}),
|
|
7617
7954
|
slug: z2.string().optional(),
|
|
7618
7955
|
title: z2.string().optional(),
|
|
7619
7956
|
type: z2.string().optional()
|
|
@@ -7773,11 +8110,31 @@ var sidebarItemSchema = z2.lazy(() => z2.union([
|
|
|
7773
8110
|
root: z2.string().optional()
|
|
7774
8111
|
})
|
|
7775
8112
|
]));
|
|
7776
|
-
var
|
|
7777
|
-
|
|
8113
|
+
var fontFallbackSchema = z2.enum(["sans", "serif", "mono"]);
|
|
8114
|
+
var remoteFontSchema = z2.strictObject({
|
|
8115
|
+
fallback: fontFallbackSchema.optional(),
|
|
8116
|
+
name: z2.string().min(1),
|
|
8117
|
+
provider: z2.enum(["google", "fontsource", "bunny", "fontshare"]).default("google"),
|
|
8118
|
+
weights: z2.array(z2.union([z2.number().int().positive(), z2.string().regex(/^\d+\.\.\d+$/u)])).nonempty().optional()
|
|
8119
|
+
});
|
|
8120
|
+
var localFontVariantSchema = z2.strictObject({
|
|
8121
|
+
src: z2.string().min(1),
|
|
8122
|
+
style: z2.enum(["normal", "italic", "oblique"]).optional(),
|
|
8123
|
+
weight: z2.union([
|
|
8124
|
+
z2.number().int().positive(),
|
|
8125
|
+
z2.string().regex(/^\d+(?:\.\.\d+)?$/u)
|
|
8126
|
+
]).optional()
|
|
8127
|
+
});
|
|
8128
|
+
var localFontSchema = z2.strictObject({
|
|
8129
|
+
fallback: fontFallbackSchema.optional(),
|
|
8130
|
+
name: z2.string().min(1),
|
|
8131
|
+
variants: z2.array(localFontVariantSchema).nonempty()
|
|
8132
|
+
});
|
|
8133
|
+
var fontValueSchema = z2.union([z2.string(), remoteFontSchema, localFontSchema]).superRefine((value, ctx) => {
|
|
8134
|
+
if (typeof value === "string" && !isFontSlug(value)) {
|
|
7778
8135
|
ctx.addIssue({
|
|
7779
8136
|
code: z2.ZodIssueCode.custom,
|
|
7780
|
-
message: `Unknown font "${value}". Supported fonts: ${FONT_SLUGS.join(", ")}.`
|
|
8137
|
+
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
8138
|
});
|
|
7782
8139
|
}
|
|
7783
8140
|
});
|
|
@@ -7797,10 +8154,10 @@ var themeConfigSchema = z2.strictObject({
|
|
|
7797
8154
|
background: perModeValueSchema,
|
|
7798
8155
|
backgroundImage: perModeValueSchema,
|
|
7799
8156
|
fonts: z2.strictObject({
|
|
7800
|
-
body:
|
|
7801
|
-
display:
|
|
7802
|
-
mono:
|
|
7803
|
-
}).
|
|
8157
|
+
body: fontValueSchema.default("inter"),
|
|
8158
|
+
display: fontValueSchema.default("inter-tight"),
|
|
8159
|
+
mono: fontValueSchema.default("ibm-plex-mono")
|
|
8160
|
+
}).prefault({}),
|
|
7804
8161
|
layout: z2.enum(["sidebar"]).default("sidebar"),
|
|
7805
8162
|
mode: z2.enum(["system", "light", "dark"]).default("system"),
|
|
7806
8163
|
radius: z2.enum(["none", "sm", "md", "lg"]).default("md")
|
|
@@ -7850,7 +8207,7 @@ var searchConfigSchema = z2.strictObject({
|
|
|
7850
8207
|
algolia: algoliaSearchSchema.optional(),
|
|
7851
8208
|
indexing: z2.strictObject({
|
|
7852
8209
|
includeHiddenPages: z2.boolean().default(false)
|
|
7853
|
-
}).
|
|
8210
|
+
}).prefault({}),
|
|
7854
8211
|
mixedbread: mixedbreadSearchSchema.optional(),
|
|
7855
8212
|
oramaCloud: oramaCloudSearchSchema.optional(),
|
|
7856
8213
|
popular: z2.array(searchPopularLinkSchema).default([]),
|
|
@@ -7873,6 +8230,22 @@ var askAiProviders = [
|
|
|
7873
8230
|
"inkeep",
|
|
7874
8231
|
"openai-compatible"
|
|
7875
8232
|
];
|
|
8233
|
+
var PRIVATE_JWK_PARAMS = ["d", "p", "q", "dp", "dq", "qi", "oth", "k"];
|
|
8234
|
+
var publicJwkSchema = z2.record(z2.string(), z2.unknown()).superRefine((jwk, ctx) => {
|
|
8235
|
+
if (typeof jwk.kty !== "string" || jwk.kty.length === 0) {
|
|
8236
|
+
ctx.addIssue({
|
|
8237
|
+
code: z2.ZodIssueCode.custom,
|
|
8238
|
+
message: 'A JWK must declare its key type ("kty").'
|
|
8239
|
+
});
|
|
8240
|
+
}
|
|
8241
|
+
const leaked = PRIVATE_JWK_PARAMS.filter((param) => (param in jwk));
|
|
8242
|
+
if (leaked.length > 0) {
|
|
8243
|
+
ctx.addIssue({
|
|
8244
|
+
code: z2.ZodIssueCode.custom,
|
|
8245
|
+
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.`
|
|
8246
|
+
});
|
|
8247
|
+
}
|
|
8248
|
+
});
|
|
7876
8249
|
var mcpConfigSchema = z2.strictObject({
|
|
7877
8250
|
enabled: z2.boolean().default(false),
|
|
7878
8251
|
instructions: z2.string().optional(),
|
|
@@ -7895,7 +8268,7 @@ var askEndpointSchema = z2.string().trim().min(1).refine((value) => {
|
|
|
7895
8268
|
var aiConfigSchema = z2.strictObject({
|
|
7896
8269
|
ask: z2.strictObject({
|
|
7897
8270
|
apiKeyEnv: z2.string().optional(),
|
|
7898
|
-
baseUrl: z2.
|
|
8271
|
+
baseUrl: z2.url().optional(),
|
|
7899
8272
|
enabled: z2.boolean().default(false),
|
|
7900
8273
|
endpoint: askEndpointSchema.optional(),
|
|
7901
8274
|
model: z2.string().default("openai/gpt-5.5"),
|
|
@@ -7923,7 +8296,12 @@ var aiConfigSchema = z2.strictObject({
|
|
|
7923
8296
|
markdownComponents: z2.record(z2.string(), z2.custom((value) => typeof value === "function", {
|
|
7924
8297
|
message: "Expected a serializer function."
|
|
7925
8298
|
})).default({}),
|
|
7926
|
-
mcp: mcpConfigSchema.
|
|
8299
|
+
mcp: mcpConfigSchema.prefault({}),
|
|
8300
|
+
skills: z2.string().min(1).optional(),
|
|
8301
|
+
webBotAuth: z2.strictObject({
|
|
8302
|
+
keys: z2.array(publicJwkSchema).default([])
|
|
8303
|
+
}).prefault({}),
|
|
8304
|
+
webmcp: z2.boolean().default(true)
|
|
7927
8305
|
});
|
|
7928
8306
|
var featuredLinkSchema = z2.strictObject({
|
|
7929
8307
|
href: z2.string(),
|
|
@@ -7940,7 +8318,7 @@ var navigationConfigSchema = z2.strictObject({
|
|
|
7940
8318
|
display: sidebarDisplaySchema.default("flat"),
|
|
7941
8319
|
items: z2.array(sidebarItemSchema).optional()
|
|
7942
8320
|
})
|
|
7943
|
-
]).
|
|
8321
|
+
]).prefault({}).transform((value) => Array.isArray(value) ? { display: "flat", items: value } : value),
|
|
7944
8322
|
tabs: z2.array(navTabSchema).default([])
|
|
7945
8323
|
});
|
|
7946
8324
|
var exportConfigSchema = z2.union([
|
|
@@ -7999,13 +8377,23 @@ var deploymentConfigSchema = z2.strictObject({
|
|
|
7999
8377
|
adapter: z2.enum(["vercel", "node", "netlify", "cloudflare"]).nullable().default(null),
|
|
8000
8378
|
base: z2.string().optional(),
|
|
8001
8379
|
output: z2.enum(["static", "server"]).default("static"),
|
|
8002
|
-
site: z2.
|
|
8380
|
+
site: z2.url().optional()
|
|
8003
8381
|
});
|
|
8004
8382
|
var redirectSchema = z2.strictObject({
|
|
8005
8383
|
from: z2.string(),
|
|
8006
8384
|
status: z2.union([z2.literal(301), z2.literal(302), z2.literal(307), z2.literal(308)]).default(301),
|
|
8007
8385
|
to: z2.string()
|
|
8008
8386
|
});
|
|
8387
|
+
var imageRemotePatternSchema = z2.strictObject({
|
|
8388
|
+
hostname: z2.string().optional(),
|
|
8389
|
+
pathname: z2.string().optional(),
|
|
8390
|
+
port: z2.string().optional(),
|
|
8391
|
+
protocol: z2.string().optional()
|
|
8392
|
+
});
|
|
8393
|
+
var imageConfigSchema = z2.strictObject({
|
|
8394
|
+
domains: z2.array(z2.string()).default([]),
|
|
8395
|
+
remotePatterns: z2.array(imageRemotePatternSchema).default([])
|
|
8396
|
+
});
|
|
8009
8397
|
var xConfigSchema = z2.strictObject({
|
|
8010
8398
|
creator: xHandleSchema,
|
|
8011
8399
|
handle: xHandleSchema
|
|
@@ -8030,13 +8418,21 @@ var ogFontSchema = z2.union([
|
|
|
8030
8418
|
name: z2.string(),
|
|
8031
8419
|
style: z2.union([ogFontStyleSchema, z2.array(ogFontStyleSchema)]).optional(),
|
|
8032
8420
|
weight: ogFontWeightSchema.optional()
|
|
8421
|
+
}),
|
|
8422
|
+
z2.strictObject({
|
|
8423
|
+
name: z2.string(),
|
|
8424
|
+
src: z2.string().min(1),
|
|
8425
|
+
style: ogFontStyleSchema.optional(),
|
|
8426
|
+
weight: z2.number().int().positive().optional()
|
|
8033
8427
|
})
|
|
8034
8428
|
]);
|
|
8035
8429
|
var ogConfigSchema = z2.strictObject({
|
|
8430
|
+
description: z2.union([z2.string(), z2.literal(false)]).optional(),
|
|
8036
8431
|
enabled: z2.boolean().optional(),
|
|
8037
8432
|
fonts: z2.array(ogFontSchema).optional(),
|
|
8038
|
-
logo: z2.string().optional(),
|
|
8433
|
+
logo: z2.union([z2.string(), z2.literal(false)]).optional(),
|
|
8039
8434
|
palette: ogPaletteSchema.optional(),
|
|
8435
|
+
site: z2.union([z2.string(), z2.literal(false)]).optional(),
|
|
8040
8436
|
titles: z2.record(z2.string(), z2.string()).optional()
|
|
8041
8437
|
});
|
|
8042
8438
|
var rssConfigSchema = z2.strictObject({
|
|
@@ -8060,10 +8456,10 @@ var contentSignalsSchema = z2.union([z2.boolean(), contentSignalsObjectSchema]).
|
|
|
8060
8456
|
});
|
|
8061
8457
|
var seoConfigSchema = z2.strictObject({
|
|
8062
8458
|
agentReadability: z2.boolean().default(true),
|
|
8063
|
-
contentSignals: contentSignalsSchema.
|
|
8459
|
+
contentSignals: contentSignalsSchema.prefault(true),
|
|
8064
8460
|
og: ogConfigSchema.default({}),
|
|
8065
8461
|
robots: z2.boolean().default(true),
|
|
8066
|
-
rss: rssConfigSchema.
|
|
8462
|
+
rss: rssConfigSchema.prefault({}),
|
|
8067
8463
|
sitemap: z2.boolean().default(true),
|
|
8068
8464
|
structuredData: z2.boolean().default(true),
|
|
8069
8465
|
x: xConfigSchema.default({})
|
|
@@ -8093,7 +8489,7 @@ var codeBlockThemeSchema = z2.strictObject({
|
|
|
8093
8489
|
light: codeThemeSchema.default("github-light")
|
|
8094
8490
|
});
|
|
8095
8491
|
var codeBlocksConfigSchema = z2.strictObject({
|
|
8096
|
-
theme: codeBlockThemeSchema.
|
|
8492
|
+
theme: codeBlockThemeSchema.prefault({})
|
|
8097
8493
|
});
|
|
8098
8494
|
var examplesConfigSchema = z2.union([
|
|
8099
8495
|
z2.string(),
|
|
@@ -8124,8 +8520,8 @@ var codeConfigSchema = z2.strictObject({
|
|
|
8124
8520
|
wrap: z2.boolean().default(false)
|
|
8125
8521
|
});
|
|
8126
8522
|
var markdownConfigSchema = z2.strictObject({
|
|
8127
|
-
code: codeConfigSchema.
|
|
8128
|
-
codeBlocks: codeBlocksConfigSchema.
|
|
8523
|
+
code: codeConfigSchema.prefault({}),
|
|
8524
|
+
codeBlocks: codeBlocksConfigSchema.prefault({}),
|
|
8129
8525
|
headingAnchors: z2.boolean().default(true),
|
|
8130
8526
|
imageZoom: z2.boolean().default(true)
|
|
8131
8527
|
});
|
|
@@ -8194,32 +8590,33 @@ var tocConfigSchema = z2.union([
|
|
|
8194
8590
|
message: "toc.minHeadingLevel must be less than or equal to toc.maxHeadingLevel."
|
|
8195
8591
|
});
|
|
8196
8592
|
var blumeConfigSchema = z2.strictObject({
|
|
8197
|
-
ai: aiConfigSchema.
|
|
8593
|
+
ai: aiConfigSchema.prefault({}),
|
|
8198
8594
|
analytics: analyticsConfigSchema.optional(),
|
|
8199
|
-
asyncapi: asyncapiConfigSchema.
|
|
8595
|
+
asyncapi: asyncapiConfigSchema.prefault({}),
|
|
8200
8596
|
banner: bannerConfigSchema.optional(),
|
|
8201
8597
|
basePath: z2.string().optional().transform((value) => normalizeBasePath(value)),
|
|
8202
|
-
content: contentConfigSchema.
|
|
8598
|
+
content: contentConfigSchema.prefault({}),
|
|
8203
8599
|
dateFormat: dateFormatConfigSchema.default({ dateStyle: "long" }),
|
|
8204
|
-
deployment: deploymentConfigSchema.
|
|
8600
|
+
deployment: deploymentConfigSchema.prefault({}),
|
|
8205
8601
|
description: z2.string().optional(),
|
|
8206
|
-
examples: examplesConfigSchema.
|
|
8207
|
-
export: exportConfigSchema.
|
|
8602
|
+
examples: examplesConfigSchema.prefault("examples"),
|
|
8603
|
+
export: exportConfigSchema.prefault(false),
|
|
8208
8604
|
feedback: z2.boolean().default(true),
|
|
8209
|
-
frontmatter: frontmatterConfigSchema.
|
|
8605
|
+
frontmatter: frontmatterConfigSchema.prefault({}),
|
|
8210
8606
|
github: githubConfigSchema.optional(),
|
|
8211
8607
|
i18n: i18nConfigSchema.optional(),
|
|
8608
|
+
image: imageConfigSchema.prefault({}),
|
|
8212
8609
|
integrations: z2.array(z2.custom()).default([]),
|
|
8213
8610
|
lastModified: lastModifiedConfigSchema.default(false),
|
|
8214
8611
|
logo: logoConfigSchema.optional(),
|
|
8215
|
-
markdown: markdownConfigSchema.
|
|
8216
|
-
navigation: navigationConfigSchema.
|
|
8217
|
-
openapi: openapiConfigSchema.
|
|
8218
|
-
react: reactConfigSchema.
|
|
8612
|
+
markdown: markdownConfigSchema.prefault({}),
|
|
8613
|
+
navigation: navigationConfigSchema.prefault({}),
|
|
8614
|
+
openapi: openapiConfigSchema.prefault({}),
|
|
8615
|
+
react: reactConfigSchema.prefault({}),
|
|
8219
8616
|
redirects: z2.array(redirectSchema).default([]),
|
|
8220
|
-
search: searchConfigSchema.
|
|
8221
|
-
seo: seoConfigSchema.
|
|
8222
|
-
theme: themeConfigSchema.
|
|
8617
|
+
search: searchConfigSchema.prefault({}),
|
|
8618
|
+
seo: seoConfigSchema.prefault({}),
|
|
8619
|
+
theme: themeConfigSchema.prefault({}),
|
|
8223
8620
|
title: z2.string().default("Documentation"),
|
|
8224
8621
|
toc: tocConfigSchema
|
|
8225
8622
|
});
|
|
@@ -8241,6 +8638,7 @@ var loadConfig = async (root, options = {}) => {
|
|
|
8241
8638
|
});
|
|
8242
8639
|
}
|
|
8243
8640
|
}
|
|
8641
|
+
const themeFontsConfigured = Boolean(raw && typeof raw === "object" && raw.theme?.fonts !== undefined);
|
|
8244
8642
|
const parsed = blumeConfigSchema.safeParse(raw ?? {});
|
|
8245
8643
|
if (!parsed.success) {
|
|
8246
8644
|
const source = configFile && existsSync6(configFile) ? readFileSync3(configFile, "utf-8") : undefined;
|
|
@@ -8275,7 +8673,8 @@ ${moreIssues}`
|
|
|
8275
8673
|
seo: { ...config.seo, og: { ...config.seo.og, enabled: ogEnabled } }
|
|
8276
8674
|
},
|
|
8277
8675
|
configFile,
|
|
8278
|
-
diagnostics: []
|
|
8676
|
+
diagnostics: [],
|
|
8677
|
+
themeFontsConfigured
|
|
8279
8678
|
};
|
|
8280
8679
|
};
|
|
8281
8680
|
|
|
@@ -11396,7 +11795,8 @@ var scanProject = async (root, options = {}) => {
|
|
|
11396
11795
|
graph,
|
|
11397
11796
|
manifest,
|
|
11398
11797
|
mode,
|
|
11399
|
-
sources
|
|
11798
|
+
sources,
|
|
11799
|
+
themeFontsConfigured: configResult.themeFontsConfigured
|
|
11400
11800
|
};
|
|
11401
11801
|
};
|
|
11402
11802
|
|
|
@@ -11580,11 +11980,11 @@ var auditCommand = defineCommand2({
|
|
|
11580
11980
|
});
|
|
11581
11981
|
|
|
11582
11982
|
// src/cli/commands/build.ts
|
|
11583
|
-
import { existsSync as
|
|
11584
|
-
import { readdir, stat as
|
|
11983
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
11984
|
+
import { mkdir as mkdir7, readdir as readdir2, readFile as readFile17, stat as stat3, writeFile as writeFile8 } from "node:fs/promises";
|
|
11585
11985
|
import { build } from "astro";
|
|
11586
11986
|
import { defineCommand as defineCommand3 } from "citty";
|
|
11587
|
-
import { join as
|
|
11987
|
+
import { dirname as dirname12, join as join30, resolve as resolve10 } from "pathe";
|
|
11588
11988
|
|
|
11589
11989
|
// src/deploy/xml.ts
|
|
11590
11990
|
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -11688,6 +12088,19 @@ var askApiUrl = (endpoint, site, abs) => {
|
|
|
11688
12088
|
}
|
|
11689
12089
|
return site && endpoint.startsWith("/") ? `${site.replace(/\/+$/u, "")}${endpoint}` : endpoint;
|
|
11690
12090
|
};
|
|
12091
|
+
var wellKnownArtifacts = (config, abs) => {
|
|
12092
|
+
const artifacts = {};
|
|
12093
|
+
if (config.ai.webBotAuth.keys.length > 0) {
|
|
12094
|
+
artifacts.httpMessageSignaturesDirectory = abs("/.well-known/http-message-signatures-directory");
|
|
12095
|
+
}
|
|
12096
|
+
if (hasApiCatalog(config)) {
|
|
12097
|
+
artifacts.apiCatalog = abs("/.well-known/api-catalog");
|
|
12098
|
+
}
|
|
12099
|
+
if (config.ai.skills) {
|
|
12100
|
+
artifacts.agentSkills = abs("/.well-known/agent-skills/index.json");
|
|
12101
|
+
}
|
|
12102
|
+
return artifacts;
|
|
12103
|
+
};
|
|
11691
12104
|
var buildAgentReadability = (project) => {
|
|
11692
12105
|
const { config } = project;
|
|
11693
12106
|
if (!config.seo.agentReadability) {
|
|
@@ -11699,9 +12112,10 @@ var buildAgentReadability = (project) => {
|
|
|
11699
12112
|
const based = withBasePath(deployBase, path);
|
|
11700
12113
|
return site ? `${site.replace(/\/+$/u, "")}${based}` : based;
|
|
11701
12114
|
};
|
|
12115
|
+
const negotiates = config.deployment.output === "server" && config.deployment.adapter === "vercel";
|
|
11702
12116
|
const artifacts = {
|
|
11703
12117
|
markdown: {
|
|
11704
|
-
contentNegotiation: "text/markdown",
|
|
12118
|
+
...negotiates ? { contentNegotiation: "text/markdown" } : {},
|
|
11705
12119
|
pattern: abs("/{route}.md")
|
|
11706
12120
|
}
|
|
11707
12121
|
};
|
|
@@ -11718,6 +12132,7 @@ var buildAgentReadability = (project) => {
|
|
|
11718
12132
|
if (config.ai.ask?.enabled) {
|
|
11719
12133
|
artifacts.askApi = askApiUrl(config.ai.ask.endpoint, site, abs);
|
|
11720
12134
|
}
|
|
12135
|
+
Object.assign(artifacts, wellKnownArtifacts(config, abs));
|
|
11721
12136
|
if (site && config.seo.sitemap) {
|
|
11722
12137
|
artifacts.sitemap = abs("/sitemap.xml");
|
|
11723
12138
|
}
|
|
@@ -11743,8 +12158,113 @@ var buildAgentReadability = (project) => {
|
|
|
11743
12158
|
return manifest;
|
|
11744
12159
|
};
|
|
11745
12160
|
|
|
12161
|
+
// src/core/content-assets.ts
|
|
12162
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
12163
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
12164
|
+
import { dirname as dirname7, extname as extname5, relative as relative11, resolve as resolve5 } from "pathe";
|
|
12165
|
+
var CONTENT_ASSETS_PREFIX = "/blume-assets/content";
|
|
12166
|
+
var IMAGE_EXTENSIONS = new Set([
|
|
12167
|
+
".apng",
|
|
12168
|
+
".avif",
|
|
12169
|
+
".bmp",
|
|
12170
|
+
".gif",
|
|
12171
|
+
".ico",
|
|
12172
|
+
".jpeg",
|
|
12173
|
+
".jpg",
|
|
12174
|
+
".png",
|
|
12175
|
+
".svg",
|
|
12176
|
+
".tiff",
|
|
12177
|
+
".webp"
|
|
12178
|
+
]);
|
|
12179
|
+
var isRelativeTarget = (target) => !(target.startsWith("/") || target.startsWith("#")) && !URL.canParse(target);
|
|
12180
|
+
var contentAssetParam = (projectRoot, absPath) => {
|
|
12181
|
+
const rel = relative11(projectRoot, absPath);
|
|
12182
|
+
if (rel.startsWith("..")) {
|
|
12183
|
+
return `_/${hashText(absPath)}${extname5(absPath)}`;
|
|
12184
|
+
}
|
|
12185
|
+
return rel;
|
|
12186
|
+
};
|
|
12187
|
+
var resolveRelativeImage = (sourceDir, target) => {
|
|
12188
|
+
if (!isRelativeTarget(target)) {
|
|
12189
|
+
return null;
|
|
12190
|
+
}
|
|
12191
|
+
let decoded = target;
|
|
12192
|
+
try {
|
|
12193
|
+
decoded = decodeURI(target);
|
|
12194
|
+
} catch {}
|
|
12195
|
+
if (!IMAGE_EXTENSIONS.has(extname5(decoded).toLowerCase())) {
|
|
12196
|
+
return null;
|
|
12197
|
+
}
|
|
12198
|
+
const abs = resolve5(sourceDir, decoded);
|
|
12199
|
+
return existsSync9(abs) ? abs : null;
|
|
12200
|
+
};
|
|
12201
|
+
var encodedParam = (param) => param.split("/").map(encodeURIComponent).join("/");
|
|
12202
|
+
var rewriteLine = (line, toUrl2) => {
|
|
12203
|
+
const masked = line.replaceAll(INLINE_CODE, (span) => " ".repeat(span.length));
|
|
12204
|
+
let out = "";
|
|
12205
|
+
let cursor = 0;
|
|
12206
|
+
for (const match of masked.matchAll(MD_IMAGE)) {
|
|
12207
|
+
const target = match.groups?.target ?? "";
|
|
12208
|
+
const url = toUrl2(target);
|
|
12209
|
+
if (url === null) {
|
|
12210
|
+
continue;
|
|
12211
|
+
}
|
|
12212
|
+
const offset = (match.index ?? 0) + targetOffsetIn(match[0], target, match.groups?.title);
|
|
12213
|
+
out += line.slice(cursor, offset) + url;
|
|
12214
|
+
cursor = offset + target.length;
|
|
12215
|
+
}
|
|
12216
|
+
return out + line.slice(cursor);
|
|
12217
|
+
};
|
|
12218
|
+
var rewriteRelativeImages = (options) => {
|
|
12219
|
+
const { source, sourcePath, projectRoot, deployBase, register } = options;
|
|
12220
|
+
const sourceDir = dirname7(sourcePath);
|
|
12221
|
+
const prefix = `${normalizeBasePath(deployBase)}${CONTENT_ASSETS_PREFIX}`;
|
|
12222
|
+
const toUrl2 = (target) => {
|
|
12223
|
+
const abs = resolveRelativeImage(sourceDir, target);
|
|
12224
|
+
if (abs === null) {
|
|
12225
|
+
return null;
|
|
12226
|
+
}
|
|
12227
|
+
const param = contentAssetParam(projectRoot, abs);
|
|
12228
|
+
register?.(param, abs);
|
|
12229
|
+
return `${prefix}/${encodedParam(param)}`;
|
|
12230
|
+
};
|
|
12231
|
+
let fence = null;
|
|
12232
|
+
const lines = source.split(`
|
|
12233
|
+
`).map((line) => {
|
|
12234
|
+
const next = nextFenceState(line, fence);
|
|
12235
|
+
const inFence = fence !== null || next !== null;
|
|
12236
|
+
fence = next;
|
|
12237
|
+
return inFence ? line : rewriteLine(line, toUrl2);
|
|
12238
|
+
});
|
|
12239
|
+
return lines.join(`
|
|
12240
|
+
`);
|
|
12241
|
+
};
|
|
12242
|
+
var collectContentAssets = async (project) => {
|
|
12243
|
+
const files = {};
|
|
12244
|
+
await Promise.all(project.manifest.routes.map(async (route) => {
|
|
12245
|
+
if (!route.sourcePath) {
|
|
12246
|
+
return;
|
|
12247
|
+
}
|
|
12248
|
+
let source;
|
|
12249
|
+
try {
|
|
12250
|
+
source = await readFile8(route.sourcePath, "utf-8");
|
|
12251
|
+
} catch {
|
|
12252
|
+
return;
|
|
12253
|
+
}
|
|
12254
|
+
rewriteRelativeImages({
|
|
12255
|
+
projectRoot: project.context.root,
|
|
12256
|
+
register: (param, abs) => {
|
|
12257
|
+
files[param] = abs;
|
|
12258
|
+
},
|
|
12259
|
+
source,
|
|
12260
|
+
sourcePath: route.sourcePath
|
|
12261
|
+
});
|
|
12262
|
+
}));
|
|
12263
|
+
return files;
|
|
12264
|
+
};
|
|
12265
|
+
|
|
11746
12266
|
// src/core/sources/read.ts
|
|
11747
|
-
import { readFile as
|
|
12267
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
11748
12268
|
var readEntryText = async (ctx, page) => {
|
|
11749
12269
|
if (page.body) {
|
|
11750
12270
|
return page.body.text;
|
|
@@ -11755,7 +12275,7 @@ var readEntryText = async (ctx, page) => {
|
|
|
11755
12275
|
return await source.read(page.source.ref);
|
|
11756
12276
|
}
|
|
11757
12277
|
if (page.sourcePath) {
|
|
11758
|
-
return await
|
|
12278
|
+
return await readFile9(page.sourcePath, "utf-8");
|
|
11759
12279
|
}
|
|
11760
12280
|
return "";
|
|
11761
12281
|
};
|
|
@@ -12154,7 +12674,15 @@ var buildFull = async (project) => {
|
|
|
12154
12674
|
...config.ai.markdownComponents
|
|
12155
12675
|
};
|
|
12156
12676
|
const sections = await Promise.all(pages.map(async (page) => {
|
|
12157
|
-
|
|
12677
|
+
let raw = await readEntryText(project, page);
|
|
12678
|
+
if (page.sourcePath) {
|
|
12679
|
+
raw = rewriteRelativeImages({
|
|
12680
|
+
deployBase: config.deployment.base,
|
|
12681
|
+
projectRoot: project.context.root,
|
|
12682
|
+
source: raw,
|
|
12683
|
+
sourcePath: page.sourcePath
|
|
12684
|
+
});
|
|
12685
|
+
}
|
|
12158
12686
|
const parsed = frontmatter_default(raw);
|
|
12159
12687
|
const body = downlevelComponents(applyAgentVisibility(parsed.content), components, parsed.data).trim();
|
|
12160
12688
|
const url = pageUrl(page.route, config.deployment.site, normalizeBasePath(config.deployment.base));
|
|
@@ -12179,14 +12707,200 @@ var buildLlmsFiles = async (project) => ({
|
|
|
12179
12707
|
index: buildIndex(project)
|
|
12180
12708
|
});
|
|
12181
12709
|
|
|
12182
|
-
// src/
|
|
12183
|
-
import {
|
|
12184
|
-
import { readFile as
|
|
12710
|
+
// src/ai/skills.ts
|
|
12711
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
12712
|
+
import { readdir, readFile as readFile10, stat as stat2 } from "node:fs/promises";
|
|
12185
12713
|
import { join as join18 } from "pathe";
|
|
12714
|
+
|
|
12715
|
+
// src/ai/tar.ts
|
|
12716
|
+
import { gzipSync } from "node:zlib";
|
|
12717
|
+
var BLOCK = 512;
|
|
12718
|
+
var NAME_MAX = 100;
|
|
12719
|
+
var encoder = new TextEncoder;
|
|
12720
|
+
var octal = (header, offset, length, value) => {
|
|
12721
|
+
const text = value.toString(8).padStart(length - 1, "0");
|
|
12722
|
+
header.set(encoder.encode(text), offset);
|
|
12723
|
+
header[offset + length - 1] = 0;
|
|
12724
|
+
};
|
|
12725
|
+
var text = (header, offset, value) => {
|
|
12726
|
+
header.set(encoder.encode(value), offset);
|
|
12727
|
+
};
|
|
12728
|
+
var fileHeader = (entry) => {
|
|
12729
|
+
const header = new Uint8Array(BLOCK);
|
|
12730
|
+
text(header, 0, entry.path);
|
|
12731
|
+
octal(header, 100, 8, entry.executable ? 493 : 420);
|
|
12732
|
+
octal(header, 108, 8, 0);
|
|
12733
|
+
octal(header, 116, 8, 0);
|
|
12734
|
+
octal(header, 124, 12, entry.content.byteLength);
|
|
12735
|
+
octal(header, 136, 12, 0);
|
|
12736
|
+
text(header, 156, "0");
|
|
12737
|
+
text(header, 257, "ustar");
|
|
12738
|
+
header[262] = 0;
|
|
12739
|
+
text(header, 263, "00");
|
|
12740
|
+
octal(header, 329, 8, 0);
|
|
12741
|
+
octal(header, 337, 8, 0);
|
|
12742
|
+
header.fill(32, 148, 156);
|
|
12743
|
+
let sum = 0;
|
|
12744
|
+
for (const byte of header) {
|
|
12745
|
+
sum += byte;
|
|
12746
|
+
}
|
|
12747
|
+
text(header, 148, sum.toString(8).padStart(6, "0"));
|
|
12748
|
+
header[154] = 0;
|
|
12749
|
+
header[155] = 32;
|
|
12750
|
+
return header;
|
|
12751
|
+
};
|
|
12752
|
+
var buildTarGz = (entries) => {
|
|
12753
|
+
const blocks = [];
|
|
12754
|
+
for (const entry of entries) {
|
|
12755
|
+
if (encoder.encode(entry.path).byteLength > NAME_MAX) {
|
|
12756
|
+
throw new Error(`tar path exceeds ${NAME_MAX} bytes: ${entry.path}`);
|
|
12757
|
+
}
|
|
12758
|
+
if (entry.path.startsWith("/") || entry.path.split("/").includes("..")) {
|
|
12759
|
+
throw new Error(`tar path must be archive-relative: ${entry.path}`);
|
|
12760
|
+
}
|
|
12761
|
+
blocks.push(fileHeader(entry), entry.content);
|
|
12762
|
+
const overhang = entry.content.byteLength % BLOCK;
|
|
12763
|
+
if (overhang > 0) {
|
|
12764
|
+
blocks.push(new Uint8Array(BLOCK - overhang));
|
|
12765
|
+
}
|
|
12766
|
+
}
|
|
12767
|
+
blocks.push(new Uint8Array(BLOCK * 2));
|
|
12768
|
+
const total = blocks.reduce((sum, block) => sum + block.byteLength, 0);
|
|
12769
|
+
const tar = new Uint8Array(total);
|
|
12770
|
+
let offset = 0;
|
|
12771
|
+
for (const block of blocks) {
|
|
12772
|
+
tar.set(block, offset);
|
|
12773
|
+
offset += block.byteLength;
|
|
12774
|
+
}
|
|
12775
|
+
return new Uint8Array(gzipSync(tar, { level: 9 }));
|
|
12776
|
+
};
|
|
12777
|
+
|
|
12778
|
+
// src/ai/skills.ts
|
|
12779
|
+
var AGENT_SKILLS_DIR = "/.well-known/agent-skills";
|
|
12780
|
+
var AGENT_SKILLS_SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
|
|
12781
|
+
var SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
12782
|
+
var SKILL_NAME_MAX = 64;
|
|
12783
|
+
var DESCRIPTION_MAX2 = 1024;
|
|
12784
|
+
var sha256 = (content) => `sha256:${createHash2("sha256").update(content).digest("hex")}`;
|
|
12785
|
+
var collectEntries = async (dir, prefix = "") => {
|
|
12786
|
+
const entries = [];
|
|
12787
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
12788
|
+
for (const item of items.toSorted((a, b) => a.name < b.name ? -1 : 1)) {
|
|
12789
|
+
if (item.name.startsWith(".")) {
|
|
12790
|
+
continue;
|
|
12791
|
+
}
|
|
12792
|
+
const path = join18(dir, item.name);
|
|
12793
|
+
const relative12 = prefix ? `${prefix}/${item.name}` : item.name;
|
|
12794
|
+
if (item.isDirectory()) {
|
|
12795
|
+
entries.push(...await collectEntries(path, relative12));
|
|
12796
|
+
} else if (item.isFile()) {
|
|
12797
|
+
const [content, info] = await Promise.all([readFile10(path), stat2(path)]);
|
|
12798
|
+
entries.push({
|
|
12799
|
+
content: new Uint8Array(content),
|
|
12800
|
+
executable: (info.mode & 64) !== 0,
|
|
12801
|
+
path: relative12
|
|
12802
|
+
});
|
|
12803
|
+
}
|
|
12804
|
+
}
|
|
12805
|
+
return entries;
|
|
12806
|
+
};
|
|
12807
|
+
var skillMeta = (raw, dirName) => {
|
|
12808
|
+
let data;
|
|
12809
|
+
try {
|
|
12810
|
+
({ data } = frontmatter_default(raw));
|
|
12811
|
+
} catch {
|
|
12812
|
+
return {
|
|
12813
|
+
meta: null,
|
|
12814
|
+
warning: `Skill "${dirName}" has unparsable SKILL.md frontmatter; skipped.`
|
|
12815
|
+
};
|
|
12816
|
+
}
|
|
12817
|
+
const name = typeof data.name === "string" ? data.name : "";
|
|
12818
|
+
const description = typeof data.description === "string" ? data.description : "";
|
|
12819
|
+
if (!(name && description)) {
|
|
12820
|
+
return {
|
|
12821
|
+
meta: null,
|
|
12822
|
+
warning: `Skill "${dirName}" is missing the required "name"/"description" frontmatter; skipped.`
|
|
12823
|
+
};
|
|
12824
|
+
}
|
|
12825
|
+
if (!SKILL_NAME.test(name) || name.length > SKILL_NAME_MAX) {
|
|
12826
|
+
return {
|
|
12827
|
+
meta: null,
|
|
12828
|
+
warning: `Skill "${dirName}" has an invalid name "${name}" (lowercase alphanumerics and single hyphens, max 64 chars); skipped.`
|
|
12829
|
+
};
|
|
12830
|
+
}
|
|
12831
|
+
return {
|
|
12832
|
+
meta: { description: description.slice(0, DESCRIPTION_MAX2), name }
|
|
12833
|
+
};
|
|
12834
|
+
};
|
|
12835
|
+
var collectSkills = async (dir) => {
|
|
12836
|
+
const skills = [];
|
|
12837
|
+
const warnings = [];
|
|
12838
|
+
const items = await readdir(dir, { withFileTypes: true });
|
|
12839
|
+
for (const item of items.toSorted((a, b) => a.name < b.name ? -1 : 1)) {
|
|
12840
|
+
if (!item.isDirectory() || item.name.startsWith(".")) {
|
|
12841
|
+
continue;
|
|
12842
|
+
}
|
|
12843
|
+
const entries = await collectEntries(join18(dir, item.name));
|
|
12844
|
+
const skillMd = entries.find((entry) => entry.path === "SKILL.md");
|
|
12845
|
+
if (!skillMd) {
|
|
12846
|
+
continue;
|
|
12847
|
+
}
|
|
12848
|
+
const { meta, warning } = skillMeta(new TextDecoder().decode(skillMd.content), item.name);
|
|
12849
|
+
if (!meta) {
|
|
12850
|
+
if (warning) {
|
|
12851
|
+
warnings.push(warning);
|
|
12852
|
+
}
|
|
12853
|
+
continue;
|
|
12854
|
+
}
|
|
12855
|
+
const single = entries.length === 1;
|
|
12856
|
+
const content = single ? skillMd.content : buildTarGz(entries);
|
|
12857
|
+
skills.push({
|
|
12858
|
+
content,
|
|
12859
|
+
description: meta.description,
|
|
12860
|
+
digest: sha256(content),
|
|
12861
|
+
name: meta.name,
|
|
12862
|
+
path: single ? `${meta.name}/SKILL.md` : `${meta.name}.tar.gz`,
|
|
12863
|
+
type: single ? "skill-md" : "archive"
|
|
12864
|
+
});
|
|
12865
|
+
}
|
|
12866
|
+
return { skills, warnings };
|
|
12867
|
+
};
|
|
12868
|
+
var buildSkillsIndex = (skills, config) => {
|
|
12869
|
+
const deployBase = normalizeBasePath(config.deployment.base);
|
|
12870
|
+
const index = {
|
|
12871
|
+
$schema: AGENT_SKILLS_SCHEMA,
|
|
12872
|
+
skills: skills.map((skill) => ({
|
|
12873
|
+
description: skill.description,
|
|
12874
|
+
digest: skill.digest,
|
|
12875
|
+
name: skill.name,
|
|
12876
|
+
type: skill.type,
|
|
12877
|
+
url: `${deployBase}${AGENT_SKILLS_DIR}/${skill.path}`
|
|
12878
|
+
}))
|
|
12879
|
+
};
|
|
12880
|
+
return `${JSON.stringify(index, null, 2)}
|
|
12881
|
+
`;
|
|
12882
|
+
};
|
|
12883
|
+
|
|
12884
|
+
// src/ai/web-bot-auth.ts
|
|
12885
|
+
var SIGNATURES_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
12886
|
+
var SIGNATURES_DIRECTORY_TYPE = "application/http-message-signatures-directory+json";
|
|
12887
|
+
var buildSignaturesDirectory = (config) => {
|
|
12888
|
+
const { keys } = config.ai.webBotAuth;
|
|
12889
|
+
if (keys.length === 0) {
|
|
12890
|
+
return null;
|
|
12891
|
+
}
|
|
12892
|
+
return `${JSON.stringify({ keys }, null, 2)}
|
|
12893
|
+
`;
|
|
12894
|
+
};
|
|
12895
|
+
|
|
12896
|
+
// src/core/gitignore.ts
|
|
12897
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
12898
|
+
import { readFile as readFile11, writeFile as writeFile6 } from "node:fs/promises";
|
|
12899
|
+
import { join as join19 } from "pathe";
|
|
12186
12900
|
var gitignoreKey = (line) => line.trim().replace(/\/+$/u, "");
|
|
12187
12901
|
var ensureGitignore = async (root, entries) => {
|
|
12188
|
-
const path =
|
|
12189
|
-
const existing =
|
|
12902
|
+
const path = join19(root, ".gitignore");
|
|
12903
|
+
const existing = existsSync10(path) ? await readFile11(path, "utf-8") : "";
|
|
12190
12904
|
const present = new Set(existing.split(`
|
|
12191
12905
|
`).flatMap((line) => {
|
|
12192
12906
|
const key = gitignoreKey(line);
|
|
@@ -12238,13 +12952,31 @@ var HEADER_RULES = [
|
|
|
12238
12952
|
underBasePath: false
|
|
12239
12953
|
}
|
|
12240
12954
|
];
|
|
12241
|
-
var buildNetlifyHeaders = (config) => {
|
|
12955
|
+
var buildNetlifyHeaders = (config, homeLinkHeader) => {
|
|
12242
12956
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
12243
|
-
|
|
12957
|
+
const rules = HEADER_RULES.map((rule) => {
|
|
12244
12958
|
const prefix = rule.underBasePath ? `${deployBase}${config.basePath}` : deployBase;
|
|
12245
12959
|
return `${prefix}/*.${rule.ext}
|
|
12246
12960
|
Content-Type: ${rule.contentType}`;
|
|
12247
|
-
})
|
|
12961
|
+
});
|
|
12962
|
+
if (homeLinkHeader) {
|
|
12963
|
+
rules.push(`${deployBase}/
|
|
12964
|
+
Link: ${homeLinkHeader}`);
|
|
12965
|
+
}
|
|
12966
|
+
if (hasApiCatalog(config)) {
|
|
12967
|
+
rules.push(`${deployBase}${API_CATALOG_PATH}
|
|
12968
|
+
Content-Type: ${API_CATALOG_TYPE}`);
|
|
12969
|
+
}
|
|
12970
|
+
if (config.ai.webBotAuth.keys.length > 0) {
|
|
12971
|
+
rules.push(`${deployBase}${SIGNATURES_DIRECTORY_PATH}
|
|
12972
|
+
Content-Type: ${SIGNATURES_DIRECTORY_TYPE}`);
|
|
12973
|
+
}
|
|
12974
|
+
if (config.ai.skills) {
|
|
12975
|
+
rules.push(`${deployBase}/.well-known/agent-skills/*.md
|
|
12976
|
+
Content-Type: text/markdown; charset=utf-8`, `${deployBase}/.well-known/agent-skills/*.tar.gz
|
|
12977
|
+
Content-Type: application/gzip`);
|
|
12978
|
+
}
|
|
12979
|
+
return `${rules.join(`
|
|
12248
12980
|
`)}
|
|
12249
12981
|
`;
|
|
12250
12982
|
};
|
|
@@ -12284,7 +13016,7 @@ var buildRobots = (project) => {
|
|
|
12284
13016
|
};
|
|
12285
13017
|
|
|
12286
13018
|
// src/astro/pages.ts
|
|
12287
|
-
import { extname as
|
|
13019
|
+
import { extname as extname6, relative as relative12 } from "pathe";
|
|
12288
13020
|
import { glob as glob4, globSync } from "tinyglobby";
|
|
12289
13021
|
var PAGE_GLOB = ["**/*.astro"];
|
|
12290
13022
|
var isPrivatePage = (rel) => rel.split("/").some((segment) => segment.startsWith("_"));
|
|
@@ -12292,11 +13024,11 @@ var toPageRoutes = (pagesRoot, files) => {
|
|
|
12292
13024
|
files.sort();
|
|
12293
13025
|
const routes = [];
|
|
12294
13026
|
for (const file of files) {
|
|
12295
|
-
const rel =
|
|
13027
|
+
const rel = relative12(pagesRoot, file);
|
|
12296
13028
|
if (isPrivatePage(rel)) {
|
|
12297
13029
|
continue;
|
|
12298
13030
|
}
|
|
12299
|
-
const withoutExt = rel.slice(0, rel.length -
|
|
13031
|
+
const withoutExt = rel.slice(0, rel.length - extname6(rel).length);
|
|
12300
13032
|
const parts = withoutExt.split("/");
|
|
12301
13033
|
if (parts.at(-1) === "index") {
|
|
12302
13034
|
parts.pop();
|
|
@@ -12408,8 +13140,102 @@ ${urls.join(`
|
|
|
12408
13140
|
`;
|
|
12409
13141
|
};
|
|
12410
13142
|
|
|
13143
|
+
// src/deploy/vercel-negotiation.ts
|
|
13144
|
+
var ACCEPT_MARKDOWN_HEADER_VALUE = "(.*,)?\\s*text/(x-)?markdown(\\s*[;,].*)?$";
|
|
13145
|
+
var ACCEPT_MARKDOWN_CONDITION = [
|
|
13146
|
+
{ key: "accept", type: "header", value: ACCEPT_MARKDOWN_HEADER_VALUE }
|
|
13147
|
+
];
|
|
13148
|
+
var VARY_ACCEPT = { vary: "Accept" };
|
|
13149
|
+
var MAX_ALTERNATION_LENGTH = 3900;
|
|
13150
|
+
var REGEX_SPECIALS = /[$()*+.?[\]^{|}\\]/gu;
|
|
13151
|
+
var routePattern = (route) => encodeURI(route).replace(REGEX_SPECIALS, "\\$&");
|
|
13152
|
+
var chunkPatterns = (patterns) => {
|
|
13153
|
+
const chunks = [];
|
|
13154
|
+
let current = [];
|
|
13155
|
+
let length = 0;
|
|
13156
|
+
for (const pattern of patterns) {
|
|
13157
|
+
if (current.length > 0 && length + pattern.length + 1 > MAX_ALTERNATION_LENGTH) {
|
|
13158
|
+
chunks.push(current);
|
|
13159
|
+
current = [];
|
|
13160
|
+
length = 0;
|
|
13161
|
+
}
|
|
13162
|
+
current.push(pattern);
|
|
13163
|
+
length += pattern.length + 1;
|
|
13164
|
+
}
|
|
13165
|
+
if (current.length > 0) {
|
|
13166
|
+
chunks.push(current);
|
|
13167
|
+
}
|
|
13168
|
+
return chunks;
|
|
13169
|
+
};
|
|
13170
|
+
var buildNegotiationRoutes = (routePaths) => {
|
|
13171
|
+
const home = routePaths.includes("/");
|
|
13172
|
+
const rest = routePaths.filter((path) => path !== "/").map((path) => routePattern(path));
|
|
13173
|
+
const chunks = chunkPatterns(rest);
|
|
13174
|
+
const rewriteRoutes = home ? [
|
|
13175
|
+
{
|
|
13176
|
+
dest: "/index.md",
|
|
13177
|
+
has: ACCEPT_MARKDOWN_CONDITION,
|
|
13178
|
+
headers: VARY_ACCEPT,
|
|
13179
|
+
src: "^/$"
|
|
13180
|
+
}
|
|
13181
|
+
] : [];
|
|
13182
|
+
for (const chunk of chunks) {
|
|
13183
|
+
rewriteRoutes.push({
|
|
13184
|
+
dest: "$1.md",
|
|
13185
|
+
has: ACCEPT_MARKDOWN_CONDITION,
|
|
13186
|
+
headers: VARY_ACCEPT,
|
|
13187
|
+
src: `^(${chunk.join("|")})/?$`
|
|
13188
|
+
});
|
|
13189
|
+
}
|
|
13190
|
+
const headerChunks = chunkPatterns(home ? ["/", ...rest] : rest);
|
|
13191
|
+
const headerRoutes = headerChunks.map((chunk) => ({
|
|
13192
|
+
continue: true,
|
|
13193
|
+
headers: VARY_ACCEPT,
|
|
13194
|
+
src: `^(?:${chunk.join("|")})/?$`
|
|
13195
|
+
}));
|
|
13196
|
+
return { headerRoutes, rewriteRoutes };
|
|
13197
|
+
};
|
|
13198
|
+
var HOME_SRC = "^/$";
|
|
13199
|
+
var isNegotiationRoute = (route) => route.has?.some((condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE) === true || route.continue === true && route.headers?.vary === "Accept" && typeof route.src === "string" && Object.keys(route).length === 3 || route.continue === true && typeof route.headers?.link === "string" && route.src === HOME_SRC && Object.keys(route).length === 3;
|
|
13200
|
+
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides) => {
|
|
13201
|
+
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
13202
|
+
if (routePaths.length === 0 && !homeLinkHeader && overrideEntries.length === 0) {
|
|
13203
|
+
return null;
|
|
13204
|
+
}
|
|
13205
|
+
let config;
|
|
13206
|
+
try {
|
|
13207
|
+
config = JSON.parse(configText);
|
|
13208
|
+
} catch {
|
|
13209
|
+
return null;
|
|
13210
|
+
}
|
|
13211
|
+
if (!Array.isArray(config.routes)) {
|
|
13212
|
+
return null;
|
|
13213
|
+
}
|
|
13214
|
+
for (const [path, contentType] of overrideEntries) {
|
|
13215
|
+
config.overrides = { ...config.overrides, [path]: { contentType } };
|
|
13216
|
+
}
|
|
13217
|
+
const routes = config.routes.filter((route) => !isNegotiationRoute(route));
|
|
13218
|
+
const filesystemIndex = routes.findIndex((route) => route.handle === "filesystem");
|
|
13219
|
+
if (filesystemIndex === -1) {
|
|
13220
|
+
return null;
|
|
13221
|
+
}
|
|
13222
|
+
const { headerRoutes, rewriteRoutes } = buildNegotiationRoutes(routePaths);
|
|
13223
|
+
if (homeLinkHeader) {
|
|
13224
|
+
headerRoutes.push({
|
|
13225
|
+
continue: true,
|
|
13226
|
+
headers: { link: homeLinkHeader },
|
|
13227
|
+
src: HOME_SRC
|
|
13228
|
+
});
|
|
13229
|
+
}
|
|
13230
|
+
routes.splice(filesystemIndex + 1, 0, ...headerRoutes);
|
|
13231
|
+
routes.splice(filesystemIndex, 0, ...rewriteRoutes);
|
|
13232
|
+
config.routes = routes;
|
|
13233
|
+
return `${JSON.stringify(config, null, "\t")}
|
|
13234
|
+
`;
|
|
13235
|
+
};
|
|
13236
|
+
|
|
12411
13237
|
// src/search/build.ts
|
|
12412
|
-
import { join as
|
|
13238
|
+
import { join as join20 } from "pathe";
|
|
12413
13239
|
var buildSearchIndex = async (outDir) => {
|
|
12414
13240
|
const pagefind = await import("pagefind");
|
|
12415
13241
|
const { index } = await pagefind.createIndex({});
|
|
@@ -12417,7 +13243,7 @@ var buildSearchIndex = async (outDir) => {
|
|
|
12417
13243
|
throw new Error("Failed to create Pagefind index.");
|
|
12418
13244
|
}
|
|
12419
13245
|
const result = await index.addDirectory({ path: outDir });
|
|
12420
|
-
await index.writeFiles({ outputPath:
|
|
13246
|
+
await index.writeFiles({ outputPath: join20(outDir, "pagefind") });
|
|
12421
13247
|
await pagefind.close();
|
|
12422
13248
|
return result.page_count;
|
|
12423
13249
|
};
|
|
@@ -12640,14 +13466,14 @@ var syncSearchProvider = async (project, reporter) => {
|
|
|
12640
13466
|
|
|
12641
13467
|
// src/cli/dev-lock.ts
|
|
12642
13468
|
import {
|
|
12643
|
-
existsSync as
|
|
13469
|
+
existsSync as existsSync11,
|
|
12644
13470
|
mkdirSync,
|
|
12645
13471
|
readFileSync as readFileSync5,
|
|
12646
13472
|
rmSync,
|
|
12647
13473
|
writeFileSync
|
|
12648
13474
|
} from "node:fs";
|
|
12649
|
-
import { join as
|
|
12650
|
-
var lockPath = (outDir) =>
|
|
13475
|
+
import { join as join21 } from "pathe";
|
|
13476
|
+
var lockPath = (outDir) => join21(outDir, "dev.lock");
|
|
12651
13477
|
var isValidPid = (pid) => typeof pid === "number" && Number.isInteger(pid) && pid > 0;
|
|
12652
13478
|
var parseLock = (raw) => {
|
|
12653
13479
|
let data;
|
|
@@ -12677,7 +13503,7 @@ var isProcessAlive = (pid) => {
|
|
|
12677
13503
|
};
|
|
12678
13504
|
var readDevLock = (outDir) => {
|
|
12679
13505
|
const path = lockPath(outDir);
|
|
12680
|
-
if (!
|
|
13506
|
+
if (!existsSync11(path)) {
|
|
12681
13507
|
return null;
|
|
12682
13508
|
}
|
|
12683
13509
|
const lock = parseLock(readFileSync5(path, "utf-8"));
|
|
@@ -12701,7 +13527,7 @@ class DevLockHeldError extends Error {
|
|
|
12701
13527
|
}
|
|
12702
13528
|
var ownsLock = (outDir) => {
|
|
12703
13529
|
const path = lockPath(outDir);
|
|
12704
|
-
if (!
|
|
13530
|
+
if (!existsSync11(path)) {
|
|
12705
13531
|
return false;
|
|
12706
13532
|
}
|
|
12707
13533
|
try {
|
|
@@ -12758,12 +13584,12 @@ var refuseIfDevRunning = (root, action, options = {}) => {
|
|
|
12758
13584
|
};
|
|
12759
13585
|
|
|
12760
13586
|
// src/astro/generate.ts
|
|
12761
|
-
import { createHash as
|
|
12762
|
-
import { existsSync as
|
|
13587
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
13588
|
+
import { existsSync as existsSync16, readFileSync as readFileSync9, realpathSync } from "node:fs";
|
|
12763
13589
|
import {
|
|
12764
13590
|
lstat,
|
|
12765
13591
|
mkdir as mkdir6,
|
|
12766
|
-
readFile as
|
|
13592
|
+
readFile as readFile16,
|
|
12767
13593
|
readlink,
|
|
12768
13594
|
realpath,
|
|
12769
13595
|
rename,
|
|
@@ -12773,7 +13599,7 @@ import {
|
|
|
12773
13599
|
} from "node:fs/promises";
|
|
12774
13600
|
import { createRequire as createRequire5 } from "node:module";
|
|
12775
13601
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
12776
|
-
import { basename as basename3, dirname as
|
|
13602
|
+
import { basename as basename3, dirname as dirname10, join as join28, normalize as normalize3, relative as relative14, resolve as resolve8 } from "pathe";
|
|
12777
13603
|
import { glob as glob7 } from "tinyglobby";
|
|
12778
13604
|
|
|
12779
13605
|
// src/ai/ask-data.ts
|
|
@@ -12784,6 +13610,7 @@ var buildAskData = async (project) => {
|
|
|
12784
13610
|
includeWhenDisabled: true
|
|
12785
13611
|
});
|
|
12786
13612
|
return {
|
|
13613
|
+
defaultLocale: project.config.i18n?.defaultLocale,
|
|
12787
13614
|
documents: documents.map((doc) => ({
|
|
12788
13615
|
content: doc.content,
|
|
12789
13616
|
description: doc.description,
|
|
@@ -12796,7 +13623,7 @@ var buildAskData = async (project) => {
|
|
|
12796
13623
|
};
|
|
12797
13624
|
|
|
12798
13625
|
// src/ai/markdown.ts
|
|
12799
|
-
import { readFile as
|
|
13626
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
12800
13627
|
var agentMarkdown = (entry) => entry.md ?? entry.mdx;
|
|
12801
13628
|
var buildRawMarkdown = async (project) => {
|
|
12802
13629
|
const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
|
|
@@ -12809,10 +13636,19 @@ var buildRawMarkdown = async (project) => {
|
|
|
12809
13636
|
if (page) {
|
|
12810
13637
|
return await readEntryText(project, page);
|
|
12811
13638
|
}
|
|
12812
|
-
return route.sourcePath ? await
|
|
13639
|
+
return route.sourcePath ? await readFile12(route.sourcePath, "utf-8") : "";
|
|
12813
13640
|
};
|
|
12814
13641
|
const entries = await Promise.all(project.manifest.routes.map(async (route) => {
|
|
12815
|
-
|
|
13642
|
+
let text2 = await readRoute(route);
|
|
13643
|
+
if (route.sourcePath) {
|
|
13644
|
+
text2 = rewriteRelativeImages({
|
|
13645
|
+
deployBase: project.config.deployment.base,
|
|
13646
|
+
projectRoot: project.context.root,
|
|
13647
|
+
source: text2,
|
|
13648
|
+
sourcePath: route.sourcePath
|
|
13649
|
+
});
|
|
13650
|
+
}
|
|
13651
|
+
const source = applyAgentVisibility(text2);
|
|
12816
13652
|
const md = downlevelComponents(source, components, frontmatter_default(source).data);
|
|
12817
13653
|
const entry = md === source ? { mdx: source } : { md, mdx: source };
|
|
12818
13654
|
return [route.path, entry];
|
|
@@ -12851,6 +13687,7 @@ var buildMcpData = async (project) => {
|
|
|
12851
13687
|
}
|
|
12852
13688
|
return {
|
|
12853
13689
|
base: normalizeBasePath(config.deployment.base),
|
|
13690
|
+
defaultLocale: config.i18n?.defaultLocale,
|
|
12854
13691
|
documents: documents.map((doc) => ({
|
|
12855
13692
|
content: doc.content,
|
|
12856
13693
|
description: doc.description,
|
|
@@ -12910,19 +13747,42 @@ var buildMcpDiscovery = (input) => ({
|
|
|
12910
13747
|
}
|
|
12911
13748
|
]
|
|
12912
13749
|
});
|
|
12913
|
-
var
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
|
|
12920
|
-
|
|
12921
|
-
|
|
12922
|
-
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
13750
|
+
var SERVER_CARD_SCHEMA = "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json";
|
|
13751
|
+
var CARD_TEXT_MAX = 100;
|
|
13752
|
+
var truncate = (text2) => text2.length > CARD_TEXT_MAX ? `${text2.slice(0, CARD_TEXT_MAX - 1)}…` : text2;
|
|
13753
|
+
var reverseDnsName = (input) => {
|
|
13754
|
+
let namespace = "localhost";
|
|
13755
|
+
if (input.site) {
|
|
13756
|
+
try {
|
|
13757
|
+
namespace = new URL(input.site).hostname.split(".").toReversed().join(".");
|
|
13758
|
+
} catch {}
|
|
13759
|
+
}
|
|
13760
|
+
return `${namespace}/${slugify(input.name) || "docs"}`;
|
|
13761
|
+
};
|
|
13762
|
+
var HTTP_URL2 = /^https?:\/\//u;
|
|
13763
|
+
var buildMcpServerCard = (input) => {
|
|
13764
|
+
const url = serverUrl(input);
|
|
13765
|
+
return {
|
|
13766
|
+
$schema: SERVER_CARD_SCHEMA,
|
|
13767
|
+
capabilities: { tools: { listChanged: false } },
|
|
13768
|
+
description: truncate(`Model Context Protocol server for the ${input.name} documentation.`),
|
|
13769
|
+
name: reverseDnsName(input),
|
|
13770
|
+
...HTTP_URL2.test(url) ? { remotes: [{ type: "streamable-http", url }] } : {},
|
|
13771
|
+
serverInfo: { name: input.name, version: input.version },
|
|
13772
|
+
title: truncate(input.name),
|
|
13773
|
+
tools: MCP_TOOLS.map((tool) => ({
|
|
13774
|
+
annotations: tool.annotations,
|
|
13775
|
+
description: tool.description,
|
|
13776
|
+
name: tool.name,
|
|
13777
|
+
title: tool.title
|
|
13778
|
+
})),
|
|
13779
|
+
transport: "streamable-http",
|
|
13780
|
+
transports: [{ endpoint: url, type: "streamable-http" }],
|
|
13781
|
+
url,
|
|
13782
|
+
version: input.version,
|
|
13783
|
+
...input.site ? { websiteUrl: trimEnd(input.site, "/") } : {}
|
|
13784
|
+
};
|
|
13785
|
+
};
|
|
12926
13786
|
|
|
12927
13787
|
// src/core/builtin-tags.ts
|
|
12928
13788
|
var BUILTIN_MDX_TAGS = new Set([
|
|
@@ -12989,8 +13849,8 @@ var validateUsedComponents = (pages, extraTags, registryNames) => {
|
|
|
12989
13849
|
};
|
|
12990
13850
|
|
|
12991
13851
|
// src/core/component-overrides.ts
|
|
12992
|
-
import { existsSync as
|
|
12993
|
-
import { dirname as
|
|
13852
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
13853
|
+
import { dirname as dirname8, extname as extname7, isAbsolute as isAbsolute6, resolve as resolve6 } from "pathe";
|
|
12994
13854
|
import ts from "typescript";
|
|
12995
13855
|
var GROUPS = ["mdx", "layout", "islands"];
|
|
12996
13856
|
var GROUP_SET = new Set(GROUPS);
|
|
@@ -13082,24 +13942,24 @@ var findDefaultExportObject = (sourceFile) => {
|
|
|
13082
13942
|
var probeExtension = (base) => {
|
|
13083
13943
|
for (const extension of COMPONENT_EXTS) {
|
|
13084
13944
|
const candidate = `${base}.${extension}`;
|
|
13085
|
-
if (
|
|
13945
|
+
if (existsSync12(candidate)) {
|
|
13086
13946
|
return candidate;
|
|
13087
13947
|
}
|
|
13088
13948
|
}
|
|
13089
13949
|
return null;
|
|
13090
13950
|
};
|
|
13091
13951
|
var toImport = (specifier, imported, dir) => {
|
|
13092
|
-
const
|
|
13952
|
+
const relative13 = specifier.startsWith(".") || isAbsolute6(specifier);
|
|
13093
13953
|
let path = specifier;
|
|
13094
|
-
let extension =
|
|
13095
|
-
if (
|
|
13096
|
-
const absolute = isAbsolute6(specifier) ? specifier :
|
|
13954
|
+
let extension = extname7(specifier).slice(1).toLowerCase();
|
|
13955
|
+
if (relative13) {
|
|
13956
|
+
const absolute = isAbsolute6(specifier) ? specifier : resolve6(dir, specifier);
|
|
13097
13957
|
if (extension) {
|
|
13098
13958
|
path = absolute;
|
|
13099
13959
|
} else {
|
|
13100
13960
|
const probed = probeExtension(absolute);
|
|
13101
13961
|
path = probed ?? absolute;
|
|
13102
|
-
extension = probed ?
|
|
13962
|
+
extension = probed ? extname7(probed).slice(1).toLowerCase() : "";
|
|
13103
13963
|
}
|
|
13104
13964
|
}
|
|
13105
13965
|
return {
|
|
@@ -13248,7 +14108,7 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
13248
14108
|
return result;
|
|
13249
14109
|
}
|
|
13250
14110
|
const imports = collectImports(sourceFile);
|
|
13251
|
-
const dir =
|
|
14111
|
+
const dir = dirname8(filePath);
|
|
13252
14112
|
for (const property of object.properties) {
|
|
13253
14113
|
collectGroupOverrides(property, imports, dir, result);
|
|
13254
14114
|
}
|
|
@@ -13256,16 +14116,16 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
13256
14116
|
};
|
|
13257
14117
|
|
|
13258
14118
|
// src/core/tsconfig-aliases.ts
|
|
13259
|
-
import { existsSync as
|
|
14119
|
+
import { existsSync as existsSync13, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
13260
14120
|
import { createRequire as createRequire3 } from "node:module";
|
|
13261
14121
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
13262
|
-
import { dirname as
|
|
13263
|
-
var scanJsonChar = (
|
|
13264
|
-
const char =
|
|
14122
|
+
import { dirname as dirname9, isAbsolute as isAbsolute7, join as join22, resolve as resolve7 } from "pathe";
|
|
14123
|
+
var scanJsonChar = (text2, index, inString) => {
|
|
14124
|
+
const char = text2[index];
|
|
13265
14125
|
if (inString) {
|
|
13266
14126
|
if (char === "\\") {
|
|
13267
14127
|
return {
|
|
13268
|
-
append: char + (
|
|
14128
|
+
append: char + (text2[index + 1] ?? ""),
|
|
13269
14129
|
inString: true,
|
|
13270
14130
|
next: index + 2
|
|
13271
14131
|
};
|
|
@@ -13275,35 +14135,35 @@ var scanJsonChar = (text, index, inString) => {
|
|
|
13275
14135
|
if (char === '"') {
|
|
13276
14136
|
return { append: char, inString: true, next: index + 1 };
|
|
13277
14137
|
}
|
|
13278
|
-
if (char === "/" &&
|
|
13279
|
-
const newline =
|
|
14138
|
+
if (char === "/" && text2[index + 1] === "/") {
|
|
14139
|
+
const newline = text2.indexOf(`
|
|
13280
14140
|
`, index + 2);
|
|
13281
14141
|
return {
|
|
13282
14142
|
append: "",
|
|
13283
14143
|
inString: false,
|
|
13284
|
-
next: newline === -1 ?
|
|
14144
|
+
next: newline === -1 ? text2.length : newline
|
|
13285
14145
|
};
|
|
13286
14146
|
}
|
|
13287
|
-
if (char === "/" &&
|
|
13288
|
-
const end =
|
|
14147
|
+
if (char === "/" && text2[index + 1] === "*") {
|
|
14148
|
+
const end = text2.indexOf("*/", index + 2);
|
|
13289
14149
|
return {
|
|
13290
14150
|
append: "",
|
|
13291
14151
|
inString: false,
|
|
13292
|
-
next: end === -1 ?
|
|
14152
|
+
next: end === -1 ? text2.length : end + 2
|
|
13293
14153
|
};
|
|
13294
14154
|
}
|
|
13295
14155
|
return { append: char ?? "", inString: false, next: index + 1 };
|
|
13296
14156
|
};
|
|
13297
|
-
var stripJsonComments = (
|
|
14157
|
+
var stripJsonComments = (text2) => {
|
|
13298
14158
|
let out = "";
|
|
13299
14159
|
let inString = false;
|
|
13300
14160
|
let index = 0;
|
|
13301
|
-
while (index <
|
|
14161
|
+
while (index < text2.length) {
|
|
13302
14162
|
const {
|
|
13303
14163
|
append,
|
|
13304
14164
|
inString: nextInString,
|
|
13305
14165
|
next
|
|
13306
|
-
} = scanJsonChar(
|
|
14166
|
+
} = scanJsonChar(text2, index, inString);
|
|
13307
14167
|
out += append;
|
|
13308
14168
|
inString = nextInString;
|
|
13309
14169
|
index = next;
|
|
@@ -13311,9 +14171,9 @@ var stripJsonComments = (text) => {
|
|
|
13311
14171
|
return out;
|
|
13312
14172
|
};
|
|
13313
14173
|
var TRAILING_COMMA = /,(?<rest>\s*[}\]])/gu;
|
|
13314
|
-
var parseJsonc = (
|
|
14174
|
+
var parseJsonc = (text2) => {
|
|
13315
14175
|
try {
|
|
13316
|
-
const cleaned = stripJsonComments(
|
|
14176
|
+
const cleaned = stripJsonComments(text2).replaceAll(TRAILING_COMMA, "$<rest>");
|
|
13317
14177
|
const value = JSON.parse(cleaned);
|
|
13318
14178
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
13319
14179
|
} catch {
|
|
@@ -13329,15 +14189,15 @@ var isFile = (path) => {
|
|
|
13329
14189
|
};
|
|
13330
14190
|
var resolveExtends = (spec, fromDir) => {
|
|
13331
14191
|
if (spec.startsWith(".") || isAbsolute7(spec)) {
|
|
13332
|
-
const candidates = spec.endsWith(".json") ? [
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
14192
|
+
const candidates = spec.endsWith(".json") ? [resolve7(fromDir, spec)] : [
|
|
14193
|
+
resolve7(fromDir, `${spec}.json`),
|
|
14194
|
+
resolve7(fromDir, spec, "tsconfig.json"),
|
|
14195
|
+
resolve7(fromDir, spec)
|
|
13336
14196
|
];
|
|
13337
14197
|
return candidates.find(isFile) ?? null;
|
|
13338
14198
|
}
|
|
13339
14199
|
try {
|
|
13340
|
-
const requireFromDir = createRequire3(pathToFileURL2(
|
|
14200
|
+
const requireFromDir = createRequire3(pathToFileURL2(join22(fromDir, "_.js")).href);
|
|
13341
14201
|
for (const sub of [`${spec}/tsconfig.json`, spec]) {
|
|
13342
14202
|
try {
|
|
13343
14203
|
return requireFromDir.resolve(sub);
|
|
@@ -13347,7 +14207,7 @@ var resolveExtends = (spec, fromDir) => {
|
|
|
13347
14207
|
return null;
|
|
13348
14208
|
};
|
|
13349
14209
|
var loadPaths = (file, seen) => {
|
|
13350
|
-
if (seen.has(file) || !
|
|
14210
|
+
if (seen.has(file) || !existsSync13(file)) {
|
|
13351
14211
|
return null;
|
|
13352
14212
|
}
|
|
13353
14213
|
seen.add(file);
|
|
@@ -13359,7 +14219,7 @@ var loadPaths = (file, seen) => {
|
|
|
13359
14219
|
if (options.paths && typeof options.paths === "object") {
|
|
13360
14220
|
const baseUrl = typeof options.baseUrl === "string" ? options.baseUrl : ".";
|
|
13361
14221
|
return {
|
|
13362
|
-
baseDir:
|
|
14222
|
+
baseDir: resolve7(dirname9(file), baseUrl),
|
|
13363
14223
|
paths: options.paths
|
|
13364
14224
|
};
|
|
13365
14225
|
}
|
|
@@ -13368,7 +14228,7 @@ var loadPaths = (file, seen) => {
|
|
|
13368
14228
|
if (typeof base !== "string") {
|
|
13369
14229
|
continue;
|
|
13370
14230
|
}
|
|
13371
|
-
const resolved = resolveExtends(base,
|
|
14231
|
+
const resolved = resolveExtends(base, dirname9(file));
|
|
13372
14232
|
const found = resolved ? loadPaths(resolved, seen) : null;
|
|
13373
14233
|
if (found) {
|
|
13374
14234
|
return found;
|
|
@@ -13386,10 +14246,10 @@ var toAlias = (key, value, baseDir) => {
|
|
|
13386
14246
|
if (find === "" || find === "*") {
|
|
13387
14247
|
return null;
|
|
13388
14248
|
}
|
|
13389
|
-
return { find, replacement:
|
|
14249
|
+
return { find, replacement: resolve7(baseDir, target) };
|
|
13390
14250
|
};
|
|
13391
14251
|
var resolveTsconfigAliases = (root) => {
|
|
13392
|
-
const entry = ["tsconfig.json", "jsconfig.json"].map((name) =>
|
|
14252
|
+
const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join22(root, name)).find((file) => existsSync13(file));
|
|
13393
14253
|
if (!entry) {
|
|
13394
14254
|
return {};
|
|
13395
14255
|
}
|
|
@@ -13407,24 +14267,126 @@ var resolveTsconfigAliases = (root) => {
|
|
|
13407
14267
|
return aliases;
|
|
13408
14268
|
};
|
|
13409
14269
|
|
|
14270
|
+
// src/og/derive.ts
|
|
14271
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
14272
|
+
import { isAbsolute as isAbsolute8, join as join23 } from "pathe";
|
|
14273
|
+
var CARD_WEIGHTS = [400, 600];
|
|
14274
|
+
var absoluteSrc = (root, src) => isAbsolute8(src) ? src : join23(root, src);
|
|
14275
|
+
var googleWeights = (weights) => {
|
|
14276
|
+
const numbers = weights.filter((weight) => typeof weight === "number");
|
|
14277
|
+
const used = numbers.filter((weight) => CARD_WEIGHTS.includes(weight));
|
|
14278
|
+
if (used.length > 0) {
|
|
14279
|
+
return used;
|
|
14280
|
+
}
|
|
14281
|
+
if (numbers.length > 0) {
|
|
14282
|
+
return numbers;
|
|
14283
|
+
}
|
|
14284
|
+
const [first] = weights;
|
|
14285
|
+
return weights.length === 1 && typeof first === "string" ? first : undefined;
|
|
14286
|
+
};
|
|
14287
|
+
var googleOgFont = (name, weights) => {
|
|
14288
|
+
const weight = googleWeights(weights);
|
|
14289
|
+
return weight === undefined ? { name } : { name, weight };
|
|
14290
|
+
};
|
|
14291
|
+
var localOgFonts = (font, root) => font.variants.map((variant) => ({
|
|
14292
|
+
name: font.name,
|
|
14293
|
+
src: absoluteSrc(root, variant.src),
|
|
14294
|
+
...typeof variant.weight === "number" ? { weight: variant.weight } : {},
|
|
14295
|
+
...variant.style === "normal" || variant.style === "italic" ? { style: variant.style } : {}
|
|
14296
|
+
}));
|
|
14297
|
+
var roleFonts = (value, root) => {
|
|
14298
|
+
if (typeof value === "string") {
|
|
14299
|
+
if (!isFontSlug(value)) {
|
|
14300
|
+
return null;
|
|
14301
|
+
}
|
|
14302
|
+
const def = GOOGLE_FONTS[value];
|
|
14303
|
+
return [googleOgFont(def.family, def.weights)];
|
|
14304
|
+
}
|
|
14305
|
+
if ("variants" in value) {
|
|
14306
|
+
return localOgFonts(value, root);
|
|
14307
|
+
}
|
|
14308
|
+
const remote = value;
|
|
14309
|
+
if ((remote.provider ?? "google") !== "google") {
|
|
14310
|
+
return null;
|
|
14311
|
+
}
|
|
14312
|
+
return [googleOgFont(remote.name, remote.weights ?? CARD_WEIGHTS)];
|
|
14313
|
+
};
|
|
14314
|
+
var roleFamily = (value) => {
|
|
14315
|
+
if (typeof value === "string") {
|
|
14316
|
+
return isFontSlug(value) ? GOOGLE_FONTS[value].family : null;
|
|
14317
|
+
}
|
|
14318
|
+
return value.name;
|
|
14319
|
+
};
|
|
14320
|
+
var deriveOgFonts = (fonts, root) => {
|
|
14321
|
+
const derived = [];
|
|
14322
|
+
const seen = new Set;
|
|
14323
|
+
const families = {};
|
|
14324
|
+
const roles = [
|
|
14325
|
+
["title", fonts?.display],
|
|
14326
|
+
["body", fonts?.body]
|
|
14327
|
+
];
|
|
14328
|
+
for (const [role, value] of roles) {
|
|
14329
|
+
if (value === undefined) {
|
|
14330
|
+
continue;
|
|
14331
|
+
}
|
|
14332
|
+
const roleEntries = roleFonts(value, root);
|
|
14333
|
+
if (!roleEntries) {
|
|
14334
|
+
continue;
|
|
14335
|
+
}
|
|
14336
|
+
families[role] = roleFamily(value) ?? undefined;
|
|
14337
|
+
for (const entry of roleEntries) {
|
|
14338
|
+
const key = JSON.stringify(entry);
|
|
14339
|
+
if (!seen.has(key)) {
|
|
14340
|
+
seen.add(key);
|
|
14341
|
+
derived.push(entry);
|
|
14342
|
+
}
|
|
14343
|
+
}
|
|
14344
|
+
}
|
|
14345
|
+
return {
|
|
14346
|
+
fonts: derived,
|
|
14347
|
+
...families.title || families.body ? { families } : {}
|
|
14348
|
+
};
|
|
14349
|
+
};
|
|
14350
|
+
var resolveOgFontSources = (fonts, root) => fonts.map((font) => typeof font !== "string" && ("src" in font) ? { ...font, src: absoluteSrc(root, font.src) } : font);
|
|
14351
|
+
var resolveOgFonts = (options, root) => {
|
|
14352
|
+
if (options.ogFonts) {
|
|
14353
|
+
return { fonts: resolveOgFontSources(options.ogFonts, root) };
|
|
14354
|
+
}
|
|
14355
|
+
return options.themeFontsConfigured ? deriveOgFonts(options.themeFonts, root) : { fonts: [] };
|
|
14356
|
+
};
|
|
14357
|
+
var missingFontFiles = (options, root) => {
|
|
14358
|
+
const sources = [];
|
|
14359
|
+
for (const value of Object.values(options.themeFonts ?? {})) {
|
|
14360
|
+
if (typeof value !== "string" && "variants" in value) {
|
|
14361
|
+
sources.push(...value.variants.map((variant) => absoluteSrc(root, variant.src)));
|
|
14362
|
+
}
|
|
14363
|
+
}
|
|
14364
|
+
for (const font of options.ogFonts) {
|
|
14365
|
+
if (typeof font !== "string" && "src" in font) {
|
|
14366
|
+
sources.push(absoluteSrc(root, font.src));
|
|
14367
|
+
}
|
|
14368
|
+
}
|
|
14369
|
+
return [...new Set(sources)].filter((path) => !existsSync14(path));
|
|
14370
|
+
};
|
|
14371
|
+
|
|
13410
14372
|
// src/og/logo.ts
|
|
13411
|
-
import { existsSync as
|
|
13412
|
-
import { join as
|
|
14373
|
+
import { existsSync as existsSync15, readFileSync as readFileSync7 } from "node:fs";
|
|
14374
|
+
import { join as join24 } from "pathe";
|
|
13413
14375
|
var resolveOgLogo = (project, source) => {
|
|
13414
14376
|
if (!source?.toLowerCase().endsWith(".svg")) {
|
|
13415
14377
|
return;
|
|
13416
14378
|
}
|
|
13417
|
-
const
|
|
14379
|
+
const relative13 = source.replace(/^\//u, "");
|
|
13418
14380
|
const file = [
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
].find((path) =>
|
|
14381
|
+
join24(project.context.root, "public", relative13),
|
|
14382
|
+
join24(project.context.root, relative13)
|
|
14383
|
+
].find((path) => existsSync15(path));
|
|
13422
14384
|
return file ? readFileSync7(file, "utf-8") : undefined;
|
|
13423
14385
|
};
|
|
13424
14386
|
|
|
13425
14387
|
// src/openapi/scalar.ts
|
|
13426
|
-
import { readFile as
|
|
13427
|
-
import { isAbsolute as
|
|
14388
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
14389
|
+
import { isAbsolute as isAbsolute9, join as join25 } from "pathe";
|
|
13428
14390
|
|
|
13429
14391
|
// src/theme/palette.ts
|
|
13430
14392
|
var FALLBACK_ACCENT = "oklch(0.62 0.16 250)";
|
|
@@ -13530,9 +14492,9 @@ var specConfiguration = async (spec, root) => {
|
|
|
13530
14492
|
if (URL_SPEC2.test(spec)) {
|
|
13531
14493
|
return { config: { url: spec } };
|
|
13532
14494
|
}
|
|
13533
|
-
const absolute =
|
|
14495
|
+
const absolute = isAbsolute9(spec) ? spec : join25(root, spec);
|
|
13534
14496
|
try {
|
|
13535
|
-
return { config: { content: await
|
|
14497
|
+
return { config: { content: await readFile13(absolute, "utf-8") } };
|
|
13536
14498
|
} catch {
|
|
13537
14499
|
return {
|
|
13538
14500
|
config: { url: spec },
|
|
@@ -14019,19 +14981,29 @@ blume-diff {
|
|
|
14019
14981
|
even though its chrome wrapper is not-prose. */
|
|
14020
14982
|
.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
14021
14983
|
display: block;
|
|
14022
|
-
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14984
|
+
/* Tall blocks scroll vertically in place instead of taking the page. Both
|
|
14985
|
+
axes live on the code element for the same reason: the pre stays static
|
|
14986
|
+
so the header bar and copy button never drift with the scroll. */
|
|
14987
|
+
max-height: 24rem;
|
|
14988
|
+
overflow: auto;
|
|
14989
|
+
/* The small bottom inset keeps the horizontal thumb off the last line's
|
|
14990
|
+
descenders now that scrollbars are visible. */
|
|
14991
|
+
padding: 0 1.25rem 0.375rem;
|
|
14992
|
+
/* Thin theme-colored scrollbars, matching the sidebar treatment, so a
|
|
14993
|
+
height-capped block reads as scrollable instead of simply ending.
|
|
14994
|
+
Safari before 18.2 supports neither property and falls back to the
|
|
14995
|
+
platform-default scrollbar — acceptable, since macOS overlays it. */
|
|
14996
|
+
scrollbar-color: var(--blume-border) transparent;
|
|
14997
|
+
scrollbar-width: thin;
|
|
14028
14998
|
}
|
|
14029
14999
|
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
15000
|
+
/* The dark border token is too close to the page background to read as a
|
|
15001
|
+
scrollbar thumb; derive a brighter one from the muted foreground instead. */
|
|
15002
|
+
:root[data-theme="dark"]
|
|
15003
|
+
.prose
|
|
15004
|
+
:where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
15005
|
+
scrollbar-color: color-mix(in oklab, var(--blume-muted-foreground) 55%, transparent)
|
|
15006
|
+
transparent;
|
|
14035
15007
|
}
|
|
14036
15008
|
|
|
14037
15009
|
/* Word wrap (markdown.code.wrap): long lines wrap instead of scrolling. The
|
|
@@ -14099,6 +15071,10 @@ pre.blume-source {
|
|
|
14099
15071
|
|
|
14100
15072
|
pre.blume-source > code {
|
|
14101
15073
|
flex: 1;
|
|
15074
|
+
/* The pane's measured height is authoritative (it can exceed the prose
|
|
15075
|
+
24rem cap), so undo the generic prose max-height: the code must fill
|
|
15076
|
+
whatever height the tab was given. */
|
|
15077
|
+
max-height: none;
|
|
14102
15078
|
min-height: 0;
|
|
14103
15079
|
overflow: auto;
|
|
14104
15080
|
}
|
|
@@ -14280,6 +15256,13 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
14280
15256
|
display: none !important;
|
|
14281
15257
|
}
|
|
14282
15258
|
|
|
15259
|
+
/* Paper can't scroll: uncap the code scroller so long blocks print in
|
|
15260
|
+
full, same as tab panels are force-expanded below. */
|
|
15261
|
+
.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
15262
|
+
max-height: none;
|
|
15263
|
+
overflow: visible;
|
|
15264
|
+
}
|
|
15265
|
+
|
|
14283
15266
|
#blume-content {
|
|
14284
15267
|
padding: 0 !important;
|
|
14285
15268
|
}
|
|
@@ -14523,13 +15506,13 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
|
|
|
14523
15506
|
};
|
|
14524
15507
|
|
|
14525
15508
|
// src/astro/examples.ts
|
|
14526
|
-
import { readFile as
|
|
14527
|
-
import { join as
|
|
15509
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
15510
|
+
import { join as join27, relative as relative13 } from "pathe";
|
|
14528
15511
|
import { glob as glob6 } from "tinyglobby";
|
|
14529
15512
|
|
|
14530
15513
|
// src/astro/islands.ts
|
|
14531
|
-
import { readFile as
|
|
14532
|
-
import { basename as basename2, join as
|
|
15514
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
15515
|
+
import { basename as basename2, join as join26 } from "pathe";
|
|
14533
15516
|
import { glob as glob5 } from "tinyglobby";
|
|
14534
15517
|
var DEFAULT_CLIENT = "visible";
|
|
14535
15518
|
var VALID_MODES = new Set([
|
|
@@ -14558,14 +15541,14 @@ var readClientMode = (source, file, warnings) => {
|
|
|
14558
15541
|
return mode;
|
|
14559
15542
|
};
|
|
14560
15543
|
var discoverIslands = async (root) => {
|
|
14561
|
-
const dir =
|
|
15544
|
+
const dir = join26(root, "islands");
|
|
14562
15545
|
const matches2 = await glob5(["**/*.{jsx,svelte,tsx,vue}"], {
|
|
14563
15546
|
absolute: true,
|
|
14564
15547
|
cwd: dir,
|
|
14565
15548
|
onlyFiles: true
|
|
14566
15549
|
});
|
|
14567
15550
|
const files = matches2.toSorted();
|
|
14568
|
-
const sources = await Promise.all(files.map((file) =>
|
|
15551
|
+
const sources = await Promise.all(files.map((file) => readFile14(file, "utf-8")));
|
|
14569
15552
|
const islands = [];
|
|
14570
15553
|
const warnings = [];
|
|
14571
15554
|
const seen = new Map;
|
|
@@ -14621,14 +15604,14 @@ var splitGlobBase = (pattern) => {
|
|
|
14621
15604
|
};
|
|
14622
15605
|
var discoverExamples = async (root, pattern = "examples") => {
|
|
14623
15606
|
const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
|
|
14624
|
-
const dir =
|
|
15607
|
+
const dir = join27(root, base);
|
|
14625
15608
|
const matches2 = await glob6([rest], {
|
|
14626
15609
|
absolute: true,
|
|
14627
15610
|
cwd: dir,
|
|
14628
15611
|
onlyFiles: true
|
|
14629
15612
|
});
|
|
14630
15613
|
const files = matches2.toSorted();
|
|
14631
|
-
const sources = await Promise.all(files.map((file) =>
|
|
15614
|
+
const sources = await Promise.all(files.map((file) => readFile15(file, "utf-8")));
|
|
14632
15615
|
const examples = [];
|
|
14633
15616
|
const warnings = [];
|
|
14634
15617
|
const seen = new Map;
|
|
@@ -14638,7 +15621,7 @@ var discoverExamples = async (root, pattern = "examples") => {
|
|
|
14638
15621
|
if (!(ext && framework)) {
|
|
14639
15622
|
return;
|
|
14640
15623
|
}
|
|
14641
|
-
const path =
|
|
15624
|
+
const path = relative13(dir, file).slice(0, -(ext.length + 1));
|
|
14642
15625
|
const existing = seen.get(path);
|
|
14643
15626
|
if (existing) {
|
|
14644
15627
|
warnings.push(`Two examples both resolve to "${path}" ("${existing}" and "${file}"); ignoring the second. Give them distinct paths.`);
|
|
@@ -14665,10 +15648,10 @@ var exampleMarkdownLookup = (examples) => Object.fromEntries(examples.map((examp
|
|
|
14665
15648
|
]));
|
|
14666
15649
|
|
|
14667
15650
|
// src/astro/generate.ts
|
|
14668
|
-
var BLUME_SRC =
|
|
15651
|
+
var BLUME_SRC = join28(packageRoot(), "src");
|
|
14669
15652
|
var canResolveFrom = (fromDir, spec) => {
|
|
14670
15653
|
try {
|
|
14671
|
-
createRequire5(pathToFileURL3(
|
|
15654
|
+
createRequire5(pathToFileURL3(join28(fromDir, "_.js")).href).resolve(spec);
|
|
14672
15655
|
return true;
|
|
14673
15656
|
} catch {
|
|
14674
15657
|
return false;
|
|
@@ -14679,7 +15662,7 @@ var resolveReactCompiler = (config, needsReact, pkgDir = packageRoot()) => {
|
|
|
14679
15662
|
return null;
|
|
14680
15663
|
}
|
|
14681
15664
|
try {
|
|
14682
|
-
return createRequire5(pathToFileURL3(
|
|
15665
|
+
return createRequire5(pathToFileURL3(join28(pkgDir, "_.js")).href).resolve("babel-plugin-react-compiler");
|
|
14683
15666
|
} catch {
|
|
14684
15667
|
return null;
|
|
14685
15668
|
}
|
|
@@ -14689,7 +15672,7 @@ var reactCompilerWarnings = (config, needsReact, compilerPath) => needsReact &&
|
|
|
14689
15672
|
] : [];
|
|
14690
15673
|
var resolveAstroPackageJson = (modulesDir) => {
|
|
14691
15674
|
try {
|
|
14692
|
-
return realpathSync(
|
|
15675
|
+
return realpathSync(join28(modulesDir, "astro", "package.json"));
|
|
14693
15676
|
} catch {
|
|
14694
15677
|
return null;
|
|
14695
15678
|
}
|
|
@@ -14697,12 +15680,12 @@ var resolveAstroPackageJson = (modulesDir) => {
|
|
|
14697
15680
|
var resolvedAstroHit = (fromDir) => {
|
|
14698
15681
|
let dir = normalize3(fromDir);
|
|
14699
15682
|
while (true) {
|
|
14700
|
-
const modulesDir =
|
|
15683
|
+
const modulesDir = join28(dir, "node_modules");
|
|
14701
15684
|
const pkg = resolveAstroPackageJson(modulesDir);
|
|
14702
15685
|
if (pkg) {
|
|
14703
15686
|
return { modulesDir, pkg };
|
|
14704
15687
|
}
|
|
14705
|
-
const parent =
|
|
15688
|
+
const parent = dirname10(dir);
|
|
14706
15689
|
if (parent === dir) {
|
|
14707
15690
|
return null;
|
|
14708
15691
|
}
|
|
@@ -14717,10 +15700,10 @@ var sameRealDir = (a, b) => {
|
|
|
14717
15700
|
}
|
|
14718
15701
|
};
|
|
14719
15702
|
var depsCandidates = (pkgDir) => [
|
|
14720
|
-
|
|
14721
|
-
|
|
15703
|
+
join28(pkgDir, "node_modules"),
|
|
15704
|
+
dirname10(pkgDir)
|
|
14722
15705
|
];
|
|
14723
|
-
var candidateHolding = (pkgDir, ...segments) => depsCandidates(pkgDir).find((dir) =>
|
|
15706
|
+
var candidateHolding = (pkgDir, ...segments) => depsCandidates(pkgDir).find((dir) => existsSync16(join28(dir, ...segments))) ?? null;
|
|
14724
15707
|
var linkDepsJunction = async (link, depsDir) => {
|
|
14725
15708
|
let existing;
|
|
14726
15709
|
try {
|
|
@@ -14733,13 +15716,13 @@ var linkDepsJunction = async (link, depsDir) => {
|
|
|
14733
15716
|
return;
|
|
14734
15717
|
}
|
|
14735
15718
|
try {
|
|
14736
|
-
if (
|
|
15719
|
+
if (resolve8(dirname10(link), await readlink(link)) === resolve8(depsDir)) {
|
|
14737
15720
|
return;
|
|
14738
15721
|
}
|
|
14739
15722
|
} catch {}
|
|
14740
15723
|
await rm2(link, { force: true });
|
|
14741
15724
|
}
|
|
14742
|
-
await mkdir6(
|
|
15725
|
+
await mkdir6(dirname10(link), { recursive: true });
|
|
14743
15726
|
await symlink(depsDir, link, "junction");
|
|
14744
15727
|
};
|
|
14745
15728
|
var readPkgVersion = (pkgJsonPath) => {
|
|
@@ -14772,7 +15755,7 @@ var dropStaleDepsLink = async (link, pkgDir) => {
|
|
|
14772
15755
|
let linkedBlume;
|
|
14773
15756
|
let runningBlume;
|
|
14774
15757
|
try {
|
|
14775
|
-
linkedBlume = await realpath(
|
|
15758
|
+
linkedBlume = await realpath(join28(link, "blume"));
|
|
14776
15759
|
runningBlume = await realpath(pkgDir);
|
|
14777
15760
|
} catch {
|
|
14778
15761
|
return;
|
|
@@ -14788,7 +15771,7 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
|
|
|
14788
15771
|
}
|
|
14789
15772
|
const mdxDir = candidateHolding(pkgDir, "@astrojs", "mdx");
|
|
14790
15773
|
const blumeAstro = resolveAstroPackageJson(astroDir);
|
|
14791
|
-
await dropStaleDepsLink(
|
|
15774
|
+
await dropStaleDepsLink(join28(outDir, "node_modules"), pkgDir);
|
|
14792
15775
|
const outDirHit = resolvedAstroHit(outDir);
|
|
14793
15776
|
const astroCorrect = blumeAstro !== null && outDirHit?.pkg === blumeAstro;
|
|
14794
15777
|
const walkLandsInDeps = astroCorrect && outDirHit !== null && sameRealDir(outDirHit.modulesDir, astroDir);
|
|
@@ -14796,7 +15779,7 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
|
|
|
14796
15779
|
return null;
|
|
14797
15780
|
}
|
|
14798
15781
|
if (mdxDir && (mdxDir === astroDir || astroCorrect)) {
|
|
14799
|
-
await linkDepsJunction(
|
|
15782
|
+
await linkDepsJunction(join28(outDir, "node_modules"), mdxDir);
|
|
14800
15783
|
return null;
|
|
14801
15784
|
}
|
|
14802
15785
|
return astroConflictWarning(blumeAstro, outDirHit?.pkg ?? null);
|
|
@@ -14837,14 +15820,14 @@ var searchProviderWarnings = (provider, root, pkgDir = packageRoot()) => {
|
|
|
14837
15820
|
}
|
|
14838
15821
|
return warnings;
|
|
14839
15822
|
};
|
|
14840
|
-
var examplesCssFile = (root, config) => config.examples.css ?
|
|
15823
|
+
var examplesCssFile = (root, config) => config.examples.css ? join28(root, config.examples.css) : null;
|
|
14841
15824
|
var writeExamplesPreview = async (options) => {
|
|
14842
15825
|
const { config, hasExamples, root, srcDir, write } = options;
|
|
14843
15826
|
if (hasExamples) {
|
|
14844
|
-
await write(
|
|
15827
|
+
await write(join28(srcDir, "pages", ...config.basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro"), examplesPageTemplate());
|
|
14845
15828
|
}
|
|
14846
15829
|
const cssFile = examplesCssFile(root, config);
|
|
14847
|
-
return cssFile && !
|
|
15830
|
+
return cssFile && !existsSync16(cssFile) ? [
|
|
14848
15831
|
`examples.css points at "${config.examples.css}", which doesn't exist; previews render without it.`
|
|
14849
15832
|
] : [];
|
|
14850
15833
|
};
|
|
@@ -14853,7 +15836,7 @@ var readOptional = async (path) => {
|
|
|
14853
15836
|
return "";
|
|
14854
15837
|
}
|
|
14855
15838
|
try {
|
|
14856
|
-
return await
|
|
15839
|
+
return await readFile16(path, "utf-8");
|
|
14857
15840
|
} catch {
|
|
14858
15841
|
return "";
|
|
14859
15842
|
}
|
|
@@ -14873,30 +15856,30 @@ var detectUsesMath = async (root, staged = []) => {
|
|
|
14873
15856
|
ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
|
|
14874
15857
|
onlyFiles: true
|
|
14875
15858
|
});
|
|
14876
|
-
const contents = await Promise.all(files.map((file) => readOptional(
|
|
15859
|
+
const contents = await Promise.all(files.map((file) => readOptional(join28(root, file))));
|
|
14877
15860
|
return [...contents, ...staged].some(containsMath);
|
|
14878
15861
|
};
|
|
14879
|
-
var hashConfigSource = (source) =>
|
|
15862
|
+
var hashConfigSource = (source) => createHash3("sha256").update(source).digest("hex");
|
|
14880
15863
|
var loadIntegrationBridge = async (config, context) => {
|
|
14881
15864
|
if (config.integrations.length === 0 || !context.configFile) {
|
|
14882
15865
|
return;
|
|
14883
15866
|
}
|
|
14884
15867
|
return {
|
|
14885
|
-
configFile:
|
|
15868
|
+
configFile: relative14(context.outDir, context.configFile),
|
|
14886
15869
|
sourceHash: hashConfigSource(await readOptional(context.configFile))
|
|
14887
15870
|
};
|
|
14888
15871
|
};
|
|
14889
15872
|
var writeIfChanged = async (path, content) => {
|
|
14890
15873
|
let existing = null;
|
|
14891
15874
|
try {
|
|
14892
|
-
existing = await
|
|
15875
|
+
existing = await readFile16(path, "utf-8");
|
|
14893
15876
|
} catch {
|
|
14894
15877
|
existing = null;
|
|
14895
15878
|
}
|
|
14896
15879
|
if (existing === content) {
|
|
14897
15880
|
return false;
|
|
14898
15881
|
}
|
|
14899
|
-
await mkdir6(
|
|
15882
|
+
await mkdir6(dirname10(path), { recursive: true });
|
|
14900
15883
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
14901
15884
|
await writeFile7(tmp, content, "utf-8");
|
|
14902
15885
|
try {
|
|
@@ -14934,12 +15917,12 @@ var collectStaged = (project) => {
|
|
|
14934
15917
|
var writeStagedContent = async (out, staged) => {
|
|
14935
15918
|
const contentDir = stagedContentDir(out);
|
|
14936
15919
|
const written = new Set;
|
|
14937
|
-
await Promise.all([...staged].map(async ([entryId,
|
|
14938
|
-
const path =
|
|
15920
|
+
await Promise.all([...staged].map(async ([entryId, text2]) => {
|
|
15921
|
+
const path = join28(contentDir, entryId);
|
|
14939
15922
|
written.add(normalize3(path));
|
|
14940
|
-
await writeIfChanged(path,
|
|
15923
|
+
await writeIfChanged(path, text2);
|
|
14941
15924
|
}));
|
|
14942
|
-
if (
|
|
15925
|
+
if (existsSync16(contentDir)) {
|
|
14943
15926
|
await pruneOrphans(contentDir, written);
|
|
14944
15927
|
}
|
|
14945
15928
|
};
|
|
@@ -14970,9 +15953,9 @@ var readLogoSvg = (project, source) => {
|
|
|
14970
15953
|
}
|
|
14971
15954
|
const rel = source.replace(/^\//u, "");
|
|
14972
15955
|
const file = [
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
].find((path) =>
|
|
15956
|
+
join28(project.context.root, "public", rel),
|
|
15957
|
+
join28(project.context.root, rel)
|
|
15958
|
+
].find((path) => existsSync16(path));
|
|
14976
15959
|
return file ? readFileSync9(file, "utf-8") : undefined;
|
|
14977
15960
|
};
|
|
14978
15961
|
var resolveLogo = (project) => {
|
|
@@ -14981,7 +15964,7 @@ var resolveLogo = (project) => {
|
|
|
14981
15964
|
return null;
|
|
14982
15965
|
}
|
|
14983
15966
|
const config = typeof logo === "string" ? { image: logo } : logo;
|
|
14984
|
-
const { href, image: source, text } = config;
|
|
15967
|
+
const { href, image: source, text: text2 } = config;
|
|
14985
15968
|
const image = typeof source === "string" ? { light: source } : source;
|
|
14986
15969
|
const light = image?.light ?? image?.dark;
|
|
14987
15970
|
const dark = image?.dark ?? image?.light;
|
|
@@ -14990,12 +15973,12 @@ var resolveLogo = (project) => {
|
|
|
14990
15973
|
const lightSvg = readLogoSvg(project, light);
|
|
14991
15974
|
const darkSvg = dark === light ? lightSvg : readLogoSvg(project, dark);
|
|
14992
15975
|
if (light && light === dark && lightSvg) {
|
|
14993
|
-
return { alt, href: brandHref, svg: lightSvg, text };
|
|
15976
|
+
return { alt, href: brandHref, svg: lightSvg, text: text2 };
|
|
14994
15977
|
}
|
|
14995
15978
|
const lightDimensions = svgDimensions(lightSvg);
|
|
14996
15979
|
const darkDimensions = svgDimensions(darkSvg);
|
|
14997
15980
|
const dimensions = lightDimensions || darkDimensions ? { dark: darkDimensions, light: lightDimensions } : undefined;
|
|
14998
|
-
return { alt, dark, dimensions, href: brandHref, light, text };
|
|
15981
|
+
return { alt, dark, dimensions, href: brandHref, light, text: text2 };
|
|
14999
15982
|
};
|
|
15000
15983
|
var FAVICON_CANDIDATES = [
|
|
15001
15984
|
"icon.svg",
|
|
@@ -15018,7 +16001,7 @@ var faviconType = (name) => {
|
|
|
15018
16001
|
};
|
|
15019
16002
|
var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync9(file).toString("base64")}`;
|
|
15020
16003
|
var defaultFavicon = () => ({
|
|
15021
|
-
href: inlineDataUri(
|
|
16004
|
+
href: inlineDataUri(join28(BLUME_SRC, "assets", "icon.png"), "image/png"),
|
|
15022
16005
|
type: "image/png"
|
|
15023
16006
|
});
|
|
15024
16007
|
var APPLE_ICON_CANDIDATES = [
|
|
@@ -15030,13 +16013,13 @@ var APPLE_ICON_CANDIDATES = [
|
|
|
15030
16013
|
var resolveIconFile = (project, candidates) => {
|
|
15031
16014
|
const { root } = project.context;
|
|
15032
16015
|
for (const name of candidates) {
|
|
15033
|
-
if (
|
|
16016
|
+
if (existsSync16(join28(root, "public", name))) {
|
|
15034
16017
|
return { href: `/${name}`, type: faviconType(name) };
|
|
15035
16018
|
}
|
|
15036
16019
|
}
|
|
15037
16020
|
for (const name of candidates) {
|
|
15038
|
-
const file =
|
|
15039
|
-
if (
|
|
16021
|
+
const file = join28(root, name);
|
|
16022
|
+
if (existsSync16(file)) {
|
|
15040
16023
|
const type = faviconType(name);
|
|
15041
16024
|
return { href: inlineDataUri(file, type ?? "image/x-icon"), type };
|
|
15042
16025
|
}
|
|
@@ -15060,18 +16043,45 @@ var resolveBanner = (config) => {
|
|
|
15060
16043
|
link: banner.link
|
|
15061
16044
|
};
|
|
15062
16045
|
};
|
|
16046
|
+
var resolveOgMark = (project, siteLogo) => {
|
|
16047
|
+
const configured = project.config.seo.og.logo;
|
|
16048
|
+
if (configured === false) {
|
|
16049
|
+
return false;
|
|
16050
|
+
}
|
|
16051
|
+
if (configured) {
|
|
16052
|
+
return resolveOgLogo(project, configured);
|
|
16053
|
+
}
|
|
16054
|
+
return siteLogo;
|
|
16055
|
+
};
|
|
16056
|
+
var resolveOgSite = (config) => {
|
|
16057
|
+
const configured = config.seo.og.site;
|
|
16058
|
+
if (configured === false) {
|
|
16059
|
+
return;
|
|
16060
|
+
}
|
|
16061
|
+
if (configured !== undefined) {
|
|
16062
|
+
return configured;
|
|
16063
|
+
}
|
|
16064
|
+
return config.deployment.site ? `${new URL(config.deployment.site).host}${normalizeBasePath(config.deployment.base)}` : undefined;
|
|
16065
|
+
};
|
|
16066
|
+
var resolveOgDescription = (config) => {
|
|
16067
|
+
const configured = config.seo.og.description;
|
|
16068
|
+
if (configured === false) {
|
|
16069
|
+
return;
|
|
16070
|
+
}
|
|
16071
|
+
return configured ?? config.description;
|
|
16072
|
+
};
|
|
15063
16073
|
var buildRuntimeData = (project) => {
|
|
15064
16074
|
const { config, context, graph, manifest } = project;
|
|
15065
16075
|
const { github } = config;
|
|
15066
16076
|
const repoUrl = github ? `https://github.com/${github.owner}/${github.repo}` : null;
|
|
15067
16077
|
const editBase = github ? `${repoUrl}/edit/${github.branch}` : null;
|
|
15068
16078
|
const logo = resolveLogo(project);
|
|
15069
|
-
const ogLogo =
|
|
16079
|
+
const ogLogo = resolveOgMark(project, logo?.svg);
|
|
15070
16080
|
const editUrlFor = (sourcePath) => {
|
|
15071
16081
|
if (!(editBase && sourcePath)) {
|
|
15072
16082
|
return null;
|
|
15073
16083
|
}
|
|
15074
|
-
const rel =
|
|
16084
|
+
const rel = relative14(context.root, sourcePath).split("\\").join("/");
|
|
15075
16085
|
const editPath = github?.dir ? `${github.dir}/${rel}` : rel;
|
|
15076
16086
|
return `${editBase}/${editPath}`;
|
|
15077
16087
|
};
|
|
@@ -15133,10 +16143,11 @@ var buildRuntimeData = (project) => {
|
|
|
15133
16143
|
route: config.ai.mcp.route
|
|
15134
16144
|
} : null,
|
|
15135
16145
|
og: {
|
|
16146
|
+
description: resolveOgDescription(config),
|
|
15136
16147
|
enabled: config.seo.og.enabled ?? false,
|
|
15137
|
-
fonts: config.seo.og.fonts ?? [],
|
|
15138
16148
|
logo: ogLogo,
|
|
15139
|
-
palette: config.seo.og.palette
|
|
16149
|
+
palette: config.seo.og.palette,
|
|
16150
|
+
site: resolveOgSite(config)
|
|
15140
16151
|
},
|
|
15141
16152
|
repoUrl,
|
|
15142
16153
|
search: {
|
|
@@ -15149,6 +16160,10 @@ var buildRuntimeData = (project) => {
|
|
|
15149
16160
|
theme: config.theme,
|
|
15150
16161
|
title: config.title,
|
|
15151
16162
|
toc: config.toc,
|
|
16163
|
+
webmcp: {
|
|
16164
|
+
enabled: config.ai.webmcp,
|
|
16165
|
+
llms: config.ai.llmsTxt.enabled
|
|
16166
|
+
},
|
|
15152
16167
|
x: config.seo.x
|
|
15153
16168
|
},
|
|
15154
16169
|
feeds: buildRssFeeds(project).map((feed) => ({
|
|
@@ -15182,7 +16197,7 @@ var buildRuntimeData = (project) => {
|
|
|
15182
16197
|
var planMcp = (project, srcDir, userPages) => {
|
|
15183
16198
|
const { config } = project;
|
|
15184
16199
|
const { route } = config.ai.mcp;
|
|
15185
|
-
const dir =
|
|
16200
|
+
const dir = join28(srcDir, "blume-mcp");
|
|
15186
16201
|
const base = {
|
|
15187
16202
|
dir,
|
|
15188
16203
|
discoveryPages: [],
|
|
@@ -15206,11 +16221,11 @@ var planMcp = (project, srcDir, userPages) => {
|
|
|
15206
16221
|
...base,
|
|
15207
16222
|
discoveryPages: [
|
|
15208
16223
|
{
|
|
15209
|
-
entrypoint:
|
|
16224
|
+
entrypoint: join28(dir, "discovery.ts"),
|
|
15210
16225
|
pattern: "/.well-known/mcp.json"
|
|
15211
16226
|
},
|
|
15212
16227
|
{
|
|
15213
|
-
entrypoint:
|
|
16228
|
+
entrypoint: join28(dir, "server-card.ts"),
|
|
15214
16229
|
pattern: "/.well-known/mcp/server-card.json"
|
|
15215
16230
|
}
|
|
15216
16231
|
],
|
|
@@ -15230,11 +16245,11 @@ var writeMcpFiles = async (project, plan, write) => {
|
|
|
15230
16245
|
version: data.version
|
|
15231
16246
|
};
|
|
15232
16247
|
await Promise.all([
|
|
15233
|
-
write(
|
|
16248
|
+
write(join28(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
|
|
15234
16249
|
`),
|
|
15235
|
-
write(
|
|
15236
|
-
write(
|
|
15237
|
-
write(
|
|
16250
|
+
write(join28(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
|
|
16251
|
+
write(join28(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
|
|
16252
|
+
write(join28(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
|
|
15238
16253
|
]);
|
|
15239
16254
|
};
|
|
15240
16255
|
var writeAskFiles = async (project, srcDir, write) => {
|
|
@@ -15244,20 +16259,20 @@ var writeAskFiles = async (project, srcDir, write) => {
|
|
|
15244
16259
|
}
|
|
15245
16260
|
const grounded = ask.provider !== "inkeep";
|
|
15246
16261
|
if (grounded) {
|
|
15247
|
-
await write(
|
|
16262
|
+
await write(join28(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
|
|
15248
16263
|
`);
|
|
15249
16264
|
}
|
|
15250
|
-
await write(
|
|
16265
|
+
await write(join28(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
|
|
15251
16266
|
};
|
|
15252
16267
|
var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
15253
16268
|
if (routeIsTaken(pages, contentPages, "/404")) {
|
|
15254
16269
|
return;
|
|
15255
16270
|
}
|
|
15256
|
-
await write(
|
|
16271
|
+
await write(join28(srcDir, "pages", "404.astro"), notFoundPageTemplate());
|
|
15257
16272
|
};
|
|
15258
16273
|
var diagnosticWarning = (diagnostic) => diagnostic.suggestion ? `${diagnostic.message} ${diagnostic.suggestion}` : diagnostic.message;
|
|
15259
16274
|
var buildComponentSlots = async (componentsFile) => {
|
|
15260
|
-
const analysis = componentsFile ? analyzeComponentOverrides(await
|
|
16275
|
+
const analysis = componentsFile ? analyzeComponentOverrides(await readFile16(componentsFile, "utf-8"), componentsFile) : null;
|
|
15261
16276
|
return {
|
|
15262
16277
|
plan: planComponentSlots(componentsFile, analysis),
|
|
15263
16278
|
tags: analysis ? [...analysis.mdx, ...analysis.islands].map((entry) => entry.key) : [],
|
|
@@ -15265,17 +16280,34 @@ var buildComponentSlots = async (componentsFile) => {
|
|
|
15265
16280
|
};
|
|
15266
16281
|
};
|
|
15267
16282
|
var contentWatchesRuntimeDir = (hasFilesystemSource, collectionBase, context) => hasFilesystemSource && runtimeDirWithin(collectionBase, context.outDir) !== null;
|
|
16283
|
+
var projectOgFonts = (project) => resolveOgFonts({
|
|
16284
|
+
ogFonts: project.config.seo.og.fonts,
|
|
16285
|
+
themeFonts: project.config.theme.fonts,
|
|
16286
|
+
themeFontsConfigured: project.themeFontsConfigured
|
|
16287
|
+
}, project.context.root);
|
|
16288
|
+
var assertFontFilesExist = (project) => {
|
|
16289
|
+
const { config, context } = project;
|
|
16290
|
+
const missing = missingFontFiles({ ogFonts: config.seo.og.fonts ?? [], themeFonts: config.theme.fonts }, context.root);
|
|
16291
|
+
if (missing.length > 0) {
|
|
16292
|
+
throw new BlumeError({
|
|
16293
|
+
code: "BLUME_FONT_FILE_MISSING",
|
|
16294
|
+
message: `Configured font file(s) not found: ${missing.join(", ")}. Font paths resolve relative to the project root.`,
|
|
16295
|
+
severity: "error"
|
|
16296
|
+
});
|
|
16297
|
+
}
|
|
16298
|
+
};
|
|
15268
16299
|
var generateRuntime = async (project) => {
|
|
15269
16300
|
const { context, config } = project;
|
|
16301
|
+
assertFontFilesExist(project);
|
|
15270
16302
|
const out = context.outDir;
|
|
15271
|
-
const srcDir =
|
|
15272
|
-
const askPath =
|
|
15273
|
-
const dataPath =
|
|
15274
|
-
const themePath =
|
|
15275
|
-
const searchClientPath =
|
|
15276
|
-
const examplesPath =
|
|
15277
|
-
const examplesThemePath =
|
|
15278
|
-
const openapiPath =
|
|
16303
|
+
const srcDir = join28(out, "src");
|
|
16304
|
+
const askPath = join28(srcDir, "generated", "Ask.astro");
|
|
16305
|
+
const dataPath = join28(srcDir, "generated", "data.json");
|
|
16306
|
+
const themePath = join28(srcDir, "generated", "app.css");
|
|
16307
|
+
const searchClientPath = join28(srcDir, "generated", "search-client.ts");
|
|
16308
|
+
const examplesPath = join28(srcDir, "generated", "examples.ts");
|
|
16309
|
+
const examplesThemePath = join28(srcDir, "generated", "examples.css");
|
|
16310
|
+
const openapiPath = join28(srcDir, "generated", "openapi.json");
|
|
15279
16311
|
const written = new Set;
|
|
15280
16312
|
const write = (path, content) => {
|
|
15281
16313
|
written.add(normalize3(path));
|
|
@@ -15330,7 +16362,7 @@ var generateRuntime = async (project) => {
|
|
|
15330
16362
|
const docsCollection = resolveDocsCollection(config, context);
|
|
15331
16363
|
const [structural] = await Promise.all([
|
|
15332
16364
|
Promise.all([
|
|
15333
|
-
write(
|
|
16365
|
+
write(join28(out, "astro.config.mjs"), astroConfigTemplate({
|
|
15334
16366
|
aliases: resolveTsconfigAliases(context.root),
|
|
15335
16367
|
askPath,
|
|
15336
16368
|
config,
|
|
@@ -15350,26 +16382,26 @@ var generateRuntime = async (project) => {
|
|
|
15350
16382
|
searchClientPath,
|
|
15351
16383
|
themePath
|
|
15352
16384
|
})),
|
|
15353
|
-
write(
|
|
15354
|
-
write(
|
|
15355
|
-
write(
|
|
15356
|
-
write(
|
|
16385
|
+
write(join28(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
|
|
16386
|
+
write(join28(out, "tsconfig.json"), runtimeTsconfigTemplate()),
|
|
16387
|
+
write(join28(srcDir, "env.d.ts"), envTemplate()),
|
|
16388
|
+
write(join28(srcDir, "content.config.ts"), contentConfigTemplate({
|
|
15357
16389
|
collection: docsCollection,
|
|
15358
16390
|
config,
|
|
15359
16391
|
context,
|
|
15360
16392
|
filesystem: hasFilesystemSource,
|
|
15361
16393
|
staged: hasStaged
|
|
15362
16394
|
})),
|
|
15363
|
-
write(
|
|
16395
|
+
write(join28(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
|
|
15364
16396
|
exportEpub,
|
|
15365
16397
|
exportPdf,
|
|
15366
16398
|
mathEnabled: usesMath,
|
|
15367
16399
|
needsReact
|
|
15368
16400
|
})),
|
|
15369
16401
|
write(askPath, askComponentTemplate(askEnabled)),
|
|
15370
|
-
write(
|
|
15371
|
-
write(
|
|
15372
|
-
write(
|
|
16402
|
+
write(join28(srcDir, "generated", "components.ts"), slotPlan.module),
|
|
16403
|
+
write(join28(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
|
|
16404
|
+
write(join28(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples, config.basePath)),
|
|
15373
16405
|
write(examplesThemePath, examplesEntryTemplate({
|
|
15374
16406
|
configTokens: buildThemeCss(config.theme),
|
|
15375
16407
|
sources: [`${context.root}/**/*.{astro,jsx,svelte,ts,tsx,vue}`],
|
|
@@ -15385,17 +16417,17 @@ var generateRuntime = async (project) => {
|
|
|
15385
16417
|
userTheme
|
|
15386
16418
|
}))
|
|
15387
16419
|
]),
|
|
15388
|
-
Promise.all(islandDiscovery.islands.map((island) => write(
|
|
15389
|
-
Promise.all(slotPlan.wrappers.map((wrapper) => write(
|
|
15390
|
-
Promise.all(exampleDiscovery.examples.map((example) => write(
|
|
16420
|
+
Promise.all(islandDiscovery.islands.map((island) => write(join28(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island)))),
|
|
16421
|
+
Promise.all(slotPlan.wrappers.map((wrapper) => write(join28(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content))),
|
|
16422
|
+
Promise.all(exampleDiscovery.examples.map((example) => write(join28(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example)))),
|
|
15391
16423
|
writeAskFiles(project, srcDir, write),
|
|
15392
16424
|
writeMcpFiles(project, mcp, write)
|
|
15393
16425
|
]);
|
|
15394
16426
|
if (config.seo.og.enabled) {
|
|
15395
|
-
await write(
|
|
16427
|
+
await write(join28(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes, projectOgFonts(project)));
|
|
15396
16428
|
}
|
|
15397
16429
|
if (hasGeneratedChangelog(project, pages)) {
|
|
15398
|
-
await write(
|
|
16430
|
+
await write(join28(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
|
|
15399
16431
|
exportEpub,
|
|
15400
16432
|
exportPdf,
|
|
15401
16433
|
needsReact,
|
|
@@ -15415,27 +16447,31 @@ var generateRuntime = async (project) => {
|
|
|
15415
16447
|
]);
|
|
15416
16448
|
if (servesStaticIndex(config.search.provider)) {
|
|
15417
16449
|
const documents = await buildSearchDocuments(project);
|
|
15418
|
-
await write(
|
|
16450
|
+
await write(join28(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
|
|
15419
16451
|
`);
|
|
15420
|
-
await write(
|
|
16452
|
+
await write(join28(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
|
|
15421
16453
|
}
|
|
15422
16454
|
if (config.search.provider === "mixedbread") {
|
|
15423
|
-
await write(
|
|
16455
|
+
await write(join28(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
|
|
15424
16456
|
}
|
|
15425
16457
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
16458
|
+
const contentAssets = await collectContentAssets(project);
|
|
15426
16459
|
await Promise.all([
|
|
15427
|
-
write(
|
|
16460
|
+
write(join28(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
|
|
16461
|
+
`),
|
|
16462
|
+
write(join28(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate("md")),
|
|
16463
|
+
write(join28(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate("mdx")),
|
|
16464
|
+
write(join28(srcDir, "generated", "content-assets.json"), `${JSON.stringify(contentAssets)}
|
|
15428
16465
|
`),
|
|
15429
|
-
write(
|
|
15430
|
-
write(join26(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate("mdx"))
|
|
16466
|
+
write(join28(srcDir, "pages", "blume-assets", "[...asset].ts"), contentAssetsEndpointTemplate(join28(project.context.outDir, "public", "blume-assets")))
|
|
15431
16467
|
]);
|
|
15432
16468
|
const feeds = buildRssFeeds(project);
|
|
15433
16469
|
if (feeds.length > 0) {
|
|
15434
16470
|
const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
|
|
15435
16471
|
await Promise.all([
|
|
15436
|
-
write(
|
|
16472
|
+
write(join28(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
|
|
15437
16473
|
`),
|
|
15438
|
-
write(
|
|
16474
|
+
write(join28(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
|
|
15439
16475
|
]);
|
|
15440
16476
|
}
|
|
15441
16477
|
const warnings = [
|
|
@@ -15471,14 +16507,14 @@ var generateRuntime = async (project) => {
|
|
|
15471
16507
|
root: context.root
|
|
15472
16508
|
});
|
|
15473
16509
|
warnings.push(...references.warnings);
|
|
15474
|
-
await Promise.all(references.files.map((file) => write(
|
|
16510
|
+
await Promise.all(references.files.map((file) => write(join28(srcDir, "pages", file.pagePath), file.content)));
|
|
15475
16511
|
}
|
|
15476
16512
|
const openApiSource2 = project.sources.find(isOpenApiSource);
|
|
15477
16513
|
await Promise.all([
|
|
15478
|
-
write(
|
|
16514
|
+
write(join28(srcDir, "generated", "data.json"), buildRuntimeData(project)),
|
|
15479
16515
|
write(openapiPath, `${JSON.stringify(openApiSource2 ? openApiSource2.openApiData() : {})}
|
|
15480
16516
|
`),
|
|
15481
|
-
write(
|
|
16517
|
+
write(join28(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
|
|
15482
16518
|
`),
|
|
15483
16519
|
writeStagedContent(out, staged)
|
|
15484
16520
|
]);
|
|
@@ -15487,8 +16523,8 @@ var generateRuntime = async (project) => {
|
|
|
15487
16523
|
};
|
|
15488
16524
|
|
|
15489
16525
|
// src/cli/env.ts
|
|
15490
|
-
import { existsSync as
|
|
15491
|
-
import { dirname as
|
|
16526
|
+
import { existsSync as existsSync17, readFileSync as readFileSync10 } from "node:fs";
|
|
16527
|
+
import { dirname as dirname11, join as join29, resolve as resolve9 } from "pathe";
|
|
15492
16528
|
var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
|
|
15493
16529
|
var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
|
|
15494
16530
|
var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
|
|
@@ -15534,19 +16570,19 @@ var applyEnv = (parsed) => {
|
|
|
15534
16570
|
};
|
|
15535
16571
|
var loadFile = (path) => {
|
|
15536
16572
|
try {
|
|
15537
|
-
if (
|
|
16573
|
+
if (existsSync17(path)) {
|
|
15538
16574
|
applyEnv(parseEnv(readFileSync10(path, "utf-8")));
|
|
15539
16575
|
}
|
|
15540
16576
|
} catch {}
|
|
15541
16577
|
};
|
|
15542
16578
|
var loadEnvFiles = (startDir) => {
|
|
15543
|
-
let dir =
|
|
16579
|
+
let dir = resolve9(startDir);
|
|
15544
16580
|
let done = false;
|
|
15545
16581
|
while (!done) {
|
|
15546
|
-
loadFile(
|
|
15547
|
-
loadFile(
|
|
15548
|
-
const parent =
|
|
15549
|
-
done =
|
|
16582
|
+
loadFile(join29(dir, ".env.local"));
|
|
16583
|
+
loadFile(join29(dir, ".env"));
|
|
16584
|
+
const parent = dirname11(dir);
|
|
16585
|
+
done = existsSync17(join29(dir, ".git")) || parent === dir;
|
|
15550
16586
|
dir = parent;
|
|
15551
16587
|
}
|
|
15552
16588
|
};
|
|
@@ -15651,20 +16687,91 @@ var emitRedirectFiles = async (config, distDir) => {
|
|
|
15651
16687
|
if (redirects.length === 0 || config.deployment.output !== "static") {
|
|
15652
16688
|
return;
|
|
15653
16689
|
}
|
|
15654
|
-
await writeFile8(
|
|
16690
|
+
await writeFile8(join30(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
|
|
15655
16691
|
const platformFiles = [
|
|
15656
16692
|
{ content: buildNetlifyRedirects(redirects), name: "_redirects" },
|
|
15657
16693
|
{ content: buildVercelConfig(redirects), name: "vercel.json" }
|
|
15658
16694
|
];
|
|
15659
|
-
await Promise.all(platformFiles.map((file) =>
|
|
16695
|
+
await Promise.all(platformFiles.map((file) => existsSync18(join30(distDir, file.name)) ? Promise.resolve() : writeFile8(join30(distDir, file.name), file.content, "utf-8")));
|
|
15660
16696
|
logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
|
|
15661
16697
|
};
|
|
15662
|
-
var emitHeaderFiles = async (
|
|
15663
|
-
|
|
16698
|
+
var emitHeaderFiles = async (project, distDir) => {
|
|
16699
|
+
const { config } = project;
|
|
16700
|
+
if (config.deployment.output !== "static" || existsSync18(join30(distDir, "_headers"))) {
|
|
16701
|
+
return;
|
|
16702
|
+
}
|
|
16703
|
+
await writeFile8(join30(distDir, "_headers"), buildNetlifyHeaders(config, buildHomeLinkHeader(config, project.manifest.routes.map((route) => route.path))), "utf-8");
|
|
16704
|
+
logger.success("Emitted _headers (UTF-8 Content-Type + homepage Link header)");
|
|
16705
|
+
};
|
|
16706
|
+
var emitAgentSkills = async (project, distDir) => {
|
|
16707
|
+
const configured = project.config.ai.skills;
|
|
16708
|
+
if (!configured) {
|
|
16709
|
+
return;
|
|
16710
|
+
}
|
|
16711
|
+
const dir = resolve10(project.context.root, configured);
|
|
16712
|
+
if (!existsSync18(dir)) {
|
|
16713
|
+
logger.warn(`ai.skills points at "${configured}" (${dir}), which does not exist; no skills published.`);
|
|
16714
|
+
return;
|
|
16715
|
+
}
|
|
16716
|
+
const outDir = join30(distDir, AGENT_SKILLS_DIR.slice(1));
|
|
16717
|
+
if (existsSync18(join30(outDir, "index.json"))) {
|
|
16718
|
+
return;
|
|
16719
|
+
}
|
|
16720
|
+
const { skills, warnings } = await collectSkills(dir);
|
|
16721
|
+
for (const warning of warnings) {
|
|
16722
|
+
logger.warn(warning);
|
|
16723
|
+
}
|
|
16724
|
+
if (skills.length === 0) {
|
|
16725
|
+
logger.warn(`ai.skills: no publishable skills found in "${configured}".`);
|
|
16726
|
+
return;
|
|
16727
|
+
}
|
|
16728
|
+
await Promise.all(skills.map(async (skill) => {
|
|
16729
|
+
const target = join30(outDir, skill.path);
|
|
16730
|
+
await mkdir7(dirname12(target), { recursive: true });
|
|
16731
|
+
await writeFile8(target, skill.content);
|
|
16732
|
+
}));
|
|
16733
|
+
await writeFile8(join30(outDir, "index.json"), buildSkillsIndex(skills, project.config), "utf-8");
|
|
16734
|
+
logger.success(`Published ${skills.length} agent skill(s) (.well-known/agent-skills/index.json)`);
|
|
16735
|
+
};
|
|
16736
|
+
var emitWellKnownFiles = async (config, distDir) => {
|
|
16737
|
+
const files = [
|
|
16738
|
+
{
|
|
16739
|
+
content: buildSignaturesDirectory(config),
|
|
16740
|
+
label: "Web Bot Auth",
|
|
16741
|
+
path: SIGNATURES_DIRECTORY_PATH
|
|
16742
|
+
},
|
|
16743
|
+
{
|
|
16744
|
+
content: buildApiCatalog(config),
|
|
16745
|
+
label: "RFC 9727",
|
|
16746
|
+
path: API_CATALOG_PATH
|
|
16747
|
+
}
|
|
16748
|
+
];
|
|
16749
|
+
for (const file of files) {
|
|
16750
|
+
const target = join30(distDir, file.path.slice(1));
|
|
16751
|
+
if (!file.content || existsSync18(target)) {
|
|
16752
|
+
continue;
|
|
16753
|
+
}
|
|
16754
|
+
await mkdir7(join30(distDir, ".well-known"), { recursive: true });
|
|
16755
|
+
await writeFile8(target, file.content, "utf-8");
|
|
16756
|
+
logger.success(`Generated ${file.path.slice(1)} (${file.label})`);
|
|
16757
|
+
}
|
|
16758
|
+
};
|
|
16759
|
+
var emitVercelNegotiation = async (config, routePaths, root) => {
|
|
16760
|
+
const configPath = join30(root, ".vercel", "output", "config.json");
|
|
16761
|
+
if (!existsSync18(configPath)) {
|
|
16762
|
+
return;
|
|
16763
|
+
}
|
|
16764
|
+
const overrides = {
|
|
16765
|
+
...hasApiCatalog(config) ? { [API_CATALOG_PATH.slice(1)]: API_CATALOG_TYPE } : {},
|
|
16766
|
+
...config.ai.webBotAuth.keys.length > 0 ? { [SIGNATURES_DIRECTORY_PATH.slice(1)]: SIGNATURES_DIRECTORY_TYPE } : {}
|
|
16767
|
+
};
|
|
16768
|
+
const injected = injectNegotiationRoutes(await readFile17(configPath, "utf-8"), routePaths, buildHomeLinkHeader(config, routePaths), overrides);
|
|
16769
|
+
if (injected === null) {
|
|
16770
|
+
logger.warn("Could not wire Accept: text/markdown negotiation into .vercel/output/config.json — raw Markdown stays available at the .md URLs.");
|
|
15664
16771
|
return;
|
|
15665
16772
|
}
|
|
15666
|
-
await writeFile8(
|
|
15667
|
-
logger.success("
|
|
16773
|
+
await writeFile8(configPath, injected, "utf-8");
|
|
16774
|
+
logger.success("Wired Accept: text/markdown negotiation into the Vercel routing config");
|
|
15668
16775
|
};
|
|
15669
16776
|
var formatBytes2 = (bytes) => {
|
|
15670
16777
|
if (bytes < 1024) {
|
|
@@ -15674,14 +16781,14 @@ var formatBytes2 = (bytes) => {
|
|
|
15674
16781
|
return `${(bytes / 1024).toFixed(digits)} kB`;
|
|
15675
16782
|
};
|
|
15676
16783
|
var astroAssets = async (distDir, ext) => {
|
|
15677
|
-
const astroDir =
|
|
15678
|
-
if (!
|
|
16784
|
+
const astroDir = join30(distDir, "_astro");
|
|
16785
|
+
if (!existsSync18(astroDir)) {
|
|
15679
16786
|
return [];
|
|
15680
16787
|
}
|
|
15681
|
-
const entries = await
|
|
16788
|
+
const entries = await readdir2(astroDir);
|
|
15682
16789
|
const files = entries.filter((name) => name.endsWith(`.${ext}`));
|
|
15683
16790
|
const sized = await Promise.all(files.map(async (name) => {
|
|
15684
|
-
const info = await
|
|
16791
|
+
const info = await stat3(join30(astroDir, name));
|
|
15685
16792
|
return { name, size: info.size };
|
|
15686
16793
|
}));
|
|
15687
16794
|
return sized.toSorted((a, b) => b.size - a.size);
|
|
@@ -15734,26 +16841,26 @@ var runClientAssetChecks = async (staticDir, args) => {
|
|
|
15734
16841
|
var isolatedOutputDir = (config, context) => {
|
|
15735
16842
|
const { adapter, output } = config.deployment;
|
|
15736
16843
|
if (output === "server" && adapter === "vercel") {
|
|
15737
|
-
return
|
|
16844
|
+
return join30(context.outDir, ".vercel", "output");
|
|
15738
16845
|
}
|
|
15739
|
-
return context.distDir ??
|
|
16846
|
+
return context.distDir ?? join30(context.outDir, "dist");
|
|
15740
16847
|
};
|
|
15741
16848
|
var isolatedStaticDir = (config, context) => {
|
|
15742
16849
|
const { adapter, output } = config.deployment;
|
|
15743
16850
|
const outputDir = isolatedOutputDir(config, context);
|
|
15744
16851
|
if (output === "server" && adapter === "vercel") {
|
|
15745
|
-
return
|
|
16852
|
+
return join30(outputDir, "static");
|
|
15746
16853
|
}
|
|
15747
16854
|
if (output === "server" && adapter === "node") {
|
|
15748
|
-
return
|
|
16855
|
+
return join30(outputDir, "client");
|
|
15749
16856
|
}
|
|
15750
16857
|
return outputDir;
|
|
15751
16858
|
};
|
|
15752
16859
|
var publishLlmsFiles = async (project, distDir) => {
|
|
15753
|
-
const indexPath =
|
|
15754
|
-
const fullPath =
|
|
15755
|
-
const writeIndex = !
|
|
15756
|
-
const writeFull = !
|
|
16860
|
+
const indexPath = join30(distDir, "llms.txt");
|
|
16861
|
+
const fullPath = join30(distDir, "llms-full.txt");
|
|
16862
|
+
const writeIndex = !existsSync18(indexPath);
|
|
16863
|
+
const writeFull = !existsSync18(fullPath);
|
|
15757
16864
|
if (!(writeIndex || writeFull)) {
|
|
15758
16865
|
return;
|
|
15759
16866
|
}
|
|
@@ -15786,23 +16893,25 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
15786
16893
|
await publishLlmsFiles(project, distDir);
|
|
15787
16894
|
}
|
|
15788
16895
|
const sitemap = buildSitemap(project);
|
|
15789
|
-
if (sitemap && !
|
|
15790
|
-
await writeFile8(
|
|
16896
|
+
if (sitemap && !existsSync18(join30(distDir, "sitemap.xml"))) {
|
|
16897
|
+
await writeFile8(join30(distDir, "sitemap.xml"), sitemap, "utf-8");
|
|
15791
16898
|
logger.success("Generated sitemap.xml");
|
|
15792
16899
|
}
|
|
15793
16900
|
const robots = buildRobots(project);
|
|
15794
|
-
if (robots && !
|
|
15795
|
-
await writeFile8(
|
|
16901
|
+
if (robots && !existsSync18(join30(distDir, "robots.txt"))) {
|
|
16902
|
+
await writeFile8(join30(distDir, "robots.txt"), robots, "utf-8");
|
|
15796
16903
|
logger.success("Generated robots.txt");
|
|
15797
16904
|
}
|
|
15798
16905
|
const agentReadability = buildAgentReadability(project);
|
|
15799
|
-
if (agentReadability && !
|
|
15800
|
-
await writeFile8(
|
|
16906
|
+
if (agentReadability && !existsSync18(join30(distDir, "agent-readability.json"))) {
|
|
16907
|
+
await writeFile8(join30(distDir, "agent-readability.json"), `${JSON.stringify(agentReadability, null, 2)}
|
|
15801
16908
|
`, "utf-8");
|
|
15802
16909
|
logger.success("Generated agent-readability.json");
|
|
15803
16910
|
}
|
|
16911
|
+
await emitWellKnownFiles(project.config, distDir);
|
|
16912
|
+
await emitAgentSkills(project, distDir);
|
|
15804
16913
|
await emitRedirectFiles(project.config, distDir);
|
|
15805
|
-
await emitHeaderFiles(project
|
|
16914
|
+
await emitHeaderFiles(project, distDir);
|
|
15806
16915
|
const { config } = project;
|
|
15807
16916
|
const features = serverFeatures(config);
|
|
15808
16917
|
const sitemapNote = config.seo.sitemap ? "no (set deployment.site)" : "no (seo.sitemap is false)";
|
|
@@ -15916,16 +17025,19 @@ var buildCommand = defineCommand3({
|
|
|
15916
17025
|
if (surfaced.moved) {
|
|
15917
17026
|
logger.success(`Surfaced ${adapter} output to ${surfaced.to}`);
|
|
15918
17027
|
}
|
|
17028
|
+
if (project.config.deployment.output === "server" && adapter === "vercel") {
|
|
17029
|
+
await emitVercelNegotiation(project.config, project.manifest.routes.map((route) => route.path), root);
|
|
17030
|
+
}
|
|
15919
17031
|
await publishBuildArtifacts(project, deployStaticDir(project.config, project.context), args);
|
|
15920
17032
|
}
|
|
15921
17033
|
});
|
|
15922
17034
|
|
|
15923
17035
|
// src/cli/commands/check.ts
|
|
15924
|
-
import { existsSync as
|
|
17036
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
15925
17037
|
import { check } from "@astrojs/check";
|
|
15926
17038
|
import { sync } from "astro";
|
|
15927
17039
|
import { defineCommand as defineCommand4 } from "citty";
|
|
15928
|
-
import { join as
|
|
17040
|
+
import { join as join31 } from "pathe";
|
|
15929
17041
|
var checkCommand = defineCommand4({
|
|
15930
17042
|
args: {
|
|
15931
17043
|
isolated: {
|
|
@@ -15961,13 +17073,13 @@ var checkCommand = defineCommand4({
|
|
|
15961
17073
|
});
|
|
15962
17074
|
const { outDir } = project.context;
|
|
15963
17075
|
await sync({ logLevel: "warn", root: outDir });
|
|
15964
|
-
const tsconfig =
|
|
17076
|
+
const tsconfig = join31(root, "tsconfig.json");
|
|
15965
17077
|
logger.start(`Type-checking ${project.graph.pages.length} page(s)`);
|
|
15966
17078
|
const failed = await check({
|
|
15967
17079
|
minimumFailingSeverity: "error",
|
|
15968
17080
|
minimumSeverity: "hint",
|
|
15969
17081
|
root: outDir,
|
|
15970
|
-
tsconfig:
|
|
17082
|
+
tsconfig: existsSync19(tsconfig) ? tsconfig : undefined,
|
|
15971
17083
|
watch: false
|
|
15972
17084
|
});
|
|
15973
17085
|
if (failed) {
|
|
@@ -15982,7 +17094,7 @@ var checkCommand = defineCommand4({
|
|
|
15982
17094
|
import { watch } from "node:fs";
|
|
15983
17095
|
import { dev } from "astro";
|
|
15984
17096
|
import { defineCommand as defineCommand5 } from "citty";
|
|
15985
|
-
import { basename as basename4, dirname as
|
|
17097
|
+
import { basename as basename4, dirname as dirname13 } from "pathe";
|
|
15986
17098
|
|
|
15987
17099
|
// src/astro/integration.ts
|
|
15988
17100
|
var overlayServer = null;
|
|
@@ -16175,7 +17287,7 @@ var devCommand = defineCommand5({
|
|
|
16175
17287
|
}),
|
|
16176
17288
|
...fileTargets.map((target) => {
|
|
16177
17289
|
const name = basename4(target);
|
|
16178
|
-
const watcher = watch(
|
|
17290
|
+
const watcher = watch(dirname13(target), (_event, filename) => {
|
|
16179
17291
|
if (!filename || filename === name) {
|
|
16180
17292
|
regenerate();
|
|
16181
17293
|
}
|
|
@@ -16199,12 +17311,12 @@ var devCommand = defineCommand5({
|
|
|
16199
17311
|
// src/cli/commands/doctor.ts
|
|
16200
17312
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
16201
17313
|
import { defineCommand as defineCommand6 } from "citty";
|
|
16202
|
-
import { join as
|
|
17314
|
+
import { join as join32 } from "pathe";
|
|
16203
17315
|
var FALLBACK_MIN_NODE = "22.12.0";
|
|
16204
17316
|
var LEADING_RANGE = /^[^\d]*/u;
|
|
16205
17317
|
var minSupportedNode = () => {
|
|
16206
17318
|
try {
|
|
16207
|
-
const pkg = JSON.parse(readFileSync11(
|
|
17319
|
+
const pkg = JSON.parse(readFileSync11(join32(packageRoot(), "package.json"), "utf-8"));
|
|
16208
17320
|
const range = pkg.engines?.node ?? "";
|
|
16209
17321
|
return range.replace(LEADING_RANGE, "") || FALLBACK_MIN_NODE;
|
|
16210
17322
|
} catch {
|
|
@@ -16297,21 +17409,21 @@ var doctorCommand = defineCommand6({
|
|
|
16297
17409
|
|
|
16298
17410
|
// src/cli/commands/eject.ts
|
|
16299
17411
|
import { defineCommand as defineCommand7 } from "citty";
|
|
16300
|
-
import { relative as
|
|
17412
|
+
import { relative as relative17 } from "pathe";
|
|
16301
17413
|
|
|
16302
17414
|
// src/registry/eject.ts
|
|
16303
|
-
import { existsSync as
|
|
16304
|
-
import { cp as cp2, mkdir as
|
|
16305
|
-
import { join as
|
|
17415
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
17416
|
+
import { cp as cp2, mkdir as mkdir8, readFile as readFile18, rm as rm3, writeFile as writeFile9 } from "node:fs/promises";
|
|
17417
|
+
import { join as join33, relative as relative15 } from "pathe";
|
|
16306
17418
|
var toPosix = (path) => path.split("\\").join("/");
|
|
16307
17419
|
var LOCAL_BLUME_SOURCE = "../../node_modules/blume/src/**/*.{astro,ts,tsx}";
|
|
16308
17420
|
var blumeSourceGlob = (root, genDir, resolveBlumeRoot = packageRoot) => {
|
|
16309
|
-
if (
|
|
17421
|
+
if (existsSync20(join33(root, "node_modules", "blume"))) {
|
|
16310
17422
|
return LOCAL_BLUME_SOURCE;
|
|
16311
17423
|
}
|
|
16312
17424
|
try {
|
|
16313
|
-
const src =
|
|
16314
|
-
return `${toPosix(
|
|
17425
|
+
const src = join33(resolveBlumeRoot(), "src");
|
|
17426
|
+
return `${toPosix(relative15(genDir, src))}/**/*.{astro,ts,tsx}`;
|
|
16315
17427
|
} catch {
|
|
16316
17428
|
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.');
|
|
16317
17429
|
return LOCAL_BLUME_SOURCE;
|
|
@@ -16324,28 +17436,28 @@ var ejectOpenApiData = (project) => {
|
|
|
16324
17436
|
var askFiles = async (project, srcDir, genDir) => {
|
|
16325
17437
|
const { ask } = project.config.ai;
|
|
16326
17438
|
if (!(ask?.enabled && !ask.endpoint)) {
|
|
16327
|
-
const endpointPath =
|
|
16328
|
-
if (
|
|
16329
|
-
const content = await
|
|
17439
|
+
const endpointPath = join33(srcDir, "pages", "api", "ask.ts");
|
|
17440
|
+
if (existsSync20(endpointPath)) {
|
|
17441
|
+
const content = await readFile18(endpointPath, "utf-8");
|
|
16330
17442
|
if (content.startsWith("// Generated by Blume. Do not edit.")) {
|
|
16331
17443
|
await rm3(endpointPath, { force: true });
|
|
16332
17444
|
}
|
|
16333
17445
|
}
|
|
16334
|
-
await rm3(
|
|
17446
|
+
await rm3(join33(genDir, "ask-data.json"), { force: true });
|
|
16335
17447
|
return [];
|
|
16336
17448
|
}
|
|
16337
17449
|
const grounded = ask.provider !== "inkeep";
|
|
16338
17450
|
const files = [
|
|
16339
17451
|
{
|
|
16340
17452
|
content: askEndpointTemplate(resolveAskBackend(ask), grounded),
|
|
16341
|
-
path:
|
|
17453
|
+
path: join33(srcDir, "pages", "api", "ask.ts")
|
|
16342
17454
|
}
|
|
16343
17455
|
];
|
|
16344
17456
|
if (grounded) {
|
|
16345
17457
|
files.push({
|
|
16346
17458
|
content: `${JSON.stringify(await buildAskData(project))}
|
|
16347
17459
|
`,
|
|
16348
|
-
path:
|
|
17460
|
+
path: join33(genDir, "ask-data.json")
|
|
16349
17461
|
});
|
|
16350
17462
|
}
|
|
16351
17463
|
return files;
|
|
@@ -16378,19 +17490,19 @@ var mcpFiles = async (project, userPages, srcDir, genDir) => {
|
|
|
16378
17490
|
{
|
|
16379
17491
|
content: `${JSON.stringify(data)}
|
|
16380
17492
|
`,
|
|
16381
|
-
path:
|
|
17493
|
+
path: join33(genDir, "mcp-data.json")
|
|
16382
17494
|
},
|
|
16383
17495
|
{
|
|
16384
17496
|
content: mcpEndpointTemplate(route),
|
|
16385
|
-
path:
|
|
17497
|
+
path: join33(srcDir, "pages", mcpPageFile(route))
|
|
16386
17498
|
},
|
|
16387
17499
|
{
|
|
16388
17500
|
content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),
|
|
16389
|
-
path:
|
|
17501
|
+
path: join33(srcDir, "blume-mcp", "discovery.ts")
|
|
16390
17502
|
},
|
|
16391
17503
|
{
|
|
16392
17504
|
content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),
|
|
16393
|
-
path:
|
|
17505
|
+
path: join33(srcDir, "blume-mcp", "server-card.ts")
|
|
16394
17506
|
}
|
|
16395
17507
|
];
|
|
16396
17508
|
};
|
|
@@ -16403,23 +17515,23 @@ var changelogFiles = (project, userPages, srcDir, options) => {
|
|
|
16403
17515
|
return [
|
|
16404
17516
|
{
|
|
16405
17517
|
content: changelogIndexTemplate(options),
|
|
16406
|
-
path:
|
|
17518
|
+
path: join33(srcDir, "pages", "changelog.astro")
|
|
16407
17519
|
}
|
|
16408
17520
|
];
|
|
16409
17521
|
};
|
|
16410
|
-
var readExamplesCss = (root, css) => css &&
|
|
17522
|
+
var readExamplesCss = (root, css) => css && existsSync20(join33(root, css)) ? readFile18(join33(root, css), "utf-8") : Promise.resolve("");
|
|
16411
17523
|
var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
|
|
16412
17524
|
{
|
|
16413
17525
|
content: examplesPageTemplate(),
|
|
16414
|
-
path:
|
|
17526
|
+
path: join33(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
|
|
16415
17527
|
}
|
|
16416
17528
|
] : [];
|
|
16417
|
-
var ejectIntegrationBridge = (config, root, configFile) => config.integrations.length > 0 && configFile ? { configFile: toPosix(
|
|
17529
|
+
var ejectIntegrationBridge = (config, root, configFile) => config.integrations.length > 0 && configFile ? { configFile: toPosix(relative15(root, configFile)) } : undefined;
|
|
16418
17530
|
var eject = async (root) => {
|
|
16419
17531
|
const project = await scanProject(root, { mode: "build" });
|
|
16420
17532
|
const { context, config } = project;
|
|
16421
|
-
const srcDir =
|
|
16422
|
-
const genDir =
|
|
17533
|
+
const srcDir = join33(root, "src");
|
|
17534
|
+
const genDir = join33(srcDir, "generated");
|
|
16423
17535
|
const askEnabled = config.ai.ask?.enabled ?? false;
|
|
16424
17536
|
const exportPdf = config.export.pdf;
|
|
16425
17537
|
const exportEpub = config.export.epub;
|
|
@@ -16437,7 +17549,7 @@ var eject = async (root) => {
|
|
|
16437
17549
|
context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
|
|
16438
17550
|
detectNeedsReact(root),
|
|
16439
17551
|
detectUsesMath(root),
|
|
16440
|
-
context.themeFile ?
|
|
17552
|
+
context.themeFile ? readFile18(context.themeFile, "utf-8") : Promise.resolve(""),
|
|
16441
17553
|
readExamplesCss(root, config.examples.css),
|
|
16442
17554
|
buildRawMarkdown(project),
|
|
16443
17555
|
discoverIslands(root)
|
|
@@ -16451,14 +17563,14 @@ var eject = async (root) => {
|
|
|
16451
17563
|
const needsSvelte = frameworks.has("svelte");
|
|
16452
17564
|
const relContext = {
|
|
16453
17565
|
...context,
|
|
16454
|
-
contentRoot: toPosix(
|
|
17566
|
+
contentRoot: toPosix(relative15(root, context.contentRoot)),
|
|
16455
17567
|
outDir: ".",
|
|
16456
17568
|
root: "."
|
|
16457
17569
|
};
|
|
16458
|
-
const componentsImport = context.componentsFile ? `../../${toPosix(
|
|
17570
|
+
const componentsImport = context.componentsFile ? `../../${toPosix(relative15(root, context.componentsFile))}` : null;
|
|
16459
17571
|
const relPages = [
|
|
16460
17572
|
...pages.map((page) => ({
|
|
16461
|
-
entrypoint: toPosix(
|
|
17573
|
+
entrypoint: toPosix(relative15(root, page.entrypoint)),
|
|
16462
17574
|
pattern: page.pattern
|
|
16463
17575
|
})),
|
|
16464
17576
|
...mcpDiscoveryPages(project, pages)
|
|
@@ -16485,14 +17597,14 @@ var eject = async (root) => {
|
|
|
16485
17597
|
searchClientPath: "./src/generated/search-client.ts",
|
|
16486
17598
|
themePath: "./src/generated/app.css"
|
|
16487
17599
|
}),
|
|
16488
|
-
path:
|
|
17600
|
+
path: join33(root, "astro.config.mjs")
|
|
16489
17601
|
},
|
|
16490
17602
|
{
|
|
16491
17603
|
content: runtimeTsconfigTemplate(),
|
|
16492
|
-
path:
|
|
17604
|
+
path: join33(root, "tsconfig.json"),
|
|
16493
17605
|
skipIfExists: true
|
|
16494
17606
|
},
|
|
16495
|
-
{ content: envTemplate(), path:
|
|
17607
|
+
{ content: envTemplate(), path: join33(srcDir, "env.d.ts") },
|
|
16496
17608
|
{
|
|
16497
17609
|
content: contentConfigTemplate({
|
|
16498
17610
|
config,
|
|
@@ -16500,7 +17612,7 @@ var eject = async (root) => {
|
|
|
16500
17612
|
staged: hasStaged,
|
|
16501
17613
|
stagedBase: stagedDir
|
|
16502
17614
|
}),
|
|
16503
|
-
path:
|
|
17615
|
+
path: join33(srcDir, "content.config.ts")
|
|
16504
17616
|
},
|
|
16505
17617
|
{
|
|
16506
17618
|
content: catchAllPageTemplate({
|
|
@@ -16509,19 +17621,19 @@ var eject = async (root) => {
|
|
|
16509
17621
|
mathEnabled: usesMath,
|
|
16510
17622
|
needsReact
|
|
16511
17623
|
}),
|
|
16512
|
-
path:
|
|
17624
|
+
path: join33(srcDir, "pages", "[...slug].astro")
|
|
16513
17625
|
},
|
|
16514
17626
|
{
|
|
16515
17627
|
content: planComponentSlots(componentsImport, null).module,
|
|
16516
|
-
path:
|
|
17628
|
+
path: join33(genDir, "components.ts")
|
|
16517
17629
|
},
|
|
16518
17630
|
{
|
|
16519
17631
|
content: islandMapTemplate(islands.islands),
|
|
16520
|
-
path:
|
|
17632
|
+
path: join33(genDir, "islands.ts")
|
|
16521
17633
|
},
|
|
16522
17634
|
{
|
|
16523
17635
|
content: exampleMapTemplate(examples.examples, config.basePath),
|
|
16524
|
-
path:
|
|
17636
|
+
path: join33(genDir, "examples.ts")
|
|
16525
17637
|
},
|
|
16526
17638
|
{
|
|
16527
17639
|
content: examplesEntryTemplate({
|
|
@@ -16529,7 +17641,7 @@ var eject = async (root) => {
|
|
|
16529
17641
|
sources: ["../../**/*.{astro,jsx,svelte,ts,tsx,vue}"],
|
|
16530
17642
|
userCss: userExamplesCss
|
|
16531
17643
|
}),
|
|
16532
|
-
path:
|
|
17644
|
+
path: join33(genDir, "examples.css")
|
|
16533
17645
|
},
|
|
16534
17646
|
{
|
|
16535
17647
|
content: tailwindEntryTemplate({
|
|
@@ -16541,30 +17653,30 @@ var eject = async (root) => {
|
|
|
16541
17653
|
twoslashCss: twoslashCss(),
|
|
16542
17654
|
userTheme
|
|
16543
17655
|
}),
|
|
16544
|
-
path:
|
|
17656
|
+
path: join33(genDir, "app.css")
|
|
16545
17657
|
},
|
|
16546
|
-
{ content: buildRuntimeData(project), path:
|
|
17658
|
+
{ content: buildRuntimeData(project), path: join33(genDir, "data.json") },
|
|
16547
17659
|
{
|
|
16548
17660
|
content: askComponentTemplate(askEnabled),
|
|
16549
|
-
path:
|
|
17661
|
+
path: join33(genDir, "Ask.astro")
|
|
16550
17662
|
},
|
|
16551
17663
|
{
|
|
16552
17664
|
content: `${JSON.stringify(ejectOpenApiData(project))}
|
|
16553
17665
|
`,
|
|
16554
|
-
path:
|
|
17666
|
+
path: join33(genDir, "openapi.json")
|
|
16555
17667
|
},
|
|
16556
17668
|
{
|
|
16557
17669
|
content: `${JSON.stringify(rawMarkdown)}
|
|
16558
17670
|
`,
|
|
16559
|
-
path:
|
|
17671
|
+
path: join33(genDir, "raw-markdown.json")
|
|
16560
17672
|
},
|
|
16561
17673
|
{
|
|
16562
17674
|
content: rawMarkdownEndpointTemplate("md"),
|
|
16563
|
-
path:
|
|
17675
|
+
path: join33(srcDir, "pages", "[...slug].md.ts")
|
|
16564
17676
|
},
|
|
16565
17677
|
{
|
|
16566
17678
|
content: rawMarkdownEndpointTemplate("mdx"),
|
|
16567
|
-
path:
|
|
17679
|
+
path: join33(srcDir, "pages", "[...slug].mdx.ts")
|
|
16568
17680
|
}
|
|
16569
17681
|
];
|
|
16570
17682
|
if (askEnabled) {
|
|
@@ -16573,7 +17685,7 @@ var eject = async (root) => {
|
|
|
16573
17685
|
if (config.seo.og.enabled) {
|
|
16574
17686
|
files.push({
|
|
16575
17687
|
content: ogEndpointTemplate(customOgRoutes(pages, config.title, config.seo.og.titles)),
|
|
16576
|
-
path:
|
|
17688
|
+
path: join33(srcDir, "pages", "og", "[...slug].png.ts")
|
|
16577
17689
|
});
|
|
16578
17690
|
}
|
|
16579
17691
|
files.push(...await mcpFiles(project, pages, srcDir, genDir), ...changelogFiles(project, pages, srcDir, {
|
|
@@ -16585,28 +17697,28 @@ var eject = async (root) => {
|
|
|
16585
17697
|
if (!routeIsTaken(pages, project.graph.pages, "/404")) {
|
|
16586
17698
|
files.push({
|
|
16587
17699
|
content: notFoundPageTemplate(),
|
|
16588
|
-
path:
|
|
17700
|
+
path: join33(srcDir, "pages", "404.astro")
|
|
16589
17701
|
});
|
|
16590
17702
|
}
|
|
16591
17703
|
files.push({
|
|
16592
17704
|
content: searchClientTemplate(config),
|
|
16593
|
-
path:
|
|
17705
|
+
path: join33(genDir, "search-client.ts")
|
|
16594
17706
|
});
|
|
16595
17707
|
if (servesStaticIndex(config.search.provider)) {
|
|
16596
17708
|
const documents = await buildSearchDocuments(project);
|
|
16597
17709
|
files.push({
|
|
16598
17710
|
content: `${JSON.stringify(documents)}
|
|
16599
17711
|
`,
|
|
16600
|
-
path:
|
|
17712
|
+
path: join33(genDir, "search.json")
|
|
16601
17713
|
}, {
|
|
16602
17714
|
content: searchEndpointTemplate(),
|
|
16603
|
-
path:
|
|
17715
|
+
path: join33(srcDir, "pages", "blume-search.json.ts")
|
|
16604
17716
|
});
|
|
16605
17717
|
}
|
|
16606
17718
|
if (config.search.provider === "mixedbread") {
|
|
16607
17719
|
files.push({
|
|
16608
17720
|
content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
|
|
16609
|
-
path:
|
|
17721
|
+
path: join33(srcDir, "pages", "api", "search.ts")
|
|
16610
17722
|
});
|
|
16611
17723
|
}
|
|
16612
17724
|
const feeds = buildRssFeeds(project);
|
|
@@ -16615,10 +17727,10 @@ var eject = async (root) => {
|
|
|
16615
17727
|
files.push({
|
|
16616
17728
|
content: `${JSON.stringify(feedXml)}
|
|
16617
17729
|
`,
|
|
16618
|
-
path:
|
|
17730
|
+
path: join33(genDir, "rss.json")
|
|
16619
17731
|
}, {
|
|
16620
17732
|
content: rssEndpointTemplate(),
|
|
16621
|
-
path:
|
|
17733
|
+
path: join33(srcDir, "pages", "[section]", "rss.xml.ts")
|
|
16622
17734
|
});
|
|
16623
17735
|
}
|
|
16624
17736
|
const warnings = [];
|
|
@@ -16632,28 +17744,28 @@ var eject = async (root) => {
|
|
|
16632
17744
|
for (const file of references.files) {
|
|
16633
17745
|
files.push({
|
|
16634
17746
|
content: file.content,
|
|
16635
|
-
path:
|
|
17747
|
+
path: join33(srcDir, "pages", file.pagePath)
|
|
16636
17748
|
});
|
|
16637
17749
|
}
|
|
16638
17750
|
}
|
|
16639
17751
|
files.push(...islands.islands.map((island) => ({
|
|
16640
17752
|
content: islandWrapperTemplate(island),
|
|
16641
|
-
path:
|
|
17753
|
+
path: join33(genDir, "islands", `${island.name}.astro`)
|
|
16642
17754
|
})), ...examples.examples.map((example) => ({
|
|
16643
17755
|
content: exampleWrapperTemplate(example),
|
|
16644
|
-
path:
|
|
17756
|
+
path: join33(genDir, "examples", `${exampleSlug(example.path)}.astro`)
|
|
16645
17757
|
})), ...examplesPreviewFiles(srcDir, config.basePath, examples.examples.length > 0));
|
|
16646
17758
|
for (const [entryId, content] of staged) {
|
|
16647
|
-
files.push({ content, path:
|
|
17759
|
+
files.push({ content, path: join33(root, stagedDir, entryId) });
|
|
16648
17760
|
}
|
|
16649
|
-
const written = files.filter((file) => !(file.skipIfExists &&
|
|
17761
|
+
const written = files.filter((file) => !(file.skipIfExists && existsSync20(file.path)));
|
|
16650
17762
|
await Promise.all(written.map(async (file) => {
|
|
16651
|
-
await
|
|
17763
|
+
await mkdir8(join33(file.path, ".."), { recursive: true });
|
|
16652
17764
|
await writeFile9(file.path, file.content, "utf-8");
|
|
16653
17765
|
}));
|
|
16654
|
-
const assetsSrc =
|
|
16655
|
-
if (
|
|
16656
|
-
await cp2(assetsSrc,
|
|
17766
|
+
const assetsSrc = join33(context.outDir, "public", "blume-assets");
|
|
17767
|
+
if (existsSync20(assetsSrc)) {
|
|
17768
|
+
await cp2(assetsSrc, join33(root, "public", "blume-assets"), {
|
|
16657
17769
|
recursive: true
|
|
16658
17770
|
});
|
|
16659
17771
|
}
|
|
@@ -16662,8 +17774,8 @@ var eject = async (root) => {
|
|
|
16662
17774
|
};
|
|
16663
17775
|
|
|
16664
17776
|
// src/cli/eject-scripts.ts
|
|
16665
|
-
import { readFile as
|
|
16666
|
-
import { join as
|
|
17777
|
+
import { readFile as readFile19, writeFile as writeFile10 } from "node:fs/promises";
|
|
17778
|
+
import { join as join34 } from "pathe";
|
|
16667
17779
|
var droppedArtifactNotices = (config) => {
|
|
16668
17780
|
const notices = [];
|
|
16669
17781
|
if (config.search.provider === "pagefind") {
|
|
@@ -16690,10 +17802,10 @@ var droppedArtifactNotices = (config) => {
|
|
|
16690
17802
|
return notices;
|
|
16691
17803
|
};
|
|
16692
17804
|
var updatePackageScripts = async (root) => {
|
|
16693
|
-
const pkgPath =
|
|
17805
|
+
const pkgPath = join34(root, "package.json");
|
|
16694
17806
|
let pkg;
|
|
16695
17807
|
try {
|
|
16696
|
-
pkg = JSON.parse(await
|
|
17808
|
+
pkg = JSON.parse(await readFile19(pkgPath, "utf-8"));
|
|
16697
17809
|
} catch {
|
|
16698
17810
|
return;
|
|
16699
17811
|
}
|
|
@@ -16709,9 +17821,9 @@ var updatePackageScripts = async (root) => {
|
|
|
16709
17821
|
};
|
|
16710
17822
|
|
|
16711
17823
|
// src/cli/init/scaffold.ts
|
|
16712
|
-
import { existsSync as
|
|
16713
|
-
import { mkdir as
|
|
16714
|
-
import { basename as basename5, dirname as
|
|
17824
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
17825
|
+
import { mkdir as mkdir9, writeFile as writeFile11 } from "node:fs/promises";
|
|
17826
|
+
import { basename as basename5, dirname as dirname14, isAbsolute as isAbsolute10, join as join35, relative as relative16 } from "pathe";
|
|
16715
17827
|
|
|
16716
17828
|
// src/core/package-json.ts
|
|
16717
17829
|
var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
|
|
@@ -16770,7 +17882,7 @@ var STARTERS = {
|
|
|
16770
17882
|
files: (dir) => [
|
|
16771
17883
|
{
|
|
16772
17884
|
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`."),
|
|
16773
|
-
path:
|
|
17885
|
+
path: join35(dir, "index.mdx")
|
|
16774
17886
|
}
|
|
16775
17887
|
]
|
|
16776
17888
|
},
|
|
@@ -16785,7 +17897,7 @@ var STARTERS = {
|
|
|
16785
17897
|
files: (dir) => [
|
|
16786
17898
|
{
|
|
16787
17899
|
content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
|
|
16788
|
-
path:
|
|
17900
|
+
path: join35(dir, "index.mdx")
|
|
16789
17901
|
},
|
|
16790
17902
|
{
|
|
16791
17903
|
content: `---
|
|
@@ -16796,7 +17908,7 @@ date: 2026-01-01
|
|
|
16796
17908
|
|
|
16797
17909
|
The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
|
|
16798
17910
|
`,
|
|
16799
|
-
path:
|
|
17911
|
+
path: join35(dir, "changelog", "v1-0-0.mdx")
|
|
16800
17912
|
}
|
|
16801
17913
|
]
|
|
16802
17914
|
},
|
|
@@ -16809,7 +17921,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
|
|
|
16809
17921
|
Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
|
|
16810
17922
|
|
|
16811
17923
|
Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
16812
|
-
path:
|
|
17924
|
+
path: join35(dir, "index.mdx")
|
|
16813
17925
|
}
|
|
16814
17926
|
]
|
|
16815
17927
|
},
|
|
@@ -16820,11 +17932,11 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
|
16820
17932
|
content: page("Introduction", "Get started with the SDK.", `# Introduction
|
|
16821
17933
|
|
|
16822
17934
|
Install the SDK and make your first call. See [Installation](/installation).`),
|
|
16823
|
-
path:
|
|
17935
|
+
path: join35(dir, "index.mdx")
|
|
16824
17936
|
},
|
|
16825
17937
|
{
|
|
16826
17938
|
content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
|
|
16827
|
-
path:
|
|
17939
|
+
path: join35(dir, "installation.mdx")
|
|
16828
17940
|
}
|
|
16829
17941
|
]
|
|
16830
17942
|
}
|
|
@@ -16839,7 +17951,7 @@ var detectPackageManager = (userAgent) => {
|
|
|
16839
17951
|
const name = userAgent?.split("/")[0];
|
|
16840
17952
|
return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : "npm";
|
|
16841
17953
|
};
|
|
16842
|
-
var validateContentDir = (root, dir) =>
|
|
17954
|
+
var validateContentDir = (root, dir) => isAbsolute10(dir) || relative16(root, join35(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
|
|
16843
17955
|
var titleize = (raw) => {
|
|
16844
17956
|
const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
|
|
16845
17957
|
return words.length === 0 ? "My Docs" : words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
@@ -16916,27 +18028,27 @@ export default defineConfig({
|
|
|
16916
18028
|
`;
|
|
16917
18029
|
var extraDepsFor = (sources) => ({
|
|
16918
18030
|
...sources.includes("notion") && { "@notionhq/client": "^2.2.15" },
|
|
16919
|
-
...sources.includes("sanity") && { "@sanity/client": "^
|
|
18031
|
+
...sources.includes("sanity") && { "@sanity/client": "^7.25.0" }
|
|
16920
18032
|
});
|
|
16921
18033
|
var buildPlan = (root, answers) => {
|
|
16922
18034
|
const files = [
|
|
16923
18035
|
{
|
|
16924
18036
|
content: blumePackageJson(toPackageName(basename5(root)), extraDepsFor(answers.sources)),
|
|
16925
|
-
path:
|
|
18037
|
+
path: join35(root, "package.json")
|
|
16926
18038
|
},
|
|
16927
|
-
{ content: buildConfig(answers), path:
|
|
18039
|
+
{ content: buildConfig(answers), path: join35(root, "blume.config.ts") }
|
|
16928
18040
|
];
|
|
16929
18041
|
if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
|
|
16930
|
-
files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path:
|
|
18042
|
+
files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path: join35(root, file.path) })));
|
|
16931
18043
|
}
|
|
16932
18044
|
return files;
|
|
16933
18045
|
};
|
|
16934
18046
|
var writeFileSafe = async (file, log) => {
|
|
16935
|
-
if (
|
|
18047
|
+
if (existsSync21(file.path)) {
|
|
16936
18048
|
log.info(`Skipped existing ${file.path}`);
|
|
16937
18049
|
return false;
|
|
16938
18050
|
}
|
|
16939
|
-
await
|
|
18051
|
+
await mkdir9(dirname14(file.path), { recursive: true });
|
|
16940
18052
|
await writeFile11(file.path, file.content, "utf-8");
|
|
16941
18053
|
log.success(`Created ${file.path}`);
|
|
16942
18054
|
return true;
|
|
@@ -17012,7 +18124,7 @@ var ejectCommand = defineCommand7({
|
|
|
17012
18124
|
}
|
|
17013
18125
|
logger.success(`Ejected ${files.length} file(s):`);
|
|
17014
18126
|
for (const file of files) {
|
|
17015
|
-
process.stdout.write(` ${
|
|
18127
|
+
process.stdout.write(` ${relative17(root, file)}
|
|
17016
18128
|
`);
|
|
17017
18129
|
}
|
|
17018
18130
|
reportDroppedArtifacts(notices);
|
|
@@ -17028,9 +18140,9 @@ The blume package remains importable.`);
|
|
|
17028
18140
|
});
|
|
17029
18141
|
|
|
17030
18142
|
// src/cli/commands/eval.ts
|
|
17031
|
-
import { existsSync as
|
|
18143
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
17032
18144
|
import { defineCommand as defineCommand8 } from "citty";
|
|
17033
|
-
import { join as
|
|
18145
|
+
import { join as join39 } from "pathe";
|
|
17034
18146
|
|
|
17035
18147
|
// src/eval/prompts.ts
|
|
17036
18148
|
var readerPrompt = (question) => `You are evaluating whether a product's documentation can answer a user's question.
|
|
@@ -17099,7 +18211,7 @@ When you are done, print the file and suggest running \`blume eval\` to try it.`
|
|
|
17099
18211
|
// src/eval/report.ts
|
|
17100
18212
|
import { mkdtemp as mkdtemp2, writeFile as writeFile12 } from "node:fs/promises";
|
|
17101
18213
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
17102
|
-
import { join as
|
|
18214
|
+
import { join as join36, relative as relative18 } from "pathe";
|
|
17103
18215
|
var ESC4 = String.fromCodePoint(27);
|
|
17104
18216
|
var COLORS3 = {
|
|
17105
18217
|
bold: `${ESC4}[1m`,
|
|
@@ -17180,15 +18292,15 @@ var summaryLine2 = (result) => {
|
|
|
17180
18292
|
var headerLine = (total, agent) => `${COLORS3.bold}blume eval${COLORS3.reset} ${total} question(s) · ${AGENTS[agent].name}`;
|
|
17181
18293
|
var startLine = (id2, index, total) => ` ${COLORS3.dim}▸ ${id2} (${index + 1}/${total})${COLORS3.reset}`;
|
|
17182
18294
|
var fixLines = (result, root) => result.diagnostics.filter((diagnostic) => diagnostic.code !== "BLUME_EVAL_ROUTE_UNKNOWN").map((finding2) => {
|
|
17183
|
-
const site = finding2.file ? `${
|
|
18295
|
+
const site = finding2.file ? `${relative18(root, finding2.file)}${finding2.line ? `:${finding2.line}` : ""}` : "";
|
|
17184
18296
|
return ` ${COLORS3.cyan}fix:${COLORS3.reset} ${site} ${COLORS3.dim}${finding2.message}${COLORS3.reset}`;
|
|
17185
18297
|
});
|
|
17186
18298
|
var warningLines = (result, root) => result.diagnostics.filter((diagnostic) => diagnostic.code === "BLUME_EVAL_ROUTE_UNKNOWN").map((finding2) => {
|
|
17187
|
-
const site = finding2.file ? ` ${
|
|
18299
|
+
const site = finding2.file ? ` ${relative18(root, finding2.file)}${finding2.line ? `:${finding2.line}` : ""}` : "";
|
|
17188
18300
|
return ` ${COLORS3.yellow}⚠${COLORS3.reset}${site} ${COLORS3.dim}${finding2.message}${COLORS3.reset}`;
|
|
17189
18301
|
});
|
|
17190
18302
|
var evalReportJson = (result, root, threshold) => {
|
|
17191
|
-
const diagnostics = result.diagnostics.map((diagnostic) => diagnostic.file ? { ...diagnostic, file:
|
|
18303
|
+
const diagnostics = result.diagnostics.map((diagnostic) => diagnostic.file ? { ...diagnostic, file: relative18(root, diagnostic.file) } : diagnostic);
|
|
17192
18304
|
return `${JSON.stringify({
|
|
17193
18305
|
diagnostics,
|
|
17194
18306
|
eval: {
|
|
@@ -17204,21 +18316,21 @@ var evalReportJson = (result, root, threshold) => {
|
|
|
17204
18316
|
`;
|
|
17205
18317
|
};
|
|
17206
18318
|
var writeEvalReport = async (result, root, threshold) => {
|
|
17207
|
-
const dir = await mkdtemp2(
|
|
17208
|
-
const path =
|
|
18319
|
+
const dir = await mkdtemp2(join36(tmpdir2(), "blume-eval-"));
|
|
18320
|
+
const path = join36(dir, "report.json");
|
|
17209
18321
|
await writeFile12(path, evalReportJson(result, root, threshold));
|
|
17210
18322
|
return path;
|
|
17211
18323
|
};
|
|
17212
18324
|
|
|
17213
18325
|
// src/eval/run.ts
|
|
17214
|
-
import { mkdir as
|
|
18326
|
+
import { mkdir as mkdir10, mkdtemp as mkdtemp3, writeFile as writeFile14 } from "node:fs/promises";
|
|
17215
18327
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
17216
|
-
import { join as
|
|
18328
|
+
import { join as join38 } from "pathe";
|
|
17217
18329
|
|
|
17218
18330
|
// src/eval/agents.ts
|
|
17219
18331
|
import { spawn as spawn2 } from "node:child_process";
|
|
17220
|
-
import { readFile as
|
|
17221
|
-
import { join as
|
|
18332
|
+
import { readFile as readFile20, writeFile as writeFile13 } from "node:fs/promises";
|
|
18333
|
+
import { join as join37 } from "pathe";
|
|
17222
18334
|
import { z as z3 } from "zod";
|
|
17223
18335
|
var KILL_GRACE_MS = 5000;
|
|
17224
18336
|
var MCP_TOOL_NAMES = [
|
|
@@ -17240,7 +18352,7 @@ var DISALLOWED_TOOLS = [
|
|
|
17240
18352
|
"WebSearch",
|
|
17241
18353
|
"Task"
|
|
17242
18354
|
];
|
|
17243
|
-
var runAgentHeadless = (bin, args, options) => new Promise((
|
|
18355
|
+
var runAgentHeadless = (bin, args, options) => new Promise((resolve11, reject) => {
|
|
17244
18356
|
const platform = options.platform ?? process.platform;
|
|
17245
18357
|
const child = spawn2(bin, args, {
|
|
17246
18358
|
cwd: options.cwd,
|
|
@@ -17269,12 +18381,12 @@ var runAgentHeadless = (bin, args, options) => new Promise((resolve9, reject) =>
|
|
|
17269
18381
|
});
|
|
17270
18382
|
child.once("close", (code) => {
|
|
17271
18383
|
clearTimeout(deadline);
|
|
17272
|
-
|
|
18384
|
+
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
17273
18385
|
});
|
|
17274
18386
|
child.once("exit", (code) => {
|
|
17275
18387
|
if (timedOut) {
|
|
17276
18388
|
clearTimeout(deadline);
|
|
17277
|
-
|
|
18389
|
+
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
17278
18390
|
}
|
|
17279
18391
|
});
|
|
17280
18392
|
child.stdin.end(options.prompt);
|
|
@@ -17284,7 +18396,7 @@ var writeMcpConfig = async (dir, snapshotPath, launcher) => {
|
|
|
17284
18396
|
args: [process.argv[1] ?? "", "mcp-stdio", "--data", snapshotPath],
|
|
17285
18397
|
command: process.execPath
|
|
17286
18398
|
};
|
|
17287
|
-
const configPath =
|
|
18399
|
+
const configPath = join37(dir, "mcp-config.json");
|
|
17288
18400
|
const config = {
|
|
17289
18401
|
mcpServers: {
|
|
17290
18402
|
[MCP_SERVER_NAME]: { args: resolved.args, command: resolved.command }
|
|
@@ -17392,17 +18504,17 @@ var readAgentOutput = async (kind, result, lastMessagePath) => {
|
|
|
17392
18504
|
text: payload.data.result
|
|
17393
18505
|
};
|
|
17394
18506
|
}
|
|
17395
|
-
let
|
|
18507
|
+
let text2;
|
|
17396
18508
|
try {
|
|
17397
|
-
const raw = await
|
|
17398
|
-
|
|
18509
|
+
const raw = await readFile20(lastMessagePath, "utf-8");
|
|
18510
|
+
text2 = raw.trim();
|
|
17399
18511
|
} catch {
|
|
17400
18512
|
return { detail: "no last message written", isError: true, text: "" };
|
|
17401
18513
|
}
|
|
17402
|
-
if (
|
|
18514
|
+
if (text2 === "") {
|
|
17403
18515
|
return { detail: "empty last message", isError: true, text: "" };
|
|
17404
18516
|
}
|
|
17405
|
-
return { isError: false, text };
|
|
18517
|
+
return { isError: false, text: text2 };
|
|
17406
18518
|
};
|
|
17407
18519
|
var verdictSchema = z3.object({
|
|
17408
18520
|
missing: z3.array(z3.string()).default([]),
|
|
@@ -17410,15 +18522,15 @@ var verdictSchema = z3.object({
|
|
|
17410
18522
|
pass: z3.boolean(),
|
|
17411
18523
|
score: z3.number().min(0).max(1).optional()
|
|
17412
18524
|
});
|
|
17413
|
-
var parseVerdict = (
|
|
17414
|
-
const start =
|
|
17415
|
-
const end =
|
|
18525
|
+
var parseVerdict = (text2) => {
|
|
18526
|
+
const start = text2.indexOf("{");
|
|
18527
|
+
const end = text2.lastIndexOf("}");
|
|
17416
18528
|
if (start === -1 || end <= start) {
|
|
17417
18529
|
return;
|
|
17418
18530
|
}
|
|
17419
18531
|
let parsed;
|
|
17420
18532
|
try {
|
|
17421
|
-
parsed = JSON.parse(
|
|
18533
|
+
parsed = JSON.parse(text2.slice(start, end + 1));
|
|
17422
18534
|
} catch {
|
|
17423
18535
|
return;
|
|
17424
18536
|
}
|
|
@@ -17427,7 +18539,7 @@ var parseVerdict = (text) => {
|
|
|
17427
18539
|
};
|
|
17428
18540
|
|
|
17429
18541
|
// src/eval/schema.ts
|
|
17430
|
-
import { readFile as
|
|
18542
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
17431
18543
|
import { load as load2 } from "js-yaml";
|
|
17432
18544
|
import { z as z4 } from "zod";
|
|
17433
18545
|
var ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/u;
|
|
@@ -17472,7 +18584,7 @@ var describeIssues = (error) => error.issues.map((issue) => {
|
|
|
17472
18584
|
var loadEvalsFile = async (path) => {
|
|
17473
18585
|
let raw;
|
|
17474
18586
|
try {
|
|
17475
|
-
raw = await
|
|
18587
|
+
raw = await readFile21(path, "utf-8");
|
|
17476
18588
|
} catch {
|
|
17477
18589
|
throw new EvalsFileError(path, `No evals file found at ${path}. Run \`blume eval init\` to draft one.`);
|
|
17478
18590
|
}
|
|
@@ -17564,9 +18676,9 @@ var errored = (question, detail, durationMs) => ({
|
|
|
17564
18676
|
var runQuestion = async (question, index, context) => {
|
|
17565
18677
|
const started = performance.now();
|
|
17566
18678
|
const elapsed = () => Math.round(performance.now() - started);
|
|
17567
|
-
const workDir =
|
|
17568
|
-
await
|
|
17569
|
-
const answerPath =
|
|
18679
|
+
const workDir = join38(context.dir, `work-${index}`);
|
|
18680
|
+
await mkdir10(workDir, { recursive: true });
|
|
18681
|
+
const answerPath = join38(workDir, "answer.txt");
|
|
17570
18682
|
const reader = await context.run(context.bin, agentArgs(context.kind, { lastMessagePath: answerPath, mcp: context.mcp }), {
|
|
17571
18683
|
cwd: workDir,
|
|
17572
18684
|
prompt: readerPrompt(question),
|
|
@@ -17579,7 +18691,7 @@ var runQuestion = async (question, index, context) => {
|
|
|
17579
18691
|
costUsd: answer.costUsd
|
|
17580
18692
|
};
|
|
17581
18693
|
}
|
|
17582
|
-
const verdictPath =
|
|
18694
|
+
const verdictPath = join38(workDir, "verdict.txt");
|
|
17583
18695
|
const judge = await context.run(context.bin, agentArgs(context.kind, { lastMessagePath: verdictPath }), {
|
|
17584
18696
|
cwd: workDir,
|
|
17585
18697
|
prompt: judgePrompt(question, answer.text),
|
|
@@ -17621,8 +18733,8 @@ var runEval = async (options) => {
|
|
|
17621
18733
|
const kind = options.agent;
|
|
17622
18734
|
const run = options.run ?? runAgentHeadless;
|
|
17623
18735
|
const anchor = { path: options.evalsPath, raw: options.rawEvals };
|
|
17624
|
-
const dir = await mkdtemp3(
|
|
17625
|
-
const snapshotPath =
|
|
18736
|
+
const dir = await mkdtemp3(join38(tmpdir3(), "blume-eval-"));
|
|
18737
|
+
const snapshotPath = join38(dir, "mcp-data.json");
|
|
17626
18738
|
await writeFile14(snapshotPath, JSON.stringify(await buildMcpData(options.project)));
|
|
17627
18739
|
const mcp = await writeMcpConfig(dir, snapshotPath);
|
|
17628
18740
|
const context = {
|
|
@@ -17760,8 +18872,8 @@ var runFixHandoff = async (agent, result, root, threshold) => {
|
|
|
17760
18872
|
}
|
|
17761
18873
|
};
|
|
17762
18874
|
var runInit = async (agent, file) => {
|
|
17763
|
-
const path =
|
|
17764
|
-
if (
|
|
18875
|
+
const path = join39(process.cwd(), file);
|
|
18876
|
+
if (existsSync22(path)) {
|
|
17765
18877
|
logger.error(`${file} already exists — edit it directly, or pass --file to draft elsewhere.`);
|
|
17766
18878
|
process.exit(1);
|
|
17767
18879
|
}
|
|
@@ -17825,7 +18937,7 @@ var evalCommand = defineCommand8({
|
|
|
17825
18937
|
let result;
|
|
17826
18938
|
try {
|
|
17827
18939
|
const project = await scanProject(root, { mode: "build" });
|
|
17828
|
-
const evalsPath =
|
|
18940
|
+
const evalsPath = join39(root, args.file);
|
|
17829
18941
|
const { evals, raw } = await loadEvalsFile(evalsPath);
|
|
17830
18942
|
process.stderr.write(`${headerLine(evals.questions.length, agent)}
|
|
17831
18943
|
|
|
@@ -17899,10 +19011,10 @@ var evalCommand = defineCommand8({
|
|
|
17899
19011
|
// src/cli/commands/init.ts
|
|
17900
19012
|
import * as clack from "@clack/prompts";
|
|
17901
19013
|
import { defineCommand as defineCommand9 } from "citty";
|
|
17902
|
-
import { resolve as
|
|
19014
|
+
import { resolve as resolve12 } from "pathe";
|
|
17903
19015
|
|
|
17904
19016
|
// src/cli/init/questions.ts
|
|
17905
|
-
import { basename as basename6, resolve as
|
|
19017
|
+
import { basename as basename6, resolve as resolve11 } from "pathe";
|
|
17906
19018
|
var cancelled = (value) => typeof value === "symbol";
|
|
17907
19019
|
var collectAnswers = async (prompter, flags, defaults) => {
|
|
17908
19020
|
const directory = flags.directory ?? await prompter.text({
|
|
@@ -17913,7 +19025,7 @@ var collectAnswers = async (prompter, flags, defaults) => {
|
|
|
17913
19025
|
if (cancelled(directory)) {
|
|
17914
19026
|
return null;
|
|
17915
19027
|
}
|
|
17916
|
-
const root =
|
|
19028
|
+
const root = resolve11(defaults.cwd, directory);
|
|
17917
19029
|
const title = await prompter.text({
|
|
17918
19030
|
initialValue: titleize(basename6(root)),
|
|
17919
19031
|
message: "What's your docs site called?",
|
|
@@ -18082,7 +19194,7 @@ var initCommand = defineCommand9({
|
|
|
18082
19194
|
title: "My Docs"
|
|
18083
19195
|
};
|
|
18084
19196
|
}
|
|
18085
|
-
const root =
|
|
19197
|
+
const root = resolve12(cwd, answers.directory);
|
|
18086
19198
|
if (validateContentDir(root, answers.contentDir) !== undefined) {
|
|
18087
19199
|
logger.error(`Invalid --content-dir "${answers.contentDir}" (must be a path inside the project).`);
|
|
18088
19200
|
process.exit(1);
|
|
@@ -18112,7 +19224,7 @@ var initCommand = defineCommand9({
|
|
|
18112
19224
|
});
|
|
18113
19225
|
|
|
18114
19226
|
// src/cli/commands/mcp-stdio.ts
|
|
18115
|
-
import { readFile as
|
|
19227
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
18116
19228
|
import { defineCommand as defineCommand10 } from "citty";
|
|
18117
19229
|
|
|
18118
19230
|
// src/ai/mcp/stdio.ts
|
|
@@ -18137,19 +19249,83 @@ var SCHEMA = {
|
|
|
18137
19249
|
title: "string"
|
|
18138
19250
|
};
|
|
18139
19251
|
var BOOST = { description: 2, title: 3 };
|
|
18140
|
-
var
|
|
18141
|
-
|
|
19252
|
+
var SEGMENTED_LANGUAGES = new Set(["ja", "ko", "th", "zh"]);
|
|
19253
|
+
var BIGRAM_LANGUAGES = new Set(["ja", "zh"]);
|
|
19254
|
+
var BIGRAM_SCRIPTS = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}々〆〇ー゙゚]+$/u;
|
|
19255
|
+
var addBigrams = (run, tokens) => {
|
|
19256
|
+
const characters = [...run];
|
|
19257
|
+
if (characters.length === 1) {
|
|
19258
|
+
tokens.add(run);
|
|
19259
|
+
return;
|
|
19260
|
+
}
|
|
19261
|
+
let previous = "";
|
|
19262
|
+
for (const character of characters) {
|
|
19263
|
+
if (previous) {
|
|
19264
|
+
tokens.add(previous + character);
|
|
19265
|
+
}
|
|
19266
|
+
previous = character;
|
|
19267
|
+
}
|
|
19268
|
+
};
|
|
19269
|
+
var segmentingTokenizer = (locale) => {
|
|
19270
|
+
const language = locale?.toLowerCase().split(/[-_]/u)[0] ?? "";
|
|
19271
|
+
if (!SEGMENTED_LANGUAGES.has(language)) {
|
|
19272
|
+
return;
|
|
19273
|
+
}
|
|
19274
|
+
if (typeof Intl.Segmenter !== "function") {
|
|
19275
|
+
return;
|
|
19276
|
+
}
|
|
19277
|
+
const segmenter = new Intl.Segmenter(language, { granularity: "word" });
|
|
19278
|
+
const bigram = BIGRAM_LANGUAGES.has(language);
|
|
19279
|
+
return {
|
|
19280
|
+
language,
|
|
19281
|
+
normalizationCache: new Map,
|
|
19282
|
+
tokenize: (raw) => {
|
|
19283
|
+
const tokens = new Set;
|
|
19284
|
+
let run = "";
|
|
19285
|
+
const flush = () => {
|
|
19286
|
+
if (run) {
|
|
19287
|
+
addBigrams(run, tokens);
|
|
19288
|
+
run = "";
|
|
19289
|
+
}
|
|
19290
|
+
};
|
|
19291
|
+
for (const segment of segmenter.segment(raw.toLowerCase())) {
|
|
19292
|
+
if (!segment.isWordLike) {
|
|
19293
|
+
flush();
|
|
19294
|
+
continue;
|
|
19295
|
+
}
|
|
19296
|
+
if (bigram && BIGRAM_SCRIPTS.test(segment.segment)) {
|
|
19297
|
+
run += segment.segment;
|
|
19298
|
+
continue;
|
|
19299
|
+
}
|
|
19300
|
+
flush();
|
|
19301
|
+
tokens.add(segment.segment);
|
|
19302
|
+
}
|
|
19303
|
+
flush();
|
|
19304
|
+
return [...tokens];
|
|
19305
|
+
}
|
|
19306
|
+
};
|
|
19307
|
+
};
|
|
19308
|
+
var buildOramaIndex = async (documents, locale) => {
|
|
19309
|
+
const tokenizer = segmentingTokenizer(locale);
|
|
19310
|
+
const db = create({
|
|
19311
|
+
schema: SCHEMA,
|
|
19312
|
+
...tokenizer ? { components: { tokenizer } } : {}
|
|
19313
|
+
});
|
|
18142
19314
|
await insertMultiple(db, documents);
|
|
18143
19315
|
return db;
|
|
18144
19316
|
};
|
|
19317
|
+
var ALL_TOKENS = 0;
|
|
18145
19318
|
var queryOramaIndex = async (db, term, limit, locale) => {
|
|
18146
|
-
const
|
|
19319
|
+
const params = {
|
|
18147
19320
|
boost: BOOST,
|
|
18148
19321
|
limit,
|
|
18149
19322
|
properties: ["title", "description", "content"],
|
|
18150
19323
|
term,
|
|
18151
19324
|
...locale ? { where: { locale: { eq: locale } } } : {}
|
|
18152
|
-
}
|
|
19325
|
+
};
|
|
19326
|
+
const bigrammed = BIGRAM_LANGUAGES.has(db.tokenizer?.language ?? "");
|
|
19327
|
+
const strict = bigrammed ? await search(db, { ...params, threshold: ALL_TOKENS }) : undefined;
|
|
19328
|
+
const found = strict && strict.hits.length > 0 ? strict : await search(db, params);
|
|
18153
19329
|
return found.hits.map((hit) => hit.document);
|
|
18154
19330
|
};
|
|
18155
19331
|
|
|
@@ -18226,14 +19402,14 @@ var excerptFor = (doc) => {
|
|
|
18226
19402
|
const head = doc.content.slice(0, EXCERPT_LENGTH).trim();
|
|
18227
19403
|
return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;
|
|
18228
19404
|
};
|
|
18229
|
-
var
|
|
19405
|
+
var text2 = (value, isError = false) => ({
|
|
18230
19406
|
content: [{ text: value, type: "text" }],
|
|
18231
19407
|
...isError ? { isError: true } : {}
|
|
18232
19408
|
});
|
|
18233
|
-
var createIndexProvider = (documents) => {
|
|
19409
|
+
var createIndexProvider = (documents, locale) => {
|
|
18234
19410
|
let dbPromise = null;
|
|
18235
|
-
return ()
|
|
18236
|
-
dbPromise ??= buildOramaIndex(documents);
|
|
19411
|
+
return function provideIndex() {
|
|
19412
|
+
dbPromise ??= buildOramaIndex(documents, locale);
|
|
18237
19413
|
return dbPromise;
|
|
18238
19414
|
};
|
|
18239
19415
|
};
|
|
@@ -18256,18 +19432,18 @@ var buildServer = (data, index) => {
|
|
|
18256
19432
|
title: doc.title,
|
|
18257
19433
|
url: urlFor(doc.route, data)
|
|
18258
19434
|
}));
|
|
18259
|
-
return
|
|
19435
|
+
return text2(JSON.stringify(results, null, 2));
|
|
18260
19436
|
}
|
|
18261
19437
|
if (name === "get_page") {
|
|
18262
19438
|
const key = normalizeRoute2(asString2(args.route), data);
|
|
18263
19439
|
const markdown = data.pages[key];
|
|
18264
19440
|
if (markdown === undefined) {
|
|
18265
|
-
return
|
|
19441
|
+
return text2(`No page found at "${key}". Use list_pages or search_docs to find valid routes.`, true);
|
|
18266
19442
|
}
|
|
18267
|
-
return
|
|
19443
|
+
return text2(markdown);
|
|
18268
19444
|
}
|
|
18269
19445
|
if (name === "list_pages") {
|
|
18270
|
-
return
|
|
19446
|
+
return text2(JSON.stringify(data.routes.map((route) => ({
|
|
18271
19447
|
contentType: route.contentType,
|
|
18272
19448
|
description: route.description,
|
|
18273
19449
|
lastModified: route.lastModified,
|
|
@@ -18277,9 +19453,9 @@ var buildServer = (data, index) => {
|
|
|
18277
19453
|
})), null, 2));
|
|
18278
19454
|
}
|
|
18279
19455
|
if (name === "get_navigation") {
|
|
18280
|
-
return
|
|
19456
|
+
return text2(JSON.stringify(data.navigation, null, 2));
|
|
18281
19457
|
}
|
|
18282
|
-
return
|
|
19458
|
+
return text2(`Unknown tool: ${name}`, true);
|
|
18283
19459
|
});
|
|
18284
19460
|
return server;
|
|
18285
19461
|
};
|
|
@@ -18289,7 +19465,7 @@ var serveMcpStdio = async (data, streams = {}) => {
|
|
|
18289
19465
|
const stdin = streams.stdin ?? process.stdin;
|
|
18290
19466
|
const stdout = streams.stdout ?? process.stdout;
|
|
18291
19467
|
const transport = new StdioServerTransport(stdin, stdout);
|
|
18292
|
-
const server = buildServer(data, createIndexProvider(data.documents));
|
|
19468
|
+
const server = buildServer(data, createIndexProvider(data.documents, data.defaultLocale));
|
|
18293
19469
|
await server.connect(transport);
|
|
18294
19470
|
await once(stdin, "end");
|
|
18295
19471
|
await transport.close();
|
|
@@ -18311,7 +19487,7 @@ var mcpStdioCommand = defineCommand10({
|
|
|
18311
19487
|
async run({ args }) {
|
|
18312
19488
|
let data;
|
|
18313
19489
|
try {
|
|
18314
|
-
data = JSON.parse(await
|
|
19490
|
+
data = JSON.parse(await readFile22(args.data, "utf-8"));
|
|
18315
19491
|
} catch (error) {
|
|
18316
19492
|
const detail = error instanceof Error ? error.message : String(error);
|
|
18317
19493
|
process.stderr.write(`blume mcp-stdio: cannot load the snapshot at ${args.data}: ${detail}
|
|
@@ -18323,10 +19499,10 @@ var mcpStdioCommand = defineCommand10({
|
|
|
18323
19499
|
});
|
|
18324
19500
|
|
|
18325
19501
|
// src/cli/commands/preview.ts
|
|
18326
|
-
import { existsSync as
|
|
19502
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
18327
19503
|
import { preview } from "astro";
|
|
18328
19504
|
import { defineCommand as defineCommand11 } from "citty";
|
|
18329
|
-
import { join as
|
|
19505
|
+
import { join as join40 } from "pathe";
|
|
18330
19506
|
var previewCommand = defineCommand11({
|
|
18331
19507
|
args: {
|
|
18332
19508
|
host: { description: "Network host to bind.", type: "string" },
|
|
@@ -18340,7 +19516,7 @@ var previewCommand = defineCommand11({
|
|
|
18340
19516
|
const root = process.cwd();
|
|
18341
19517
|
const { config } = await loadConfig(root);
|
|
18342
19518
|
const context = resolveProjectContext(root, config);
|
|
18343
|
-
if (!
|
|
19519
|
+
if (!existsSync23(join40(context.outDir, "astro.config.mjs"))) {
|
|
18344
19520
|
logger.error("No build found. Run `blume build` first.");
|
|
18345
19521
|
process.exit(1);
|
|
18346
19522
|
}
|
|
@@ -18358,7 +19534,7 @@ var previewCommand = defineCommand11({
|
|
|
18358
19534
|
// src/cli/commands/sync.ts
|
|
18359
19535
|
import { rm as rm4 } from "node:fs/promises";
|
|
18360
19536
|
import { defineCommand as defineCommand12 } from "citty";
|
|
18361
|
-
import { join as
|
|
19537
|
+
import { join as join41 } from "pathe";
|
|
18362
19538
|
var syncCommand = defineCommand12({
|
|
18363
19539
|
args: {
|
|
18364
19540
|
force: {
|
|
@@ -18380,7 +19556,7 @@ var syncCommand = defineCommand12({
|
|
|
18380
19556
|
if (args.force) {
|
|
18381
19557
|
const { config } = await loadConfig(root);
|
|
18382
19558
|
const context = resolveProjectContext(root, config);
|
|
18383
|
-
await rm4(
|
|
19559
|
+
await rm4(join41(context.outDir, "cache"), { force: true, recursive: true });
|
|
18384
19560
|
logger.info("Cleared source cache.");
|
|
18385
19561
|
}
|
|
18386
19562
|
const lock = readDevLock(resolveRuntimeDir(root));
|
|
@@ -18398,13 +19574,13 @@ var syncCommand = defineCommand12({
|
|
|
18398
19574
|
});
|
|
18399
19575
|
|
|
18400
19576
|
// src/cli/commands/validate.ts
|
|
18401
|
-
import { existsSync as
|
|
19577
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
18402
19578
|
import { defineCommand as defineCommand13 } from "citty";
|
|
18403
|
-
import { join as
|
|
19579
|
+
import { join as join43 } from "pathe";
|
|
18404
19580
|
|
|
18405
19581
|
// src/core/links.ts
|
|
18406
|
-
import { existsSync as
|
|
18407
|
-
import { basename as basename7, join as
|
|
19582
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
19583
|
+
import { basename as basename7, join as join42 } from "pathe";
|
|
18408
19584
|
var HTTP = /^https?:\/\//iu;
|
|
18409
19585
|
var PROTOCOL_RELATIVE = /^\/\//u;
|
|
18410
19586
|
var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
|
|
@@ -18417,7 +19593,7 @@ var decodePercent = (value) => {
|
|
|
18417
19593
|
};
|
|
18418
19594
|
var DOC_EXT = /\.(?:md|mdx)$/iu;
|
|
18419
19595
|
var FILE_EXT = /\.[a-z0-9]+$/iu;
|
|
18420
|
-
var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null &&
|
|
19596
|
+
var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null && existsSync24(join42(ctx.publicDir, resolved));
|
|
18421
19597
|
var NUMERIC_PREFIX3 = /^\d+[-_.]/u;
|
|
18422
19598
|
var isIndexPage = (page2) => /^index\.(?:md|mdx)$/iu.test(basename7(page2.navPath).replace(NUMERIC_PREFIX3, ""));
|
|
18423
19599
|
var applyRelativePart = (segments, part) => {
|
|
@@ -18628,12 +19804,12 @@ var validateCommand = defineCommand13({
|
|
|
18628
19804
|
});
|
|
18629
19805
|
extraRoutes.push(...manifest.routes.flatMap((route) => route.fallback ? [route.path] : []));
|
|
18630
19806
|
}
|
|
18631
|
-
const publicDir =
|
|
19807
|
+
const publicDir = join43(root, "public");
|
|
18632
19808
|
diagnostics.push(...await validateLinks(project.graph, {
|
|
18633
19809
|
basePath: project.config.basePath,
|
|
18634
19810
|
checkExternal: Boolean(args.external),
|
|
18635
19811
|
extraRoutes,
|
|
18636
|
-
publicDir:
|
|
19812
|
+
publicDir: existsSync25(publicDir) ? publicDir : null,
|
|
18637
19813
|
redirects: project.config.redirects
|
|
18638
19814
|
}));
|
|
18639
19815
|
} catch (error) {
|
|
@@ -18697,5 +19873,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
18697
19873
|
});
|
|
18698
19874
|
runMain(main);
|
|
18699
19875
|
|
|
18700
|
-
//# debugId=
|
|
19876
|
+
//# debugId=CCE85D85BA5F467464756E2164756E21
|
|
18701
19877
|
//# sourceMappingURL=index.js.map
|