blume 1.2.1 → 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 +37 -0
- package/dist/cli/index.js +1680 -534
- package/dist/cli/index.js.map +37 -28
- package/dist/types/core/config-input.d.ts +131 -11
- package/dist/types/core/config.d.ts +9 -1
- package/dist/types/core/data.d.ts +24 -5
- package/dist/types/core/i18n-ui.d.ts +58 -799
- package/dist/types/core/schema.d.ts +534 -3305
- package/dist/types/theme/fonts.d.ts +55 -11
- package/docs/02-deployment.mdx +2 -0
- package/docs/07-faq.mdx +14 -14
- package/docs/advanced/skills.mdx +2 -2
- package/docs/configuration/ai.mdx +126 -2
- package/docs/configuration/index.mdx +19 -1
- package/docs/configuration/search.mdx +2 -0
- package/docs/configuration/seo.mdx +26 -3
- package/docs/configuration/theming.mdx +44 -2
- package/docs/content/syntax.mdx +18 -2
- package/docs/reference/cli.mdx +3 -3
- package/package.json +9 -8
- package/skills/blume/SKILL.md +6 -4
- package/skills/blume-migrate/SKILL.md +5 -3
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/monorepo.md +2 -1
- package/src/ai/agent-readability.ts +31 -1
- package/src/ai/api-catalog.ts +81 -0
- package/src/ai/link-headers.ts +52 -0
- package/src/ai/llms.ts +12 -1
- package/src/ai/markdown.ts +15 -2
- package/src/ai/mcp/discovery.ts +70 -15
- package/src/ai/mcp/server.ts +5 -4
- package/src/ai/skills.ts +193 -0
- package/src/ai/tar.ts +104 -0
- package/src/ai/web-bot-auth.ts +30 -0
- package/src/astro/generate.ts +116 -6
- package/src/astro/integration.ts +52 -14
- package/src/astro/templates.ts +176 -33
- 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/PageLayout.astro +2 -0
- package/src/components/layout/ReferenceLayout.astro +2 -0
- package/src/components/layout/RootLayout.astro +62 -10
- package/src/components/layout/Search.astro +2 -2
- package/src/components/layout/WebMcp.astro +49 -0
- package/src/core/config-input.ts +143 -11
- package/src/core/config.ts +17 -1
- package/src/core/content-assets.ts +199 -0
- package/src/core/data.ts +21 -5
- package/src/core/diagnostics.ts +6 -5
- package/src/core/i18n-ui.ts +19 -28
- package/src/core/project-graph.ts +6 -0
- package/src/core/schema.ts +224 -71
- package/src/core/sources/normalize.ts +5 -5
- package/src/deploy/headers.ts +45 -3
- package/src/deploy/vercel-negotiation.ts +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 +98 -4
- 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}
|
|
@@ -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
|
}
|
|
@@ -11740,11 +12155,116 @@ var buildAgentReadability = (project) => {
|
|
|
11740
12155
|
if (config.github) {
|
|
11741
12156
|
manifest.repository = `https://github.com/${config.github.owner}/${config.github.repo}`;
|
|
11742
12157
|
}
|
|
11743
|
-
return manifest;
|
|
12158
|
+
return manifest;
|
|
12159
|
+
};
|
|
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;
|
|
11744
12264
|
};
|
|
11745
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
|
|
@@ -12797,7 +13623,7 @@ var buildAskData = async (project) => {
|
|
|
12797
13623
|
};
|
|
12798
13624
|
|
|
12799
13625
|
// src/ai/markdown.ts
|
|
12800
|
-
import { readFile as
|
|
13626
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
12801
13627
|
var agentMarkdown = (entry) => entry.md ?? entry.mdx;
|
|
12802
13628
|
var buildRawMarkdown = async (project) => {
|
|
12803
13629
|
const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
|
|
@@ -12810,10 +13636,19 @@ var buildRawMarkdown = async (project) => {
|
|
|
12810
13636
|
if (page) {
|
|
12811
13637
|
return await readEntryText(project, page);
|
|
12812
13638
|
}
|
|
12813
|
-
return route.sourcePath ? await
|
|
13639
|
+
return route.sourcePath ? await readFile12(route.sourcePath, "utf-8") : "";
|
|
12814
13640
|
};
|
|
12815
13641
|
const entries = await Promise.all(project.manifest.routes.map(async (route) => {
|
|
12816
|
-
|
|
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);
|
|
12817
13652
|
const md = downlevelComponents(source, components, frontmatter_default(source).data);
|
|
12818
13653
|
const entry = md === source ? { mdx: source } : { md, mdx: source };
|
|
12819
13654
|
return [route.path, entry];
|
|
@@ -12912,19 +13747,42 @@ var buildMcpDiscovery = (input) => ({
|
|
|
12912
13747
|
}
|
|
12913
13748
|
]
|
|
12914
13749
|
});
|
|
12915
|
-
var
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
|
|
12920
|
-
|
|
12921
|
-
|
|
12922
|
-
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
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
|
+
};
|
|
12928
13786
|
|
|
12929
13787
|
// src/core/builtin-tags.ts
|
|
12930
13788
|
var BUILTIN_MDX_TAGS = new Set([
|
|
@@ -12991,8 +13849,8 @@ var validateUsedComponents = (pages, extraTags, registryNames) => {
|
|
|
12991
13849
|
};
|
|
12992
13850
|
|
|
12993
13851
|
// src/core/component-overrides.ts
|
|
12994
|
-
import { existsSync as
|
|
12995
|
-
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";
|
|
12996
13854
|
import ts from "typescript";
|
|
12997
13855
|
var GROUPS = ["mdx", "layout", "islands"];
|
|
12998
13856
|
var GROUP_SET = new Set(GROUPS);
|
|
@@ -13084,24 +13942,24 @@ var findDefaultExportObject = (sourceFile) => {
|
|
|
13084
13942
|
var probeExtension = (base) => {
|
|
13085
13943
|
for (const extension of COMPONENT_EXTS) {
|
|
13086
13944
|
const candidate = `${base}.${extension}`;
|
|
13087
|
-
if (
|
|
13945
|
+
if (existsSync12(candidate)) {
|
|
13088
13946
|
return candidate;
|
|
13089
13947
|
}
|
|
13090
13948
|
}
|
|
13091
13949
|
return null;
|
|
13092
13950
|
};
|
|
13093
13951
|
var toImport = (specifier, imported, dir) => {
|
|
13094
|
-
const
|
|
13952
|
+
const relative13 = specifier.startsWith(".") || isAbsolute6(specifier);
|
|
13095
13953
|
let path = specifier;
|
|
13096
|
-
let extension =
|
|
13097
|
-
if (
|
|
13098
|
-
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);
|
|
13099
13957
|
if (extension) {
|
|
13100
13958
|
path = absolute;
|
|
13101
13959
|
} else {
|
|
13102
13960
|
const probed = probeExtension(absolute);
|
|
13103
13961
|
path = probed ?? absolute;
|
|
13104
|
-
extension = probed ?
|
|
13962
|
+
extension = probed ? extname7(probed).slice(1).toLowerCase() : "";
|
|
13105
13963
|
}
|
|
13106
13964
|
}
|
|
13107
13965
|
return {
|
|
@@ -13250,7 +14108,7 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
13250
14108
|
return result;
|
|
13251
14109
|
}
|
|
13252
14110
|
const imports = collectImports(sourceFile);
|
|
13253
|
-
const dir =
|
|
14111
|
+
const dir = dirname8(filePath);
|
|
13254
14112
|
for (const property of object.properties) {
|
|
13255
14113
|
collectGroupOverrides(property, imports, dir, result);
|
|
13256
14114
|
}
|
|
@@ -13258,16 +14116,16 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
13258
14116
|
};
|
|
13259
14117
|
|
|
13260
14118
|
// src/core/tsconfig-aliases.ts
|
|
13261
|
-
import { existsSync as
|
|
14119
|
+
import { existsSync as existsSync13, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
13262
14120
|
import { createRequire as createRequire3 } from "node:module";
|
|
13263
14121
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
13264
|
-
import { dirname as
|
|
13265
|
-
var scanJsonChar = (
|
|
13266
|
-
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];
|
|
13267
14125
|
if (inString) {
|
|
13268
14126
|
if (char === "\\") {
|
|
13269
14127
|
return {
|
|
13270
|
-
append: char + (
|
|
14128
|
+
append: char + (text2[index + 1] ?? ""),
|
|
13271
14129
|
inString: true,
|
|
13272
14130
|
next: index + 2
|
|
13273
14131
|
};
|
|
@@ -13277,35 +14135,35 @@ var scanJsonChar = (text, index, inString) => {
|
|
|
13277
14135
|
if (char === '"') {
|
|
13278
14136
|
return { append: char, inString: true, next: index + 1 };
|
|
13279
14137
|
}
|
|
13280
|
-
if (char === "/" &&
|
|
13281
|
-
const newline =
|
|
14138
|
+
if (char === "/" && text2[index + 1] === "/") {
|
|
14139
|
+
const newline = text2.indexOf(`
|
|
13282
14140
|
`, index + 2);
|
|
13283
14141
|
return {
|
|
13284
14142
|
append: "",
|
|
13285
14143
|
inString: false,
|
|
13286
|
-
next: newline === -1 ?
|
|
14144
|
+
next: newline === -1 ? text2.length : newline
|
|
13287
14145
|
};
|
|
13288
14146
|
}
|
|
13289
|
-
if (char === "/" &&
|
|
13290
|
-
const end =
|
|
14147
|
+
if (char === "/" && text2[index + 1] === "*") {
|
|
14148
|
+
const end = text2.indexOf("*/", index + 2);
|
|
13291
14149
|
return {
|
|
13292
14150
|
append: "",
|
|
13293
14151
|
inString: false,
|
|
13294
|
-
next: end === -1 ?
|
|
14152
|
+
next: end === -1 ? text2.length : end + 2
|
|
13295
14153
|
};
|
|
13296
14154
|
}
|
|
13297
14155
|
return { append: char ?? "", inString: false, next: index + 1 };
|
|
13298
14156
|
};
|
|
13299
|
-
var stripJsonComments = (
|
|
14157
|
+
var stripJsonComments = (text2) => {
|
|
13300
14158
|
let out = "";
|
|
13301
14159
|
let inString = false;
|
|
13302
14160
|
let index = 0;
|
|
13303
|
-
while (index <
|
|
14161
|
+
while (index < text2.length) {
|
|
13304
14162
|
const {
|
|
13305
14163
|
append,
|
|
13306
14164
|
inString: nextInString,
|
|
13307
14165
|
next
|
|
13308
|
-
} = scanJsonChar(
|
|
14166
|
+
} = scanJsonChar(text2, index, inString);
|
|
13309
14167
|
out += append;
|
|
13310
14168
|
inString = nextInString;
|
|
13311
14169
|
index = next;
|
|
@@ -13313,9 +14171,9 @@ var stripJsonComments = (text) => {
|
|
|
13313
14171
|
return out;
|
|
13314
14172
|
};
|
|
13315
14173
|
var TRAILING_COMMA = /,(?<rest>\s*[}\]])/gu;
|
|
13316
|
-
var parseJsonc = (
|
|
14174
|
+
var parseJsonc = (text2) => {
|
|
13317
14175
|
try {
|
|
13318
|
-
const cleaned = stripJsonComments(
|
|
14176
|
+
const cleaned = stripJsonComments(text2).replaceAll(TRAILING_COMMA, "$<rest>");
|
|
13319
14177
|
const value = JSON.parse(cleaned);
|
|
13320
14178
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
13321
14179
|
} catch {
|
|
@@ -13331,15 +14189,15 @@ var isFile = (path) => {
|
|
|
13331
14189
|
};
|
|
13332
14190
|
var resolveExtends = (spec, fromDir) => {
|
|
13333
14191
|
if (spec.startsWith(".") || isAbsolute7(spec)) {
|
|
13334
|
-
const candidates = spec.endsWith(".json") ? [
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
14192
|
+
const candidates = spec.endsWith(".json") ? [resolve7(fromDir, spec)] : [
|
|
14193
|
+
resolve7(fromDir, `${spec}.json`),
|
|
14194
|
+
resolve7(fromDir, spec, "tsconfig.json"),
|
|
14195
|
+
resolve7(fromDir, spec)
|
|
13338
14196
|
];
|
|
13339
14197
|
return candidates.find(isFile) ?? null;
|
|
13340
14198
|
}
|
|
13341
14199
|
try {
|
|
13342
|
-
const requireFromDir = createRequire3(pathToFileURL2(
|
|
14200
|
+
const requireFromDir = createRequire3(pathToFileURL2(join22(fromDir, "_.js")).href);
|
|
13343
14201
|
for (const sub of [`${spec}/tsconfig.json`, spec]) {
|
|
13344
14202
|
try {
|
|
13345
14203
|
return requireFromDir.resolve(sub);
|
|
@@ -13349,7 +14207,7 @@ var resolveExtends = (spec, fromDir) => {
|
|
|
13349
14207
|
return null;
|
|
13350
14208
|
};
|
|
13351
14209
|
var loadPaths = (file, seen) => {
|
|
13352
|
-
if (seen.has(file) || !
|
|
14210
|
+
if (seen.has(file) || !existsSync13(file)) {
|
|
13353
14211
|
return null;
|
|
13354
14212
|
}
|
|
13355
14213
|
seen.add(file);
|
|
@@ -13361,7 +14219,7 @@ var loadPaths = (file, seen) => {
|
|
|
13361
14219
|
if (options.paths && typeof options.paths === "object") {
|
|
13362
14220
|
const baseUrl = typeof options.baseUrl === "string" ? options.baseUrl : ".";
|
|
13363
14221
|
return {
|
|
13364
|
-
baseDir:
|
|
14222
|
+
baseDir: resolve7(dirname9(file), baseUrl),
|
|
13365
14223
|
paths: options.paths
|
|
13366
14224
|
};
|
|
13367
14225
|
}
|
|
@@ -13370,7 +14228,7 @@ var loadPaths = (file, seen) => {
|
|
|
13370
14228
|
if (typeof base !== "string") {
|
|
13371
14229
|
continue;
|
|
13372
14230
|
}
|
|
13373
|
-
const resolved = resolveExtends(base,
|
|
14231
|
+
const resolved = resolveExtends(base, dirname9(file));
|
|
13374
14232
|
const found = resolved ? loadPaths(resolved, seen) : null;
|
|
13375
14233
|
if (found) {
|
|
13376
14234
|
return found;
|
|
@@ -13388,10 +14246,10 @@ var toAlias = (key, value, baseDir) => {
|
|
|
13388
14246
|
if (find === "" || find === "*") {
|
|
13389
14247
|
return null;
|
|
13390
14248
|
}
|
|
13391
|
-
return { find, replacement:
|
|
14249
|
+
return { find, replacement: resolve7(baseDir, target) };
|
|
13392
14250
|
};
|
|
13393
14251
|
var resolveTsconfigAliases = (root) => {
|
|
13394
|
-
const entry = ["tsconfig.json", "jsconfig.json"].map((name) =>
|
|
14252
|
+
const entry = ["tsconfig.json", "jsconfig.json"].map((name) => join22(root, name)).find((file) => existsSync13(file));
|
|
13395
14253
|
if (!entry) {
|
|
13396
14254
|
return {};
|
|
13397
14255
|
}
|
|
@@ -13409,24 +14267,126 @@ var resolveTsconfigAliases = (root) => {
|
|
|
13409
14267
|
return aliases;
|
|
13410
14268
|
};
|
|
13411
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
|
+
|
|
13412
14372
|
// src/og/logo.ts
|
|
13413
|
-
import { existsSync as
|
|
13414
|
-
import { join as
|
|
14373
|
+
import { existsSync as existsSync15, readFileSync as readFileSync7 } from "node:fs";
|
|
14374
|
+
import { join as join24 } from "pathe";
|
|
13415
14375
|
var resolveOgLogo = (project, source) => {
|
|
13416
14376
|
if (!source?.toLowerCase().endsWith(".svg")) {
|
|
13417
14377
|
return;
|
|
13418
14378
|
}
|
|
13419
|
-
const
|
|
14379
|
+
const relative13 = source.replace(/^\//u, "");
|
|
13420
14380
|
const file = [
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
].find((path) =>
|
|
14381
|
+
join24(project.context.root, "public", relative13),
|
|
14382
|
+
join24(project.context.root, relative13)
|
|
14383
|
+
].find((path) => existsSync15(path));
|
|
13424
14384
|
return file ? readFileSync7(file, "utf-8") : undefined;
|
|
13425
14385
|
};
|
|
13426
14386
|
|
|
13427
14387
|
// src/openapi/scalar.ts
|
|
13428
|
-
import { readFile as
|
|
13429
|
-
import { isAbsolute as
|
|
14388
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
14389
|
+
import { isAbsolute as isAbsolute9, join as join25 } from "pathe";
|
|
13430
14390
|
|
|
13431
14391
|
// src/theme/palette.ts
|
|
13432
14392
|
var FALLBACK_ACCENT = "oklch(0.62 0.16 250)";
|
|
@@ -13532,9 +14492,9 @@ var specConfiguration = async (spec, root) => {
|
|
|
13532
14492
|
if (URL_SPEC2.test(spec)) {
|
|
13533
14493
|
return { config: { url: spec } };
|
|
13534
14494
|
}
|
|
13535
|
-
const absolute =
|
|
14495
|
+
const absolute = isAbsolute9(spec) ? spec : join25(root, spec);
|
|
13536
14496
|
try {
|
|
13537
|
-
return { config: { content: await
|
|
14497
|
+
return { config: { content: await readFile13(absolute, "utf-8") } };
|
|
13538
14498
|
} catch {
|
|
13539
14499
|
return {
|
|
13540
14500
|
config: { url: spec },
|
|
@@ -14021,19 +14981,29 @@ blume-diff {
|
|
|
14021
14981
|
even though its chrome wrapper is not-prose. */
|
|
14022
14982
|
.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
14023
14983
|
display: block;
|
|
14024
|
-
|
|
14025
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
|
|
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;
|
|
14030
14998
|
}
|
|
14031
14999
|
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
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;
|
|
14037
15007
|
}
|
|
14038
15008
|
|
|
14039
15009
|
/* Word wrap (markdown.code.wrap): long lines wrap instead of scrolling. The
|
|
@@ -14101,6 +15071,10 @@ pre.blume-source {
|
|
|
14101
15071
|
|
|
14102
15072
|
pre.blume-source > code {
|
|
14103
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;
|
|
14104
15078
|
min-height: 0;
|
|
14105
15079
|
overflow: auto;
|
|
14106
15080
|
}
|
|
@@ -14282,6 +15256,13 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
14282
15256
|
display: none !important;
|
|
14283
15257
|
}
|
|
14284
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
|
+
|
|
14285
15266
|
#blume-content {
|
|
14286
15267
|
padding: 0 !important;
|
|
14287
15268
|
}
|
|
@@ -14525,13 +15506,13 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
|
|
|
14525
15506
|
};
|
|
14526
15507
|
|
|
14527
15508
|
// src/astro/examples.ts
|
|
14528
|
-
import { readFile as
|
|
14529
|
-
import { join as
|
|
15509
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
15510
|
+
import { join as join27, relative as relative13 } from "pathe";
|
|
14530
15511
|
import { glob as glob6 } from "tinyglobby";
|
|
14531
15512
|
|
|
14532
15513
|
// src/astro/islands.ts
|
|
14533
|
-
import { readFile as
|
|
14534
|
-
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";
|
|
14535
15516
|
import { glob as glob5 } from "tinyglobby";
|
|
14536
15517
|
var DEFAULT_CLIENT = "visible";
|
|
14537
15518
|
var VALID_MODES = new Set([
|
|
@@ -14560,14 +15541,14 @@ var readClientMode = (source, file, warnings) => {
|
|
|
14560
15541
|
return mode;
|
|
14561
15542
|
};
|
|
14562
15543
|
var discoverIslands = async (root) => {
|
|
14563
|
-
const dir =
|
|
15544
|
+
const dir = join26(root, "islands");
|
|
14564
15545
|
const matches2 = await glob5(["**/*.{jsx,svelte,tsx,vue}"], {
|
|
14565
15546
|
absolute: true,
|
|
14566
15547
|
cwd: dir,
|
|
14567
15548
|
onlyFiles: true
|
|
14568
15549
|
});
|
|
14569
15550
|
const files = matches2.toSorted();
|
|
14570
|
-
const sources = await Promise.all(files.map((file) =>
|
|
15551
|
+
const sources = await Promise.all(files.map((file) => readFile14(file, "utf-8")));
|
|
14571
15552
|
const islands = [];
|
|
14572
15553
|
const warnings = [];
|
|
14573
15554
|
const seen = new Map;
|
|
@@ -14623,14 +15604,14 @@ var splitGlobBase = (pattern) => {
|
|
|
14623
15604
|
};
|
|
14624
15605
|
var discoverExamples = async (root, pattern = "examples") => {
|
|
14625
15606
|
const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
|
|
14626
|
-
const dir =
|
|
15607
|
+
const dir = join27(root, base);
|
|
14627
15608
|
const matches2 = await glob6([rest], {
|
|
14628
15609
|
absolute: true,
|
|
14629
15610
|
cwd: dir,
|
|
14630
15611
|
onlyFiles: true
|
|
14631
15612
|
});
|
|
14632
15613
|
const files = matches2.toSorted();
|
|
14633
|
-
const sources = await Promise.all(files.map((file) =>
|
|
15614
|
+
const sources = await Promise.all(files.map((file) => readFile15(file, "utf-8")));
|
|
14634
15615
|
const examples = [];
|
|
14635
15616
|
const warnings = [];
|
|
14636
15617
|
const seen = new Map;
|
|
@@ -14640,7 +15621,7 @@ var discoverExamples = async (root, pattern = "examples") => {
|
|
|
14640
15621
|
if (!(ext && framework)) {
|
|
14641
15622
|
return;
|
|
14642
15623
|
}
|
|
14643
|
-
const path =
|
|
15624
|
+
const path = relative13(dir, file).slice(0, -(ext.length + 1));
|
|
14644
15625
|
const existing = seen.get(path);
|
|
14645
15626
|
if (existing) {
|
|
14646
15627
|
warnings.push(`Two examples both resolve to "${path}" ("${existing}" and "${file}"); ignoring the second. Give them distinct paths.`);
|
|
@@ -14667,10 +15648,10 @@ var exampleMarkdownLookup = (examples) => Object.fromEntries(examples.map((examp
|
|
|
14667
15648
|
]));
|
|
14668
15649
|
|
|
14669
15650
|
// src/astro/generate.ts
|
|
14670
|
-
var BLUME_SRC =
|
|
15651
|
+
var BLUME_SRC = join28(packageRoot(), "src");
|
|
14671
15652
|
var canResolveFrom = (fromDir, spec) => {
|
|
14672
15653
|
try {
|
|
14673
|
-
createRequire5(pathToFileURL3(
|
|
15654
|
+
createRequire5(pathToFileURL3(join28(fromDir, "_.js")).href).resolve(spec);
|
|
14674
15655
|
return true;
|
|
14675
15656
|
} catch {
|
|
14676
15657
|
return false;
|
|
@@ -14681,7 +15662,7 @@ var resolveReactCompiler = (config, needsReact, pkgDir = packageRoot()) => {
|
|
|
14681
15662
|
return null;
|
|
14682
15663
|
}
|
|
14683
15664
|
try {
|
|
14684
|
-
return createRequire5(pathToFileURL3(
|
|
15665
|
+
return createRequire5(pathToFileURL3(join28(pkgDir, "_.js")).href).resolve("babel-plugin-react-compiler");
|
|
14685
15666
|
} catch {
|
|
14686
15667
|
return null;
|
|
14687
15668
|
}
|
|
@@ -14691,7 +15672,7 @@ var reactCompilerWarnings = (config, needsReact, compilerPath) => needsReact &&
|
|
|
14691
15672
|
] : [];
|
|
14692
15673
|
var resolveAstroPackageJson = (modulesDir) => {
|
|
14693
15674
|
try {
|
|
14694
|
-
return realpathSync(
|
|
15675
|
+
return realpathSync(join28(modulesDir, "astro", "package.json"));
|
|
14695
15676
|
} catch {
|
|
14696
15677
|
return null;
|
|
14697
15678
|
}
|
|
@@ -14699,12 +15680,12 @@ var resolveAstroPackageJson = (modulesDir) => {
|
|
|
14699
15680
|
var resolvedAstroHit = (fromDir) => {
|
|
14700
15681
|
let dir = normalize3(fromDir);
|
|
14701
15682
|
while (true) {
|
|
14702
|
-
const modulesDir =
|
|
15683
|
+
const modulesDir = join28(dir, "node_modules");
|
|
14703
15684
|
const pkg = resolveAstroPackageJson(modulesDir);
|
|
14704
15685
|
if (pkg) {
|
|
14705
15686
|
return { modulesDir, pkg };
|
|
14706
15687
|
}
|
|
14707
|
-
const parent =
|
|
15688
|
+
const parent = dirname10(dir);
|
|
14708
15689
|
if (parent === dir) {
|
|
14709
15690
|
return null;
|
|
14710
15691
|
}
|
|
@@ -14719,10 +15700,10 @@ var sameRealDir = (a, b) => {
|
|
|
14719
15700
|
}
|
|
14720
15701
|
};
|
|
14721
15702
|
var depsCandidates = (pkgDir) => [
|
|
14722
|
-
|
|
14723
|
-
|
|
15703
|
+
join28(pkgDir, "node_modules"),
|
|
15704
|
+
dirname10(pkgDir)
|
|
14724
15705
|
];
|
|
14725
|
-
var candidateHolding = (pkgDir, ...segments) => depsCandidates(pkgDir).find((dir) =>
|
|
15706
|
+
var candidateHolding = (pkgDir, ...segments) => depsCandidates(pkgDir).find((dir) => existsSync16(join28(dir, ...segments))) ?? null;
|
|
14726
15707
|
var linkDepsJunction = async (link, depsDir) => {
|
|
14727
15708
|
let existing;
|
|
14728
15709
|
try {
|
|
@@ -14735,13 +15716,13 @@ var linkDepsJunction = async (link, depsDir) => {
|
|
|
14735
15716
|
return;
|
|
14736
15717
|
}
|
|
14737
15718
|
try {
|
|
14738
|
-
if (
|
|
15719
|
+
if (resolve8(dirname10(link), await readlink(link)) === resolve8(depsDir)) {
|
|
14739
15720
|
return;
|
|
14740
15721
|
}
|
|
14741
15722
|
} catch {}
|
|
14742
15723
|
await rm2(link, { force: true });
|
|
14743
15724
|
}
|
|
14744
|
-
await mkdir6(
|
|
15725
|
+
await mkdir6(dirname10(link), { recursive: true });
|
|
14745
15726
|
await symlink(depsDir, link, "junction");
|
|
14746
15727
|
};
|
|
14747
15728
|
var readPkgVersion = (pkgJsonPath) => {
|
|
@@ -14774,7 +15755,7 @@ var dropStaleDepsLink = async (link, pkgDir) => {
|
|
|
14774
15755
|
let linkedBlume;
|
|
14775
15756
|
let runningBlume;
|
|
14776
15757
|
try {
|
|
14777
|
-
linkedBlume = await realpath(
|
|
15758
|
+
linkedBlume = await realpath(join28(link, "blume"));
|
|
14778
15759
|
runningBlume = await realpath(pkgDir);
|
|
14779
15760
|
} catch {
|
|
14780
15761
|
return;
|
|
@@ -14790,7 +15771,7 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
|
|
|
14790
15771
|
}
|
|
14791
15772
|
const mdxDir = candidateHolding(pkgDir, "@astrojs", "mdx");
|
|
14792
15773
|
const blumeAstro = resolveAstroPackageJson(astroDir);
|
|
14793
|
-
await dropStaleDepsLink(
|
|
15774
|
+
await dropStaleDepsLink(join28(outDir, "node_modules"), pkgDir);
|
|
14794
15775
|
const outDirHit = resolvedAstroHit(outDir);
|
|
14795
15776
|
const astroCorrect = blumeAstro !== null && outDirHit?.pkg === blumeAstro;
|
|
14796
15777
|
const walkLandsInDeps = astroCorrect && outDirHit !== null && sameRealDir(outDirHit.modulesDir, astroDir);
|
|
@@ -14798,7 +15779,7 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
|
|
|
14798
15779
|
return null;
|
|
14799
15780
|
}
|
|
14800
15781
|
if (mdxDir && (mdxDir === astroDir || astroCorrect)) {
|
|
14801
|
-
await linkDepsJunction(
|
|
15782
|
+
await linkDepsJunction(join28(outDir, "node_modules"), mdxDir);
|
|
14802
15783
|
return null;
|
|
14803
15784
|
}
|
|
14804
15785
|
return astroConflictWarning(blumeAstro, outDirHit?.pkg ?? null);
|
|
@@ -14839,14 +15820,14 @@ var searchProviderWarnings = (provider, root, pkgDir = packageRoot()) => {
|
|
|
14839
15820
|
}
|
|
14840
15821
|
return warnings;
|
|
14841
15822
|
};
|
|
14842
|
-
var examplesCssFile = (root, config) => config.examples.css ?
|
|
15823
|
+
var examplesCssFile = (root, config) => config.examples.css ? join28(root, config.examples.css) : null;
|
|
14843
15824
|
var writeExamplesPreview = async (options) => {
|
|
14844
15825
|
const { config, hasExamples, root, srcDir, write } = options;
|
|
14845
15826
|
if (hasExamples) {
|
|
14846
|
-
await write(
|
|
15827
|
+
await write(join28(srcDir, "pages", ...config.basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro"), examplesPageTemplate());
|
|
14847
15828
|
}
|
|
14848
15829
|
const cssFile = examplesCssFile(root, config);
|
|
14849
|
-
return cssFile && !
|
|
15830
|
+
return cssFile && !existsSync16(cssFile) ? [
|
|
14850
15831
|
`examples.css points at "${config.examples.css}", which doesn't exist; previews render without it.`
|
|
14851
15832
|
] : [];
|
|
14852
15833
|
};
|
|
@@ -14855,7 +15836,7 @@ var readOptional = async (path) => {
|
|
|
14855
15836
|
return "";
|
|
14856
15837
|
}
|
|
14857
15838
|
try {
|
|
14858
|
-
return await
|
|
15839
|
+
return await readFile16(path, "utf-8");
|
|
14859
15840
|
} catch {
|
|
14860
15841
|
return "";
|
|
14861
15842
|
}
|
|
@@ -14875,30 +15856,30 @@ var detectUsesMath = async (root, staged = []) => {
|
|
|
14875
15856
|
ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
|
|
14876
15857
|
onlyFiles: true
|
|
14877
15858
|
});
|
|
14878
|
-
const contents = await Promise.all(files.map((file) => readOptional(
|
|
15859
|
+
const contents = await Promise.all(files.map((file) => readOptional(join28(root, file))));
|
|
14879
15860
|
return [...contents, ...staged].some(containsMath);
|
|
14880
15861
|
};
|
|
14881
|
-
var hashConfigSource = (source) =>
|
|
15862
|
+
var hashConfigSource = (source) => createHash3("sha256").update(source).digest("hex");
|
|
14882
15863
|
var loadIntegrationBridge = async (config, context) => {
|
|
14883
15864
|
if (config.integrations.length === 0 || !context.configFile) {
|
|
14884
15865
|
return;
|
|
14885
15866
|
}
|
|
14886
15867
|
return {
|
|
14887
|
-
configFile:
|
|
15868
|
+
configFile: relative14(context.outDir, context.configFile),
|
|
14888
15869
|
sourceHash: hashConfigSource(await readOptional(context.configFile))
|
|
14889
15870
|
};
|
|
14890
15871
|
};
|
|
14891
15872
|
var writeIfChanged = async (path, content) => {
|
|
14892
15873
|
let existing = null;
|
|
14893
15874
|
try {
|
|
14894
|
-
existing = await
|
|
15875
|
+
existing = await readFile16(path, "utf-8");
|
|
14895
15876
|
} catch {
|
|
14896
15877
|
existing = null;
|
|
14897
15878
|
}
|
|
14898
15879
|
if (existing === content) {
|
|
14899
15880
|
return false;
|
|
14900
15881
|
}
|
|
14901
|
-
await mkdir6(
|
|
15882
|
+
await mkdir6(dirname10(path), { recursive: true });
|
|
14902
15883
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
14903
15884
|
await writeFile7(tmp, content, "utf-8");
|
|
14904
15885
|
try {
|
|
@@ -14936,12 +15917,12 @@ var collectStaged = (project) => {
|
|
|
14936
15917
|
var writeStagedContent = async (out, staged) => {
|
|
14937
15918
|
const contentDir = stagedContentDir(out);
|
|
14938
15919
|
const written = new Set;
|
|
14939
|
-
await Promise.all([...staged].map(async ([entryId,
|
|
14940
|
-
const path =
|
|
15920
|
+
await Promise.all([...staged].map(async ([entryId, text2]) => {
|
|
15921
|
+
const path = join28(contentDir, entryId);
|
|
14941
15922
|
written.add(normalize3(path));
|
|
14942
|
-
await writeIfChanged(path,
|
|
15923
|
+
await writeIfChanged(path, text2);
|
|
14943
15924
|
}));
|
|
14944
|
-
if (
|
|
15925
|
+
if (existsSync16(contentDir)) {
|
|
14945
15926
|
await pruneOrphans(contentDir, written);
|
|
14946
15927
|
}
|
|
14947
15928
|
};
|
|
@@ -14972,9 +15953,9 @@ var readLogoSvg = (project, source) => {
|
|
|
14972
15953
|
}
|
|
14973
15954
|
const rel = source.replace(/^\//u, "");
|
|
14974
15955
|
const file = [
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
].find((path) =>
|
|
15956
|
+
join28(project.context.root, "public", rel),
|
|
15957
|
+
join28(project.context.root, rel)
|
|
15958
|
+
].find((path) => existsSync16(path));
|
|
14978
15959
|
return file ? readFileSync9(file, "utf-8") : undefined;
|
|
14979
15960
|
};
|
|
14980
15961
|
var resolveLogo = (project) => {
|
|
@@ -14983,7 +15964,7 @@ var resolveLogo = (project) => {
|
|
|
14983
15964
|
return null;
|
|
14984
15965
|
}
|
|
14985
15966
|
const config = typeof logo === "string" ? { image: logo } : logo;
|
|
14986
|
-
const { href, image: source, text } = config;
|
|
15967
|
+
const { href, image: source, text: text2 } = config;
|
|
14987
15968
|
const image = typeof source === "string" ? { light: source } : source;
|
|
14988
15969
|
const light = image?.light ?? image?.dark;
|
|
14989
15970
|
const dark = image?.dark ?? image?.light;
|
|
@@ -14992,12 +15973,12 @@ var resolveLogo = (project) => {
|
|
|
14992
15973
|
const lightSvg = readLogoSvg(project, light);
|
|
14993
15974
|
const darkSvg = dark === light ? lightSvg : readLogoSvg(project, dark);
|
|
14994
15975
|
if (light && light === dark && lightSvg) {
|
|
14995
|
-
return { alt, href: brandHref, svg: lightSvg, text };
|
|
15976
|
+
return { alt, href: brandHref, svg: lightSvg, text: text2 };
|
|
14996
15977
|
}
|
|
14997
15978
|
const lightDimensions = svgDimensions(lightSvg);
|
|
14998
15979
|
const darkDimensions = svgDimensions(darkSvg);
|
|
14999
15980
|
const dimensions = lightDimensions || darkDimensions ? { dark: darkDimensions, light: lightDimensions } : undefined;
|
|
15000
|
-
return { alt, dark, dimensions, href: brandHref, light, text };
|
|
15981
|
+
return { alt, dark, dimensions, href: brandHref, light, text: text2 };
|
|
15001
15982
|
};
|
|
15002
15983
|
var FAVICON_CANDIDATES = [
|
|
15003
15984
|
"icon.svg",
|
|
@@ -15020,7 +16001,7 @@ var faviconType = (name) => {
|
|
|
15020
16001
|
};
|
|
15021
16002
|
var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync9(file).toString("base64")}`;
|
|
15022
16003
|
var defaultFavicon = () => ({
|
|
15023
|
-
href: inlineDataUri(
|
|
16004
|
+
href: inlineDataUri(join28(BLUME_SRC, "assets", "icon.png"), "image/png"),
|
|
15024
16005
|
type: "image/png"
|
|
15025
16006
|
});
|
|
15026
16007
|
var APPLE_ICON_CANDIDATES = [
|
|
@@ -15032,13 +16013,13 @@ var APPLE_ICON_CANDIDATES = [
|
|
|
15032
16013
|
var resolveIconFile = (project, candidates) => {
|
|
15033
16014
|
const { root } = project.context;
|
|
15034
16015
|
for (const name of candidates) {
|
|
15035
|
-
if (
|
|
16016
|
+
if (existsSync16(join28(root, "public", name))) {
|
|
15036
16017
|
return { href: `/${name}`, type: faviconType(name) };
|
|
15037
16018
|
}
|
|
15038
16019
|
}
|
|
15039
16020
|
for (const name of candidates) {
|
|
15040
|
-
const file =
|
|
15041
|
-
if (
|
|
16021
|
+
const file = join28(root, name);
|
|
16022
|
+
if (existsSync16(file)) {
|
|
15042
16023
|
const type = faviconType(name);
|
|
15043
16024
|
return { href: inlineDataUri(file, type ?? "image/x-icon"), type };
|
|
15044
16025
|
}
|
|
@@ -15062,18 +16043,45 @@ var resolveBanner = (config) => {
|
|
|
15062
16043
|
link: banner.link
|
|
15063
16044
|
};
|
|
15064
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
|
+
};
|
|
15065
16073
|
var buildRuntimeData = (project) => {
|
|
15066
16074
|
const { config, context, graph, manifest } = project;
|
|
15067
16075
|
const { github } = config;
|
|
15068
16076
|
const repoUrl = github ? `https://github.com/${github.owner}/${github.repo}` : null;
|
|
15069
16077
|
const editBase = github ? `${repoUrl}/edit/${github.branch}` : null;
|
|
15070
16078
|
const logo = resolveLogo(project);
|
|
15071
|
-
const ogLogo =
|
|
16079
|
+
const ogLogo = resolveOgMark(project, logo?.svg);
|
|
15072
16080
|
const editUrlFor = (sourcePath) => {
|
|
15073
16081
|
if (!(editBase && sourcePath)) {
|
|
15074
16082
|
return null;
|
|
15075
16083
|
}
|
|
15076
|
-
const rel =
|
|
16084
|
+
const rel = relative14(context.root, sourcePath).split("\\").join("/");
|
|
15077
16085
|
const editPath = github?.dir ? `${github.dir}/${rel}` : rel;
|
|
15078
16086
|
return `${editBase}/${editPath}`;
|
|
15079
16087
|
};
|
|
@@ -15135,10 +16143,11 @@ var buildRuntimeData = (project) => {
|
|
|
15135
16143
|
route: config.ai.mcp.route
|
|
15136
16144
|
} : null,
|
|
15137
16145
|
og: {
|
|
16146
|
+
description: resolveOgDescription(config),
|
|
15138
16147
|
enabled: config.seo.og.enabled ?? false,
|
|
15139
|
-
fonts: config.seo.og.fonts ?? [],
|
|
15140
16148
|
logo: ogLogo,
|
|
15141
|
-
palette: config.seo.og.palette
|
|
16149
|
+
palette: config.seo.og.palette,
|
|
16150
|
+
site: resolveOgSite(config)
|
|
15142
16151
|
},
|
|
15143
16152
|
repoUrl,
|
|
15144
16153
|
search: {
|
|
@@ -15151,6 +16160,10 @@ var buildRuntimeData = (project) => {
|
|
|
15151
16160
|
theme: config.theme,
|
|
15152
16161
|
title: config.title,
|
|
15153
16162
|
toc: config.toc,
|
|
16163
|
+
webmcp: {
|
|
16164
|
+
enabled: config.ai.webmcp,
|
|
16165
|
+
llms: config.ai.llmsTxt.enabled
|
|
16166
|
+
},
|
|
15154
16167
|
x: config.seo.x
|
|
15155
16168
|
},
|
|
15156
16169
|
feeds: buildRssFeeds(project).map((feed) => ({
|
|
@@ -15184,7 +16197,7 @@ var buildRuntimeData = (project) => {
|
|
|
15184
16197
|
var planMcp = (project, srcDir, userPages) => {
|
|
15185
16198
|
const { config } = project;
|
|
15186
16199
|
const { route } = config.ai.mcp;
|
|
15187
|
-
const dir =
|
|
16200
|
+
const dir = join28(srcDir, "blume-mcp");
|
|
15188
16201
|
const base = {
|
|
15189
16202
|
dir,
|
|
15190
16203
|
discoveryPages: [],
|
|
@@ -15208,11 +16221,11 @@ var planMcp = (project, srcDir, userPages) => {
|
|
|
15208
16221
|
...base,
|
|
15209
16222
|
discoveryPages: [
|
|
15210
16223
|
{
|
|
15211
|
-
entrypoint:
|
|
16224
|
+
entrypoint: join28(dir, "discovery.ts"),
|
|
15212
16225
|
pattern: "/.well-known/mcp.json"
|
|
15213
16226
|
},
|
|
15214
16227
|
{
|
|
15215
|
-
entrypoint:
|
|
16228
|
+
entrypoint: join28(dir, "server-card.ts"),
|
|
15216
16229
|
pattern: "/.well-known/mcp/server-card.json"
|
|
15217
16230
|
}
|
|
15218
16231
|
],
|
|
@@ -15232,11 +16245,11 @@ var writeMcpFiles = async (project, plan, write) => {
|
|
|
15232
16245
|
version: data.version
|
|
15233
16246
|
};
|
|
15234
16247
|
await Promise.all([
|
|
15235
|
-
write(
|
|
16248
|
+
write(join28(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
|
|
15236
16249
|
`),
|
|
15237
|
-
write(
|
|
15238
|
-
write(
|
|
15239
|
-
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)))
|
|
15240
16253
|
]);
|
|
15241
16254
|
};
|
|
15242
16255
|
var writeAskFiles = async (project, srcDir, write) => {
|
|
@@ -15246,20 +16259,20 @@ var writeAskFiles = async (project, srcDir, write) => {
|
|
|
15246
16259
|
}
|
|
15247
16260
|
const grounded = ask.provider !== "inkeep";
|
|
15248
16261
|
if (grounded) {
|
|
15249
|
-
await write(
|
|
16262
|
+
await write(join28(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
|
|
15250
16263
|
`);
|
|
15251
16264
|
}
|
|
15252
|
-
await write(
|
|
16265
|
+
await write(join28(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
|
|
15253
16266
|
};
|
|
15254
16267
|
var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
15255
16268
|
if (routeIsTaken(pages, contentPages, "/404")) {
|
|
15256
16269
|
return;
|
|
15257
16270
|
}
|
|
15258
|
-
await write(
|
|
16271
|
+
await write(join28(srcDir, "pages", "404.astro"), notFoundPageTemplate());
|
|
15259
16272
|
};
|
|
15260
16273
|
var diagnosticWarning = (diagnostic) => diagnostic.suggestion ? `${diagnostic.message} ${diagnostic.suggestion}` : diagnostic.message;
|
|
15261
16274
|
var buildComponentSlots = async (componentsFile) => {
|
|
15262
|
-
const analysis = componentsFile ? analyzeComponentOverrides(await
|
|
16275
|
+
const analysis = componentsFile ? analyzeComponentOverrides(await readFile16(componentsFile, "utf-8"), componentsFile) : null;
|
|
15263
16276
|
return {
|
|
15264
16277
|
plan: planComponentSlots(componentsFile, analysis),
|
|
15265
16278
|
tags: analysis ? [...analysis.mdx, ...analysis.islands].map((entry) => entry.key) : [],
|
|
@@ -15267,17 +16280,34 @@ var buildComponentSlots = async (componentsFile) => {
|
|
|
15267
16280
|
};
|
|
15268
16281
|
};
|
|
15269
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
|
+
};
|
|
15270
16299
|
var generateRuntime = async (project) => {
|
|
15271
16300
|
const { context, config } = project;
|
|
16301
|
+
assertFontFilesExist(project);
|
|
15272
16302
|
const out = context.outDir;
|
|
15273
|
-
const srcDir =
|
|
15274
|
-
const askPath =
|
|
15275
|
-
const dataPath =
|
|
15276
|
-
const themePath =
|
|
15277
|
-
const searchClientPath =
|
|
15278
|
-
const examplesPath =
|
|
15279
|
-
const examplesThemePath =
|
|
15280
|
-
const openapiPath =
|
|
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");
|
|
15281
16311
|
const written = new Set;
|
|
15282
16312
|
const write = (path, content) => {
|
|
15283
16313
|
written.add(normalize3(path));
|
|
@@ -15332,7 +16362,7 @@ var generateRuntime = async (project) => {
|
|
|
15332
16362
|
const docsCollection = resolveDocsCollection(config, context);
|
|
15333
16363
|
const [structural] = await Promise.all([
|
|
15334
16364
|
Promise.all([
|
|
15335
|
-
write(
|
|
16365
|
+
write(join28(out, "astro.config.mjs"), astroConfigTemplate({
|
|
15336
16366
|
aliases: resolveTsconfigAliases(context.root),
|
|
15337
16367
|
askPath,
|
|
15338
16368
|
config,
|
|
@@ -15352,26 +16382,26 @@ var generateRuntime = async (project) => {
|
|
|
15352
16382
|
searchClientPath,
|
|
15353
16383
|
themePath
|
|
15354
16384
|
})),
|
|
15355
|
-
write(
|
|
15356
|
-
write(
|
|
15357
|
-
write(
|
|
15358
|
-
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({
|
|
15359
16389
|
collection: docsCollection,
|
|
15360
16390
|
config,
|
|
15361
16391
|
context,
|
|
15362
16392
|
filesystem: hasFilesystemSource,
|
|
15363
16393
|
staged: hasStaged
|
|
15364
16394
|
})),
|
|
15365
|
-
write(
|
|
16395
|
+
write(join28(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
|
|
15366
16396
|
exportEpub,
|
|
15367
16397
|
exportPdf,
|
|
15368
16398
|
mathEnabled: usesMath,
|
|
15369
16399
|
needsReact
|
|
15370
16400
|
})),
|
|
15371
16401
|
write(askPath, askComponentTemplate(askEnabled)),
|
|
15372
|
-
write(
|
|
15373
|
-
write(
|
|
15374
|
-
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)),
|
|
15375
16405
|
write(examplesThemePath, examplesEntryTemplate({
|
|
15376
16406
|
configTokens: buildThemeCss(config.theme),
|
|
15377
16407
|
sources: [`${context.root}/**/*.{astro,jsx,svelte,ts,tsx,vue}`],
|
|
@@ -15387,17 +16417,17 @@ var generateRuntime = async (project) => {
|
|
|
15387
16417
|
userTheme
|
|
15388
16418
|
}))
|
|
15389
16419
|
]),
|
|
15390
|
-
Promise.all(islandDiscovery.islands.map((island) => write(
|
|
15391
|
-
Promise.all(slotPlan.wrappers.map((wrapper) => write(
|
|
15392
|
-
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)))),
|
|
15393
16423
|
writeAskFiles(project, srcDir, write),
|
|
15394
16424
|
writeMcpFiles(project, mcp, write)
|
|
15395
16425
|
]);
|
|
15396
16426
|
if (config.seo.og.enabled) {
|
|
15397
|
-
await write(
|
|
16427
|
+
await write(join28(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes, projectOgFonts(project)));
|
|
15398
16428
|
}
|
|
15399
16429
|
if (hasGeneratedChangelog(project, pages)) {
|
|
15400
|
-
await write(
|
|
16430
|
+
await write(join28(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
|
|
15401
16431
|
exportEpub,
|
|
15402
16432
|
exportPdf,
|
|
15403
16433
|
needsReact,
|
|
@@ -15417,27 +16447,31 @@ var generateRuntime = async (project) => {
|
|
|
15417
16447
|
]);
|
|
15418
16448
|
if (servesStaticIndex(config.search.provider)) {
|
|
15419
16449
|
const documents = await buildSearchDocuments(project);
|
|
15420
|
-
await write(
|
|
16450
|
+
await write(join28(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
|
|
15421
16451
|
`);
|
|
15422
|
-
await write(
|
|
16452
|
+
await write(join28(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
|
|
15423
16453
|
}
|
|
15424
16454
|
if (config.search.provider === "mixedbread") {
|
|
15425
|
-
await write(
|
|
16455
|
+
await write(join28(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
|
|
15426
16456
|
}
|
|
15427
16457
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
16458
|
+
const contentAssets = await collectContentAssets(project);
|
|
15428
16459
|
await Promise.all([
|
|
15429
|
-
write(
|
|
16460
|
+
write(join28(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
|
|
15430
16461
|
`),
|
|
15431
|
-
write(
|
|
15432
|
-
write(
|
|
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)}
|
|
16465
|
+
`),
|
|
16466
|
+
write(join28(srcDir, "pages", "blume-assets", "[...asset].ts"), contentAssetsEndpointTemplate(join28(project.context.outDir, "public", "blume-assets")))
|
|
15433
16467
|
]);
|
|
15434
16468
|
const feeds = buildRssFeeds(project);
|
|
15435
16469
|
if (feeds.length > 0) {
|
|
15436
16470
|
const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
|
|
15437
16471
|
await Promise.all([
|
|
15438
|
-
write(
|
|
16472
|
+
write(join28(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
|
|
15439
16473
|
`),
|
|
15440
|
-
write(
|
|
16474
|
+
write(join28(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
|
|
15441
16475
|
]);
|
|
15442
16476
|
}
|
|
15443
16477
|
const warnings = [
|
|
@@ -15473,14 +16507,14 @@ var generateRuntime = async (project) => {
|
|
|
15473
16507
|
root: context.root
|
|
15474
16508
|
});
|
|
15475
16509
|
warnings.push(...references.warnings);
|
|
15476
|
-
await Promise.all(references.files.map((file) => write(
|
|
16510
|
+
await Promise.all(references.files.map((file) => write(join28(srcDir, "pages", file.pagePath), file.content)));
|
|
15477
16511
|
}
|
|
15478
16512
|
const openApiSource2 = project.sources.find(isOpenApiSource);
|
|
15479
16513
|
await Promise.all([
|
|
15480
|
-
write(
|
|
16514
|
+
write(join28(srcDir, "generated", "data.json"), buildRuntimeData(project)),
|
|
15481
16515
|
write(openapiPath, `${JSON.stringify(openApiSource2 ? openApiSource2.openApiData() : {})}
|
|
15482
16516
|
`),
|
|
15483
|
-
write(
|
|
16517
|
+
write(join28(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
|
|
15484
16518
|
`),
|
|
15485
16519
|
writeStagedContent(out, staged)
|
|
15486
16520
|
]);
|
|
@@ -15489,8 +16523,8 @@ var generateRuntime = async (project) => {
|
|
|
15489
16523
|
};
|
|
15490
16524
|
|
|
15491
16525
|
// src/cli/env.ts
|
|
15492
|
-
import { existsSync as
|
|
15493
|
-
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";
|
|
15494
16528
|
var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
|
|
15495
16529
|
var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
|
|
15496
16530
|
var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
|
|
@@ -15536,19 +16570,19 @@ var applyEnv = (parsed) => {
|
|
|
15536
16570
|
};
|
|
15537
16571
|
var loadFile = (path) => {
|
|
15538
16572
|
try {
|
|
15539
|
-
if (
|
|
16573
|
+
if (existsSync17(path)) {
|
|
15540
16574
|
applyEnv(parseEnv(readFileSync10(path, "utf-8")));
|
|
15541
16575
|
}
|
|
15542
16576
|
} catch {}
|
|
15543
16577
|
};
|
|
15544
16578
|
var loadEnvFiles = (startDir) => {
|
|
15545
|
-
let dir =
|
|
16579
|
+
let dir = resolve9(startDir);
|
|
15546
16580
|
let done = false;
|
|
15547
16581
|
while (!done) {
|
|
15548
|
-
loadFile(
|
|
15549
|
-
loadFile(
|
|
15550
|
-
const parent =
|
|
15551
|
-
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;
|
|
15552
16586
|
dir = parent;
|
|
15553
16587
|
}
|
|
15554
16588
|
};
|
|
@@ -15653,20 +16687,91 @@ var emitRedirectFiles = async (config, distDir) => {
|
|
|
15653
16687
|
if (redirects.length === 0 || config.deployment.output !== "static") {
|
|
15654
16688
|
return;
|
|
15655
16689
|
}
|
|
15656
|
-
await writeFile8(
|
|
16690
|
+
await writeFile8(join30(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
|
|
15657
16691
|
const platformFiles = [
|
|
15658
16692
|
{ content: buildNetlifyRedirects(redirects), name: "_redirects" },
|
|
15659
16693
|
{ content: buildVercelConfig(redirects), name: "vercel.json" }
|
|
15660
16694
|
];
|
|
15661
|
-
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")));
|
|
15662
16696
|
logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
|
|
15663
16697
|
};
|
|
15664
|
-
var emitHeaderFiles = async (
|
|
15665
|
-
|
|
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.");
|
|
15666
16771
|
return;
|
|
15667
16772
|
}
|
|
15668
|
-
await writeFile8(
|
|
15669
|
-
logger.success("
|
|
16773
|
+
await writeFile8(configPath, injected, "utf-8");
|
|
16774
|
+
logger.success("Wired Accept: text/markdown negotiation into the Vercel routing config");
|
|
15670
16775
|
};
|
|
15671
16776
|
var formatBytes2 = (bytes) => {
|
|
15672
16777
|
if (bytes < 1024) {
|
|
@@ -15676,14 +16781,14 @@ var formatBytes2 = (bytes) => {
|
|
|
15676
16781
|
return `${(bytes / 1024).toFixed(digits)} kB`;
|
|
15677
16782
|
};
|
|
15678
16783
|
var astroAssets = async (distDir, ext) => {
|
|
15679
|
-
const astroDir =
|
|
15680
|
-
if (!
|
|
16784
|
+
const astroDir = join30(distDir, "_astro");
|
|
16785
|
+
if (!existsSync18(astroDir)) {
|
|
15681
16786
|
return [];
|
|
15682
16787
|
}
|
|
15683
|
-
const entries = await
|
|
16788
|
+
const entries = await readdir2(astroDir);
|
|
15684
16789
|
const files = entries.filter((name) => name.endsWith(`.${ext}`));
|
|
15685
16790
|
const sized = await Promise.all(files.map(async (name) => {
|
|
15686
|
-
const info = await
|
|
16791
|
+
const info = await stat3(join30(astroDir, name));
|
|
15687
16792
|
return { name, size: info.size };
|
|
15688
16793
|
}));
|
|
15689
16794
|
return sized.toSorted((a, b) => b.size - a.size);
|
|
@@ -15736,26 +16841,26 @@ var runClientAssetChecks = async (staticDir, args) => {
|
|
|
15736
16841
|
var isolatedOutputDir = (config, context) => {
|
|
15737
16842
|
const { adapter, output } = config.deployment;
|
|
15738
16843
|
if (output === "server" && adapter === "vercel") {
|
|
15739
|
-
return
|
|
16844
|
+
return join30(context.outDir, ".vercel", "output");
|
|
15740
16845
|
}
|
|
15741
|
-
return context.distDir ??
|
|
16846
|
+
return context.distDir ?? join30(context.outDir, "dist");
|
|
15742
16847
|
};
|
|
15743
16848
|
var isolatedStaticDir = (config, context) => {
|
|
15744
16849
|
const { adapter, output } = config.deployment;
|
|
15745
16850
|
const outputDir = isolatedOutputDir(config, context);
|
|
15746
16851
|
if (output === "server" && adapter === "vercel") {
|
|
15747
|
-
return
|
|
16852
|
+
return join30(outputDir, "static");
|
|
15748
16853
|
}
|
|
15749
16854
|
if (output === "server" && adapter === "node") {
|
|
15750
|
-
return
|
|
16855
|
+
return join30(outputDir, "client");
|
|
15751
16856
|
}
|
|
15752
16857
|
return outputDir;
|
|
15753
16858
|
};
|
|
15754
16859
|
var publishLlmsFiles = async (project, distDir) => {
|
|
15755
|
-
const indexPath =
|
|
15756
|
-
const fullPath =
|
|
15757
|
-
const writeIndex = !
|
|
15758
|
-
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);
|
|
15759
16864
|
if (!(writeIndex || writeFull)) {
|
|
15760
16865
|
return;
|
|
15761
16866
|
}
|
|
@@ -15788,23 +16893,25 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
15788
16893
|
await publishLlmsFiles(project, distDir);
|
|
15789
16894
|
}
|
|
15790
16895
|
const sitemap = buildSitemap(project);
|
|
15791
|
-
if (sitemap && !
|
|
15792
|
-
await writeFile8(
|
|
16896
|
+
if (sitemap && !existsSync18(join30(distDir, "sitemap.xml"))) {
|
|
16897
|
+
await writeFile8(join30(distDir, "sitemap.xml"), sitemap, "utf-8");
|
|
15793
16898
|
logger.success("Generated sitemap.xml");
|
|
15794
16899
|
}
|
|
15795
16900
|
const robots = buildRobots(project);
|
|
15796
|
-
if (robots && !
|
|
15797
|
-
await writeFile8(
|
|
16901
|
+
if (robots && !existsSync18(join30(distDir, "robots.txt"))) {
|
|
16902
|
+
await writeFile8(join30(distDir, "robots.txt"), robots, "utf-8");
|
|
15798
16903
|
logger.success("Generated robots.txt");
|
|
15799
16904
|
}
|
|
15800
16905
|
const agentReadability = buildAgentReadability(project);
|
|
15801
|
-
if (agentReadability && !
|
|
15802
|
-
await writeFile8(
|
|
16906
|
+
if (agentReadability && !existsSync18(join30(distDir, "agent-readability.json"))) {
|
|
16907
|
+
await writeFile8(join30(distDir, "agent-readability.json"), `${JSON.stringify(agentReadability, null, 2)}
|
|
15803
16908
|
`, "utf-8");
|
|
15804
16909
|
logger.success("Generated agent-readability.json");
|
|
15805
16910
|
}
|
|
16911
|
+
await emitWellKnownFiles(project.config, distDir);
|
|
16912
|
+
await emitAgentSkills(project, distDir);
|
|
15806
16913
|
await emitRedirectFiles(project.config, distDir);
|
|
15807
|
-
await emitHeaderFiles(project
|
|
16914
|
+
await emitHeaderFiles(project, distDir);
|
|
15808
16915
|
const { config } = project;
|
|
15809
16916
|
const features = serverFeatures(config);
|
|
15810
16917
|
const sitemapNote = config.seo.sitemap ? "no (set deployment.site)" : "no (seo.sitemap is false)";
|
|
@@ -15918,16 +17025,19 @@ var buildCommand = defineCommand3({
|
|
|
15918
17025
|
if (surfaced.moved) {
|
|
15919
17026
|
logger.success(`Surfaced ${adapter} output to ${surfaced.to}`);
|
|
15920
17027
|
}
|
|
17028
|
+
if (project.config.deployment.output === "server" && adapter === "vercel") {
|
|
17029
|
+
await emitVercelNegotiation(project.config, project.manifest.routes.map((route) => route.path), root);
|
|
17030
|
+
}
|
|
15921
17031
|
await publishBuildArtifacts(project, deployStaticDir(project.config, project.context), args);
|
|
15922
17032
|
}
|
|
15923
17033
|
});
|
|
15924
17034
|
|
|
15925
17035
|
// src/cli/commands/check.ts
|
|
15926
|
-
import { existsSync as
|
|
17036
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
15927
17037
|
import { check } from "@astrojs/check";
|
|
15928
17038
|
import { sync } from "astro";
|
|
15929
17039
|
import { defineCommand as defineCommand4 } from "citty";
|
|
15930
|
-
import { join as
|
|
17040
|
+
import { join as join31 } from "pathe";
|
|
15931
17041
|
var checkCommand = defineCommand4({
|
|
15932
17042
|
args: {
|
|
15933
17043
|
isolated: {
|
|
@@ -15963,13 +17073,13 @@ var checkCommand = defineCommand4({
|
|
|
15963
17073
|
});
|
|
15964
17074
|
const { outDir } = project.context;
|
|
15965
17075
|
await sync({ logLevel: "warn", root: outDir });
|
|
15966
|
-
const tsconfig =
|
|
17076
|
+
const tsconfig = join31(root, "tsconfig.json");
|
|
15967
17077
|
logger.start(`Type-checking ${project.graph.pages.length} page(s)`);
|
|
15968
17078
|
const failed = await check({
|
|
15969
17079
|
minimumFailingSeverity: "error",
|
|
15970
17080
|
minimumSeverity: "hint",
|
|
15971
17081
|
root: outDir,
|
|
15972
|
-
tsconfig:
|
|
17082
|
+
tsconfig: existsSync19(tsconfig) ? tsconfig : undefined,
|
|
15973
17083
|
watch: false
|
|
15974
17084
|
});
|
|
15975
17085
|
if (failed) {
|
|
@@ -15984,7 +17094,7 @@ var checkCommand = defineCommand4({
|
|
|
15984
17094
|
import { watch } from "node:fs";
|
|
15985
17095
|
import { dev } from "astro";
|
|
15986
17096
|
import { defineCommand as defineCommand5 } from "citty";
|
|
15987
|
-
import { basename as basename4, dirname as
|
|
17097
|
+
import { basename as basename4, dirname as dirname13 } from "pathe";
|
|
15988
17098
|
|
|
15989
17099
|
// src/astro/integration.ts
|
|
15990
17100
|
var overlayServer = null;
|
|
@@ -16177,7 +17287,7 @@ var devCommand = defineCommand5({
|
|
|
16177
17287
|
}),
|
|
16178
17288
|
...fileTargets.map((target) => {
|
|
16179
17289
|
const name = basename4(target);
|
|
16180
|
-
const watcher = watch(
|
|
17290
|
+
const watcher = watch(dirname13(target), (_event, filename) => {
|
|
16181
17291
|
if (!filename || filename === name) {
|
|
16182
17292
|
regenerate();
|
|
16183
17293
|
}
|
|
@@ -16201,12 +17311,12 @@ var devCommand = defineCommand5({
|
|
|
16201
17311
|
// src/cli/commands/doctor.ts
|
|
16202
17312
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
16203
17313
|
import { defineCommand as defineCommand6 } from "citty";
|
|
16204
|
-
import { join as
|
|
17314
|
+
import { join as join32 } from "pathe";
|
|
16205
17315
|
var FALLBACK_MIN_NODE = "22.12.0";
|
|
16206
17316
|
var LEADING_RANGE = /^[^\d]*/u;
|
|
16207
17317
|
var minSupportedNode = () => {
|
|
16208
17318
|
try {
|
|
16209
|
-
const pkg = JSON.parse(readFileSync11(
|
|
17319
|
+
const pkg = JSON.parse(readFileSync11(join32(packageRoot(), "package.json"), "utf-8"));
|
|
16210
17320
|
const range = pkg.engines?.node ?? "";
|
|
16211
17321
|
return range.replace(LEADING_RANGE, "") || FALLBACK_MIN_NODE;
|
|
16212
17322
|
} catch {
|
|
@@ -16299,21 +17409,21 @@ var doctorCommand = defineCommand6({
|
|
|
16299
17409
|
|
|
16300
17410
|
// src/cli/commands/eject.ts
|
|
16301
17411
|
import { defineCommand as defineCommand7 } from "citty";
|
|
16302
|
-
import { relative as
|
|
17412
|
+
import { relative as relative17 } from "pathe";
|
|
16303
17413
|
|
|
16304
17414
|
// src/registry/eject.ts
|
|
16305
|
-
import { existsSync as
|
|
16306
|
-
import { cp as cp2, mkdir as
|
|
16307
|
-
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";
|
|
16308
17418
|
var toPosix = (path) => path.split("\\").join("/");
|
|
16309
17419
|
var LOCAL_BLUME_SOURCE = "../../node_modules/blume/src/**/*.{astro,ts,tsx}";
|
|
16310
17420
|
var blumeSourceGlob = (root, genDir, resolveBlumeRoot = packageRoot) => {
|
|
16311
|
-
if (
|
|
17421
|
+
if (existsSync20(join33(root, "node_modules", "blume"))) {
|
|
16312
17422
|
return LOCAL_BLUME_SOURCE;
|
|
16313
17423
|
}
|
|
16314
17424
|
try {
|
|
16315
|
-
const src =
|
|
16316
|
-
return `${toPosix(
|
|
17425
|
+
const src = join33(resolveBlumeRoot(), "src");
|
|
17426
|
+
return `${toPosix(relative15(genDir, src))}/**/*.{astro,ts,tsx}`;
|
|
16317
17427
|
} catch {
|
|
16318
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.');
|
|
16319
17429
|
return LOCAL_BLUME_SOURCE;
|
|
@@ -16326,28 +17436,28 @@ var ejectOpenApiData = (project) => {
|
|
|
16326
17436
|
var askFiles = async (project, srcDir, genDir) => {
|
|
16327
17437
|
const { ask } = project.config.ai;
|
|
16328
17438
|
if (!(ask?.enabled && !ask.endpoint)) {
|
|
16329
|
-
const endpointPath =
|
|
16330
|
-
if (
|
|
16331
|
-
const content = await
|
|
17439
|
+
const endpointPath = join33(srcDir, "pages", "api", "ask.ts");
|
|
17440
|
+
if (existsSync20(endpointPath)) {
|
|
17441
|
+
const content = await readFile18(endpointPath, "utf-8");
|
|
16332
17442
|
if (content.startsWith("// Generated by Blume. Do not edit.")) {
|
|
16333
17443
|
await rm3(endpointPath, { force: true });
|
|
16334
17444
|
}
|
|
16335
17445
|
}
|
|
16336
|
-
await rm3(
|
|
17446
|
+
await rm3(join33(genDir, "ask-data.json"), { force: true });
|
|
16337
17447
|
return [];
|
|
16338
17448
|
}
|
|
16339
17449
|
const grounded = ask.provider !== "inkeep";
|
|
16340
17450
|
const files = [
|
|
16341
17451
|
{
|
|
16342
17452
|
content: askEndpointTemplate(resolveAskBackend(ask), grounded),
|
|
16343
|
-
path:
|
|
17453
|
+
path: join33(srcDir, "pages", "api", "ask.ts")
|
|
16344
17454
|
}
|
|
16345
17455
|
];
|
|
16346
17456
|
if (grounded) {
|
|
16347
17457
|
files.push({
|
|
16348
17458
|
content: `${JSON.stringify(await buildAskData(project))}
|
|
16349
17459
|
`,
|
|
16350
|
-
path:
|
|
17460
|
+
path: join33(genDir, "ask-data.json")
|
|
16351
17461
|
});
|
|
16352
17462
|
}
|
|
16353
17463
|
return files;
|
|
@@ -16380,19 +17490,19 @@ var mcpFiles = async (project, userPages, srcDir, genDir) => {
|
|
|
16380
17490
|
{
|
|
16381
17491
|
content: `${JSON.stringify(data)}
|
|
16382
17492
|
`,
|
|
16383
|
-
path:
|
|
17493
|
+
path: join33(genDir, "mcp-data.json")
|
|
16384
17494
|
},
|
|
16385
17495
|
{
|
|
16386
17496
|
content: mcpEndpointTemplate(route),
|
|
16387
|
-
path:
|
|
17497
|
+
path: join33(srcDir, "pages", mcpPageFile(route))
|
|
16388
17498
|
},
|
|
16389
17499
|
{
|
|
16390
17500
|
content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),
|
|
16391
|
-
path:
|
|
17501
|
+
path: join33(srcDir, "blume-mcp", "discovery.ts")
|
|
16392
17502
|
},
|
|
16393
17503
|
{
|
|
16394
17504
|
content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),
|
|
16395
|
-
path:
|
|
17505
|
+
path: join33(srcDir, "blume-mcp", "server-card.ts")
|
|
16396
17506
|
}
|
|
16397
17507
|
];
|
|
16398
17508
|
};
|
|
@@ -16405,23 +17515,23 @@ var changelogFiles = (project, userPages, srcDir, options) => {
|
|
|
16405
17515
|
return [
|
|
16406
17516
|
{
|
|
16407
17517
|
content: changelogIndexTemplate(options),
|
|
16408
|
-
path:
|
|
17518
|
+
path: join33(srcDir, "pages", "changelog.astro")
|
|
16409
17519
|
}
|
|
16410
17520
|
];
|
|
16411
17521
|
};
|
|
16412
|
-
var readExamplesCss = (root, css) => css &&
|
|
17522
|
+
var readExamplesCss = (root, css) => css && existsSync20(join33(root, css)) ? readFile18(join33(root, css), "utf-8") : Promise.resolve("");
|
|
16413
17523
|
var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
|
|
16414
17524
|
{
|
|
16415
17525
|
content: examplesPageTemplate(),
|
|
16416
|
-
path:
|
|
17526
|
+
path: join33(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
|
|
16417
17527
|
}
|
|
16418
17528
|
] : [];
|
|
16419
|
-
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;
|
|
16420
17530
|
var eject = async (root) => {
|
|
16421
17531
|
const project = await scanProject(root, { mode: "build" });
|
|
16422
17532
|
const { context, config } = project;
|
|
16423
|
-
const srcDir =
|
|
16424
|
-
const genDir =
|
|
17533
|
+
const srcDir = join33(root, "src");
|
|
17534
|
+
const genDir = join33(srcDir, "generated");
|
|
16425
17535
|
const askEnabled = config.ai.ask?.enabled ?? false;
|
|
16426
17536
|
const exportPdf = config.export.pdf;
|
|
16427
17537
|
const exportEpub = config.export.epub;
|
|
@@ -16439,7 +17549,7 @@ var eject = async (root) => {
|
|
|
16439
17549
|
context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
|
|
16440
17550
|
detectNeedsReact(root),
|
|
16441
17551
|
detectUsesMath(root),
|
|
16442
|
-
context.themeFile ?
|
|
17552
|
+
context.themeFile ? readFile18(context.themeFile, "utf-8") : Promise.resolve(""),
|
|
16443
17553
|
readExamplesCss(root, config.examples.css),
|
|
16444
17554
|
buildRawMarkdown(project),
|
|
16445
17555
|
discoverIslands(root)
|
|
@@ -16453,14 +17563,14 @@ var eject = async (root) => {
|
|
|
16453
17563
|
const needsSvelte = frameworks.has("svelte");
|
|
16454
17564
|
const relContext = {
|
|
16455
17565
|
...context,
|
|
16456
|
-
contentRoot: toPosix(
|
|
17566
|
+
contentRoot: toPosix(relative15(root, context.contentRoot)),
|
|
16457
17567
|
outDir: ".",
|
|
16458
17568
|
root: "."
|
|
16459
17569
|
};
|
|
16460
|
-
const componentsImport = context.componentsFile ? `../../${toPosix(
|
|
17570
|
+
const componentsImport = context.componentsFile ? `../../${toPosix(relative15(root, context.componentsFile))}` : null;
|
|
16461
17571
|
const relPages = [
|
|
16462
17572
|
...pages.map((page) => ({
|
|
16463
|
-
entrypoint: toPosix(
|
|
17573
|
+
entrypoint: toPosix(relative15(root, page.entrypoint)),
|
|
16464
17574
|
pattern: page.pattern
|
|
16465
17575
|
})),
|
|
16466
17576
|
...mcpDiscoveryPages(project, pages)
|
|
@@ -16487,14 +17597,14 @@ var eject = async (root) => {
|
|
|
16487
17597
|
searchClientPath: "./src/generated/search-client.ts",
|
|
16488
17598
|
themePath: "./src/generated/app.css"
|
|
16489
17599
|
}),
|
|
16490
|
-
path:
|
|
17600
|
+
path: join33(root, "astro.config.mjs")
|
|
16491
17601
|
},
|
|
16492
17602
|
{
|
|
16493
17603
|
content: runtimeTsconfigTemplate(),
|
|
16494
|
-
path:
|
|
17604
|
+
path: join33(root, "tsconfig.json"),
|
|
16495
17605
|
skipIfExists: true
|
|
16496
17606
|
},
|
|
16497
|
-
{ content: envTemplate(), path:
|
|
17607
|
+
{ content: envTemplate(), path: join33(srcDir, "env.d.ts") },
|
|
16498
17608
|
{
|
|
16499
17609
|
content: contentConfigTemplate({
|
|
16500
17610
|
config,
|
|
@@ -16502,7 +17612,7 @@ var eject = async (root) => {
|
|
|
16502
17612
|
staged: hasStaged,
|
|
16503
17613
|
stagedBase: stagedDir
|
|
16504
17614
|
}),
|
|
16505
|
-
path:
|
|
17615
|
+
path: join33(srcDir, "content.config.ts")
|
|
16506
17616
|
},
|
|
16507
17617
|
{
|
|
16508
17618
|
content: catchAllPageTemplate({
|
|
@@ -16511,19 +17621,19 @@ var eject = async (root) => {
|
|
|
16511
17621
|
mathEnabled: usesMath,
|
|
16512
17622
|
needsReact
|
|
16513
17623
|
}),
|
|
16514
|
-
path:
|
|
17624
|
+
path: join33(srcDir, "pages", "[...slug].astro")
|
|
16515
17625
|
},
|
|
16516
17626
|
{
|
|
16517
17627
|
content: planComponentSlots(componentsImport, null).module,
|
|
16518
|
-
path:
|
|
17628
|
+
path: join33(genDir, "components.ts")
|
|
16519
17629
|
},
|
|
16520
17630
|
{
|
|
16521
17631
|
content: islandMapTemplate(islands.islands),
|
|
16522
|
-
path:
|
|
17632
|
+
path: join33(genDir, "islands.ts")
|
|
16523
17633
|
},
|
|
16524
17634
|
{
|
|
16525
17635
|
content: exampleMapTemplate(examples.examples, config.basePath),
|
|
16526
|
-
path:
|
|
17636
|
+
path: join33(genDir, "examples.ts")
|
|
16527
17637
|
},
|
|
16528
17638
|
{
|
|
16529
17639
|
content: examplesEntryTemplate({
|
|
@@ -16531,7 +17641,7 @@ var eject = async (root) => {
|
|
|
16531
17641
|
sources: ["../../**/*.{astro,jsx,svelte,ts,tsx,vue}"],
|
|
16532
17642
|
userCss: userExamplesCss
|
|
16533
17643
|
}),
|
|
16534
|
-
path:
|
|
17644
|
+
path: join33(genDir, "examples.css")
|
|
16535
17645
|
},
|
|
16536
17646
|
{
|
|
16537
17647
|
content: tailwindEntryTemplate({
|
|
@@ -16543,30 +17653,30 @@ var eject = async (root) => {
|
|
|
16543
17653
|
twoslashCss: twoslashCss(),
|
|
16544
17654
|
userTheme
|
|
16545
17655
|
}),
|
|
16546
|
-
path:
|
|
17656
|
+
path: join33(genDir, "app.css")
|
|
16547
17657
|
},
|
|
16548
|
-
{ content: buildRuntimeData(project), path:
|
|
17658
|
+
{ content: buildRuntimeData(project), path: join33(genDir, "data.json") },
|
|
16549
17659
|
{
|
|
16550
17660
|
content: askComponentTemplate(askEnabled),
|
|
16551
|
-
path:
|
|
17661
|
+
path: join33(genDir, "Ask.astro")
|
|
16552
17662
|
},
|
|
16553
17663
|
{
|
|
16554
17664
|
content: `${JSON.stringify(ejectOpenApiData(project))}
|
|
16555
17665
|
`,
|
|
16556
|
-
path:
|
|
17666
|
+
path: join33(genDir, "openapi.json")
|
|
16557
17667
|
},
|
|
16558
17668
|
{
|
|
16559
17669
|
content: `${JSON.stringify(rawMarkdown)}
|
|
16560
17670
|
`,
|
|
16561
|
-
path:
|
|
17671
|
+
path: join33(genDir, "raw-markdown.json")
|
|
16562
17672
|
},
|
|
16563
17673
|
{
|
|
16564
17674
|
content: rawMarkdownEndpointTemplate("md"),
|
|
16565
|
-
path:
|
|
17675
|
+
path: join33(srcDir, "pages", "[...slug].md.ts")
|
|
16566
17676
|
},
|
|
16567
17677
|
{
|
|
16568
17678
|
content: rawMarkdownEndpointTemplate("mdx"),
|
|
16569
|
-
path:
|
|
17679
|
+
path: join33(srcDir, "pages", "[...slug].mdx.ts")
|
|
16570
17680
|
}
|
|
16571
17681
|
];
|
|
16572
17682
|
if (askEnabled) {
|
|
@@ -16575,7 +17685,7 @@ var eject = async (root) => {
|
|
|
16575
17685
|
if (config.seo.og.enabled) {
|
|
16576
17686
|
files.push({
|
|
16577
17687
|
content: ogEndpointTemplate(customOgRoutes(pages, config.title, config.seo.og.titles)),
|
|
16578
|
-
path:
|
|
17688
|
+
path: join33(srcDir, "pages", "og", "[...slug].png.ts")
|
|
16579
17689
|
});
|
|
16580
17690
|
}
|
|
16581
17691
|
files.push(...await mcpFiles(project, pages, srcDir, genDir), ...changelogFiles(project, pages, srcDir, {
|
|
@@ -16587,28 +17697,28 @@ var eject = async (root) => {
|
|
|
16587
17697
|
if (!routeIsTaken(pages, project.graph.pages, "/404")) {
|
|
16588
17698
|
files.push({
|
|
16589
17699
|
content: notFoundPageTemplate(),
|
|
16590
|
-
path:
|
|
17700
|
+
path: join33(srcDir, "pages", "404.astro")
|
|
16591
17701
|
});
|
|
16592
17702
|
}
|
|
16593
17703
|
files.push({
|
|
16594
17704
|
content: searchClientTemplate(config),
|
|
16595
|
-
path:
|
|
17705
|
+
path: join33(genDir, "search-client.ts")
|
|
16596
17706
|
});
|
|
16597
17707
|
if (servesStaticIndex(config.search.provider)) {
|
|
16598
17708
|
const documents = await buildSearchDocuments(project);
|
|
16599
17709
|
files.push({
|
|
16600
17710
|
content: `${JSON.stringify(documents)}
|
|
16601
17711
|
`,
|
|
16602
|
-
path:
|
|
17712
|
+
path: join33(genDir, "search.json")
|
|
16603
17713
|
}, {
|
|
16604
17714
|
content: searchEndpointTemplate(),
|
|
16605
|
-
path:
|
|
17715
|
+
path: join33(srcDir, "pages", "blume-search.json.ts")
|
|
16606
17716
|
});
|
|
16607
17717
|
}
|
|
16608
17718
|
if (config.search.provider === "mixedbread") {
|
|
16609
17719
|
files.push({
|
|
16610
17720
|
content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
|
|
16611
|
-
path:
|
|
17721
|
+
path: join33(srcDir, "pages", "api", "search.ts")
|
|
16612
17722
|
});
|
|
16613
17723
|
}
|
|
16614
17724
|
const feeds = buildRssFeeds(project);
|
|
@@ -16617,10 +17727,10 @@ var eject = async (root) => {
|
|
|
16617
17727
|
files.push({
|
|
16618
17728
|
content: `${JSON.stringify(feedXml)}
|
|
16619
17729
|
`,
|
|
16620
|
-
path:
|
|
17730
|
+
path: join33(genDir, "rss.json")
|
|
16621
17731
|
}, {
|
|
16622
17732
|
content: rssEndpointTemplate(),
|
|
16623
|
-
path:
|
|
17733
|
+
path: join33(srcDir, "pages", "[section]", "rss.xml.ts")
|
|
16624
17734
|
});
|
|
16625
17735
|
}
|
|
16626
17736
|
const warnings = [];
|
|
@@ -16634,28 +17744,28 @@ var eject = async (root) => {
|
|
|
16634
17744
|
for (const file of references.files) {
|
|
16635
17745
|
files.push({
|
|
16636
17746
|
content: file.content,
|
|
16637
|
-
path:
|
|
17747
|
+
path: join33(srcDir, "pages", file.pagePath)
|
|
16638
17748
|
});
|
|
16639
17749
|
}
|
|
16640
17750
|
}
|
|
16641
17751
|
files.push(...islands.islands.map((island) => ({
|
|
16642
17752
|
content: islandWrapperTemplate(island),
|
|
16643
|
-
path:
|
|
17753
|
+
path: join33(genDir, "islands", `${island.name}.astro`)
|
|
16644
17754
|
})), ...examples.examples.map((example) => ({
|
|
16645
17755
|
content: exampleWrapperTemplate(example),
|
|
16646
|
-
path:
|
|
17756
|
+
path: join33(genDir, "examples", `${exampleSlug(example.path)}.astro`)
|
|
16647
17757
|
})), ...examplesPreviewFiles(srcDir, config.basePath, examples.examples.length > 0));
|
|
16648
17758
|
for (const [entryId, content] of staged) {
|
|
16649
|
-
files.push({ content, path:
|
|
17759
|
+
files.push({ content, path: join33(root, stagedDir, entryId) });
|
|
16650
17760
|
}
|
|
16651
|
-
const written = files.filter((file) => !(file.skipIfExists &&
|
|
17761
|
+
const written = files.filter((file) => !(file.skipIfExists && existsSync20(file.path)));
|
|
16652
17762
|
await Promise.all(written.map(async (file) => {
|
|
16653
|
-
await
|
|
17763
|
+
await mkdir8(join33(file.path, ".."), { recursive: true });
|
|
16654
17764
|
await writeFile9(file.path, file.content, "utf-8");
|
|
16655
17765
|
}));
|
|
16656
|
-
const assetsSrc =
|
|
16657
|
-
if (
|
|
16658
|
-
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"), {
|
|
16659
17769
|
recursive: true
|
|
16660
17770
|
});
|
|
16661
17771
|
}
|
|
@@ -16664,8 +17774,8 @@ var eject = async (root) => {
|
|
|
16664
17774
|
};
|
|
16665
17775
|
|
|
16666
17776
|
// src/cli/eject-scripts.ts
|
|
16667
|
-
import { readFile as
|
|
16668
|
-
import { join as
|
|
17777
|
+
import { readFile as readFile19, writeFile as writeFile10 } from "node:fs/promises";
|
|
17778
|
+
import { join as join34 } from "pathe";
|
|
16669
17779
|
var droppedArtifactNotices = (config) => {
|
|
16670
17780
|
const notices = [];
|
|
16671
17781
|
if (config.search.provider === "pagefind") {
|
|
@@ -16692,10 +17802,10 @@ var droppedArtifactNotices = (config) => {
|
|
|
16692
17802
|
return notices;
|
|
16693
17803
|
};
|
|
16694
17804
|
var updatePackageScripts = async (root) => {
|
|
16695
|
-
const pkgPath =
|
|
17805
|
+
const pkgPath = join34(root, "package.json");
|
|
16696
17806
|
let pkg;
|
|
16697
17807
|
try {
|
|
16698
|
-
pkg = JSON.parse(await
|
|
17808
|
+
pkg = JSON.parse(await readFile19(pkgPath, "utf-8"));
|
|
16699
17809
|
} catch {
|
|
16700
17810
|
return;
|
|
16701
17811
|
}
|
|
@@ -16711,9 +17821,9 @@ var updatePackageScripts = async (root) => {
|
|
|
16711
17821
|
};
|
|
16712
17822
|
|
|
16713
17823
|
// src/cli/init/scaffold.ts
|
|
16714
|
-
import { existsSync as
|
|
16715
|
-
import { mkdir as
|
|
16716
|
-
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";
|
|
16717
17827
|
|
|
16718
17828
|
// src/core/package-json.ts
|
|
16719
17829
|
var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
|
|
@@ -16772,7 +17882,7 @@ var STARTERS = {
|
|
|
16772
17882
|
files: (dir) => [
|
|
16773
17883
|
{
|
|
16774
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`."),
|
|
16775
|
-
path:
|
|
17885
|
+
path: join35(dir, "index.mdx")
|
|
16776
17886
|
}
|
|
16777
17887
|
]
|
|
16778
17888
|
},
|
|
@@ -16787,7 +17897,7 @@ var STARTERS = {
|
|
|
16787
17897
|
files: (dir) => [
|
|
16788
17898
|
{
|
|
16789
17899
|
content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
|
|
16790
|
-
path:
|
|
17900
|
+
path: join35(dir, "index.mdx")
|
|
16791
17901
|
},
|
|
16792
17902
|
{
|
|
16793
17903
|
content: `---
|
|
@@ -16798,7 +17908,7 @@ date: 2026-01-01
|
|
|
16798
17908
|
|
|
16799
17909
|
The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
|
|
16800
17910
|
`,
|
|
16801
|
-
path:
|
|
17911
|
+
path: join35(dir, "changelog", "v1-0-0.mdx")
|
|
16802
17912
|
}
|
|
16803
17913
|
]
|
|
16804
17914
|
},
|
|
@@ -16811,7 +17921,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
|
|
|
16811
17921
|
Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
|
|
16812
17922
|
|
|
16813
17923
|
Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
16814
|
-
path:
|
|
17924
|
+
path: join35(dir, "index.mdx")
|
|
16815
17925
|
}
|
|
16816
17926
|
]
|
|
16817
17927
|
},
|
|
@@ -16822,11 +17932,11 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
|
16822
17932
|
content: page("Introduction", "Get started with the SDK.", `# Introduction
|
|
16823
17933
|
|
|
16824
17934
|
Install the SDK and make your first call. See [Installation](/installation).`),
|
|
16825
|
-
path:
|
|
17935
|
+
path: join35(dir, "index.mdx")
|
|
16826
17936
|
},
|
|
16827
17937
|
{
|
|
16828
17938
|
content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
|
|
16829
|
-
path:
|
|
17939
|
+
path: join35(dir, "installation.mdx")
|
|
16830
17940
|
}
|
|
16831
17941
|
]
|
|
16832
17942
|
}
|
|
@@ -16841,7 +17951,7 @@ var detectPackageManager = (userAgent) => {
|
|
|
16841
17951
|
const name = userAgent?.split("/")[0];
|
|
16842
17952
|
return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : "npm";
|
|
16843
17953
|
};
|
|
16844
|
-
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;
|
|
16845
17955
|
var titleize = (raw) => {
|
|
16846
17956
|
const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
|
|
16847
17957
|
return words.length === 0 ? "My Docs" : words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
@@ -16918,27 +18028,27 @@ export default defineConfig({
|
|
|
16918
18028
|
`;
|
|
16919
18029
|
var extraDepsFor = (sources) => ({
|
|
16920
18030
|
...sources.includes("notion") && { "@notionhq/client": "^2.2.15" },
|
|
16921
|
-
...sources.includes("sanity") && { "@sanity/client": "^
|
|
18031
|
+
...sources.includes("sanity") && { "@sanity/client": "^7.25.0" }
|
|
16922
18032
|
});
|
|
16923
18033
|
var buildPlan = (root, answers) => {
|
|
16924
18034
|
const files = [
|
|
16925
18035
|
{
|
|
16926
18036
|
content: blumePackageJson(toPackageName(basename5(root)), extraDepsFor(answers.sources)),
|
|
16927
|
-
path:
|
|
18037
|
+
path: join35(root, "package.json")
|
|
16928
18038
|
},
|
|
16929
|
-
{ content: buildConfig(answers), path:
|
|
18039
|
+
{ content: buildConfig(answers), path: join35(root, "blume.config.ts") }
|
|
16930
18040
|
];
|
|
16931
18041
|
if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
|
|
16932
|
-
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) })));
|
|
16933
18043
|
}
|
|
16934
18044
|
return files;
|
|
16935
18045
|
};
|
|
16936
18046
|
var writeFileSafe = async (file, log) => {
|
|
16937
|
-
if (
|
|
18047
|
+
if (existsSync21(file.path)) {
|
|
16938
18048
|
log.info(`Skipped existing ${file.path}`);
|
|
16939
18049
|
return false;
|
|
16940
18050
|
}
|
|
16941
|
-
await
|
|
18051
|
+
await mkdir9(dirname14(file.path), { recursive: true });
|
|
16942
18052
|
await writeFile11(file.path, file.content, "utf-8");
|
|
16943
18053
|
log.success(`Created ${file.path}`);
|
|
16944
18054
|
return true;
|
|
@@ -17014,7 +18124,7 @@ var ejectCommand = defineCommand7({
|
|
|
17014
18124
|
}
|
|
17015
18125
|
logger.success(`Ejected ${files.length} file(s):`);
|
|
17016
18126
|
for (const file of files) {
|
|
17017
|
-
process.stdout.write(` ${
|
|
18127
|
+
process.stdout.write(` ${relative17(root, file)}
|
|
17018
18128
|
`);
|
|
17019
18129
|
}
|
|
17020
18130
|
reportDroppedArtifacts(notices);
|
|
@@ -17030,9 +18140,9 @@ The blume package remains importable.`);
|
|
|
17030
18140
|
});
|
|
17031
18141
|
|
|
17032
18142
|
// src/cli/commands/eval.ts
|
|
17033
|
-
import { existsSync as
|
|
18143
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
17034
18144
|
import { defineCommand as defineCommand8 } from "citty";
|
|
17035
|
-
import { join as
|
|
18145
|
+
import { join as join39 } from "pathe";
|
|
17036
18146
|
|
|
17037
18147
|
// src/eval/prompts.ts
|
|
17038
18148
|
var readerPrompt = (question) => `You are evaluating whether a product's documentation can answer a user's question.
|
|
@@ -17101,7 +18211,7 @@ When you are done, print the file and suggest running \`blume eval\` to try it.`
|
|
|
17101
18211
|
// src/eval/report.ts
|
|
17102
18212
|
import { mkdtemp as mkdtemp2, writeFile as writeFile12 } from "node:fs/promises";
|
|
17103
18213
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
17104
|
-
import { join as
|
|
18214
|
+
import { join as join36, relative as relative18 } from "pathe";
|
|
17105
18215
|
var ESC4 = String.fromCodePoint(27);
|
|
17106
18216
|
var COLORS3 = {
|
|
17107
18217
|
bold: `${ESC4}[1m`,
|
|
@@ -17182,15 +18292,15 @@ var summaryLine2 = (result) => {
|
|
|
17182
18292
|
var headerLine = (total, agent) => `${COLORS3.bold}blume eval${COLORS3.reset} ${total} question(s) · ${AGENTS[agent].name}`;
|
|
17183
18293
|
var startLine = (id2, index, total) => ` ${COLORS3.dim}▸ ${id2} (${index + 1}/${total})${COLORS3.reset}`;
|
|
17184
18294
|
var fixLines = (result, root) => result.diagnostics.filter((diagnostic) => diagnostic.code !== "BLUME_EVAL_ROUTE_UNKNOWN").map((finding2) => {
|
|
17185
|
-
const site = finding2.file ? `${
|
|
18295
|
+
const site = finding2.file ? `${relative18(root, finding2.file)}${finding2.line ? `:${finding2.line}` : ""}` : "";
|
|
17186
18296
|
return ` ${COLORS3.cyan}fix:${COLORS3.reset} ${site} ${COLORS3.dim}${finding2.message}${COLORS3.reset}`;
|
|
17187
18297
|
});
|
|
17188
18298
|
var warningLines = (result, root) => result.diagnostics.filter((diagnostic) => diagnostic.code === "BLUME_EVAL_ROUTE_UNKNOWN").map((finding2) => {
|
|
17189
|
-
const site = finding2.file ? ` ${
|
|
18299
|
+
const site = finding2.file ? ` ${relative18(root, finding2.file)}${finding2.line ? `:${finding2.line}` : ""}` : "";
|
|
17190
18300
|
return ` ${COLORS3.yellow}⚠${COLORS3.reset}${site} ${COLORS3.dim}${finding2.message}${COLORS3.reset}`;
|
|
17191
18301
|
});
|
|
17192
18302
|
var evalReportJson = (result, root, threshold) => {
|
|
17193
|
-
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);
|
|
17194
18304
|
return `${JSON.stringify({
|
|
17195
18305
|
diagnostics,
|
|
17196
18306
|
eval: {
|
|
@@ -17206,21 +18316,21 @@ var evalReportJson = (result, root, threshold) => {
|
|
|
17206
18316
|
`;
|
|
17207
18317
|
};
|
|
17208
18318
|
var writeEvalReport = async (result, root, threshold) => {
|
|
17209
|
-
const dir = await mkdtemp2(
|
|
17210
|
-
const path =
|
|
18319
|
+
const dir = await mkdtemp2(join36(tmpdir2(), "blume-eval-"));
|
|
18320
|
+
const path = join36(dir, "report.json");
|
|
17211
18321
|
await writeFile12(path, evalReportJson(result, root, threshold));
|
|
17212
18322
|
return path;
|
|
17213
18323
|
};
|
|
17214
18324
|
|
|
17215
18325
|
// src/eval/run.ts
|
|
17216
|
-
import { mkdir as
|
|
18326
|
+
import { mkdir as mkdir10, mkdtemp as mkdtemp3, writeFile as writeFile14 } from "node:fs/promises";
|
|
17217
18327
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
17218
|
-
import { join as
|
|
18328
|
+
import { join as join38 } from "pathe";
|
|
17219
18329
|
|
|
17220
18330
|
// src/eval/agents.ts
|
|
17221
18331
|
import { spawn as spawn2 } from "node:child_process";
|
|
17222
|
-
import { readFile as
|
|
17223
|
-
import { join as
|
|
18332
|
+
import { readFile as readFile20, writeFile as writeFile13 } from "node:fs/promises";
|
|
18333
|
+
import { join as join37 } from "pathe";
|
|
17224
18334
|
import { z as z3 } from "zod";
|
|
17225
18335
|
var KILL_GRACE_MS = 5000;
|
|
17226
18336
|
var MCP_TOOL_NAMES = [
|
|
@@ -17242,7 +18352,7 @@ var DISALLOWED_TOOLS = [
|
|
|
17242
18352
|
"WebSearch",
|
|
17243
18353
|
"Task"
|
|
17244
18354
|
];
|
|
17245
|
-
var runAgentHeadless = (bin, args, options) => new Promise((
|
|
18355
|
+
var runAgentHeadless = (bin, args, options) => new Promise((resolve11, reject) => {
|
|
17246
18356
|
const platform = options.platform ?? process.platform;
|
|
17247
18357
|
const child = spawn2(bin, args, {
|
|
17248
18358
|
cwd: options.cwd,
|
|
@@ -17271,12 +18381,12 @@ var runAgentHeadless = (bin, args, options) => new Promise((resolve9, reject) =>
|
|
|
17271
18381
|
});
|
|
17272
18382
|
child.once("close", (code) => {
|
|
17273
18383
|
clearTimeout(deadline);
|
|
17274
|
-
|
|
18384
|
+
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
17275
18385
|
});
|
|
17276
18386
|
child.once("exit", (code) => {
|
|
17277
18387
|
if (timedOut) {
|
|
17278
18388
|
clearTimeout(deadline);
|
|
17279
|
-
|
|
18389
|
+
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
17280
18390
|
}
|
|
17281
18391
|
});
|
|
17282
18392
|
child.stdin.end(options.prompt);
|
|
@@ -17286,7 +18396,7 @@ var writeMcpConfig = async (dir, snapshotPath, launcher) => {
|
|
|
17286
18396
|
args: [process.argv[1] ?? "", "mcp-stdio", "--data", snapshotPath],
|
|
17287
18397
|
command: process.execPath
|
|
17288
18398
|
};
|
|
17289
|
-
const configPath =
|
|
18399
|
+
const configPath = join37(dir, "mcp-config.json");
|
|
17290
18400
|
const config = {
|
|
17291
18401
|
mcpServers: {
|
|
17292
18402
|
[MCP_SERVER_NAME]: { args: resolved.args, command: resolved.command }
|
|
@@ -17394,17 +18504,17 @@ var readAgentOutput = async (kind, result, lastMessagePath) => {
|
|
|
17394
18504
|
text: payload.data.result
|
|
17395
18505
|
};
|
|
17396
18506
|
}
|
|
17397
|
-
let
|
|
18507
|
+
let text2;
|
|
17398
18508
|
try {
|
|
17399
|
-
const raw = await
|
|
17400
|
-
|
|
18509
|
+
const raw = await readFile20(lastMessagePath, "utf-8");
|
|
18510
|
+
text2 = raw.trim();
|
|
17401
18511
|
} catch {
|
|
17402
18512
|
return { detail: "no last message written", isError: true, text: "" };
|
|
17403
18513
|
}
|
|
17404
|
-
if (
|
|
18514
|
+
if (text2 === "") {
|
|
17405
18515
|
return { detail: "empty last message", isError: true, text: "" };
|
|
17406
18516
|
}
|
|
17407
|
-
return { isError: false, text };
|
|
18517
|
+
return { isError: false, text: text2 };
|
|
17408
18518
|
};
|
|
17409
18519
|
var verdictSchema = z3.object({
|
|
17410
18520
|
missing: z3.array(z3.string()).default([]),
|
|
@@ -17412,15 +18522,15 @@ var verdictSchema = z3.object({
|
|
|
17412
18522
|
pass: z3.boolean(),
|
|
17413
18523
|
score: z3.number().min(0).max(1).optional()
|
|
17414
18524
|
});
|
|
17415
|
-
var parseVerdict = (
|
|
17416
|
-
const start =
|
|
17417
|
-
const end =
|
|
18525
|
+
var parseVerdict = (text2) => {
|
|
18526
|
+
const start = text2.indexOf("{");
|
|
18527
|
+
const end = text2.lastIndexOf("}");
|
|
17418
18528
|
if (start === -1 || end <= start) {
|
|
17419
18529
|
return;
|
|
17420
18530
|
}
|
|
17421
18531
|
let parsed;
|
|
17422
18532
|
try {
|
|
17423
|
-
parsed = JSON.parse(
|
|
18533
|
+
parsed = JSON.parse(text2.slice(start, end + 1));
|
|
17424
18534
|
} catch {
|
|
17425
18535
|
return;
|
|
17426
18536
|
}
|
|
@@ -17429,7 +18539,7 @@ var parseVerdict = (text) => {
|
|
|
17429
18539
|
};
|
|
17430
18540
|
|
|
17431
18541
|
// src/eval/schema.ts
|
|
17432
|
-
import { readFile as
|
|
18542
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
17433
18543
|
import { load as load2 } from "js-yaml";
|
|
17434
18544
|
import { z as z4 } from "zod";
|
|
17435
18545
|
var ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/u;
|
|
@@ -17474,7 +18584,7 @@ var describeIssues = (error) => error.issues.map((issue) => {
|
|
|
17474
18584
|
var loadEvalsFile = async (path) => {
|
|
17475
18585
|
let raw;
|
|
17476
18586
|
try {
|
|
17477
|
-
raw = await
|
|
18587
|
+
raw = await readFile21(path, "utf-8");
|
|
17478
18588
|
} catch {
|
|
17479
18589
|
throw new EvalsFileError(path, `No evals file found at ${path}. Run \`blume eval init\` to draft one.`);
|
|
17480
18590
|
}
|
|
@@ -17566,9 +18676,9 @@ var errored = (question, detail, durationMs) => ({
|
|
|
17566
18676
|
var runQuestion = async (question, index, context) => {
|
|
17567
18677
|
const started = performance.now();
|
|
17568
18678
|
const elapsed = () => Math.round(performance.now() - started);
|
|
17569
|
-
const workDir =
|
|
17570
|
-
await
|
|
17571
|
-
const answerPath =
|
|
18679
|
+
const workDir = join38(context.dir, `work-${index}`);
|
|
18680
|
+
await mkdir10(workDir, { recursive: true });
|
|
18681
|
+
const answerPath = join38(workDir, "answer.txt");
|
|
17572
18682
|
const reader = await context.run(context.bin, agentArgs(context.kind, { lastMessagePath: answerPath, mcp: context.mcp }), {
|
|
17573
18683
|
cwd: workDir,
|
|
17574
18684
|
prompt: readerPrompt(question),
|
|
@@ -17581,7 +18691,7 @@ var runQuestion = async (question, index, context) => {
|
|
|
17581
18691
|
costUsd: answer.costUsd
|
|
17582
18692
|
};
|
|
17583
18693
|
}
|
|
17584
|
-
const verdictPath =
|
|
18694
|
+
const verdictPath = join38(workDir, "verdict.txt");
|
|
17585
18695
|
const judge = await context.run(context.bin, agentArgs(context.kind, { lastMessagePath: verdictPath }), {
|
|
17586
18696
|
cwd: workDir,
|
|
17587
18697
|
prompt: judgePrompt(question, answer.text),
|
|
@@ -17623,8 +18733,8 @@ var runEval = async (options) => {
|
|
|
17623
18733
|
const kind = options.agent;
|
|
17624
18734
|
const run = options.run ?? runAgentHeadless;
|
|
17625
18735
|
const anchor = { path: options.evalsPath, raw: options.rawEvals };
|
|
17626
|
-
const dir = await mkdtemp3(
|
|
17627
|
-
const snapshotPath =
|
|
18736
|
+
const dir = await mkdtemp3(join38(tmpdir3(), "blume-eval-"));
|
|
18737
|
+
const snapshotPath = join38(dir, "mcp-data.json");
|
|
17628
18738
|
await writeFile14(snapshotPath, JSON.stringify(await buildMcpData(options.project)));
|
|
17629
18739
|
const mcp = await writeMcpConfig(dir, snapshotPath);
|
|
17630
18740
|
const context = {
|
|
@@ -17762,8 +18872,8 @@ var runFixHandoff = async (agent, result, root, threshold) => {
|
|
|
17762
18872
|
}
|
|
17763
18873
|
};
|
|
17764
18874
|
var runInit = async (agent, file) => {
|
|
17765
|
-
const path =
|
|
17766
|
-
if (
|
|
18875
|
+
const path = join39(process.cwd(), file);
|
|
18876
|
+
if (existsSync22(path)) {
|
|
17767
18877
|
logger.error(`${file} already exists — edit it directly, or pass --file to draft elsewhere.`);
|
|
17768
18878
|
process.exit(1);
|
|
17769
18879
|
}
|
|
@@ -17827,7 +18937,7 @@ var evalCommand = defineCommand8({
|
|
|
17827
18937
|
let result;
|
|
17828
18938
|
try {
|
|
17829
18939
|
const project = await scanProject(root, { mode: "build" });
|
|
17830
|
-
const evalsPath =
|
|
18940
|
+
const evalsPath = join39(root, args.file);
|
|
17831
18941
|
const { evals, raw } = await loadEvalsFile(evalsPath);
|
|
17832
18942
|
process.stderr.write(`${headerLine(evals.questions.length, agent)}
|
|
17833
18943
|
|
|
@@ -17901,10 +19011,10 @@ var evalCommand = defineCommand8({
|
|
|
17901
19011
|
// src/cli/commands/init.ts
|
|
17902
19012
|
import * as clack from "@clack/prompts";
|
|
17903
19013
|
import { defineCommand as defineCommand9 } from "citty";
|
|
17904
|
-
import { resolve as
|
|
19014
|
+
import { resolve as resolve12 } from "pathe";
|
|
17905
19015
|
|
|
17906
19016
|
// src/cli/init/questions.ts
|
|
17907
|
-
import { basename as basename6, resolve as
|
|
19017
|
+
import { basename as basename6, resolve as resolve11 } from "pathe";
|
|
17908
19018
|
var cancelled = (value) => typeof value === "symbol";
|
|
17909
19019
|
var collectAnswers = async (prompter, flags, defaults) => {
|
|
17910
19020
|
const directory = flags.directory ?? await prompter.text({
|
|
@@ -17915,7 +19025,7 @@ var collectAnswers = async (prompter, flags, defaults) => {
|
|
|
17915
19025
|
if (cancelled(directory)) {
|
|
17916
19026
|
return null;
|
|
17917
19027
|
}
|
|
17918
|
-
const root =
|
|
19028
|
+
const root = resolve11(defaults.cwd, directory);
|
|
17919
19029
|
const title = await prompter.text({
|
|
17920
19030
|
initialValue: titleize(basename6(root)),
|
|
17921
19031
|
message: "What's your docs site called?",
|
|
@@ -18084,7 +19194,7 @@ var initCommand = defineCommand9({
|
|
|
18084
19194
|
title: "My Docs"
|
|
18085
19195
|
};
|
|
18086
19196
|
}
|
|
18087
|
-
const root =
|
|
19197
|
+
const root = resolve12(cwd, answers.directory);
|
|
18088
19198
|
if (validateContentDir(root, answers.contentDir) !== undefined) {
|
|
18089
19199
|
logger.error(`Invalid --content-dir "${answers.contentDir}" (must be a path inside the project).`);
|
|
18090
19200
|
process.exit(1);
|
|
@@ -18114,7 +19224,7 @@ var initCommand = defineCommand9({
|
|
|
18114
19224
|
});
|
|
18115
19225
|
|
|
18116
19226
|
// src/cli/commands/mcp-stdio.ts
|
|
18117
|
-
import { readFile as
|
|
19227
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
18118
19228
|
import { defineCommand as defineCommand10 } from "citty";
|
|
18119
19229
|
|
|
18120
19230
|
// src/ai/mcp/stdio.ts
|
|
@@ -18140,6 +19250,22 @@ var SCHEMA = {
|
|
|
18140
19250
|
};
|
|
18141
19251
|
var BOOST = { description: 2, title: 3 };
|
|
18142
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
|
+
};
|
|
18143
19269
|
var segmentingTokenizer = (locale) => {
|
|
18144
19270
|
const language = locale?.toLowerCase().split(/[-_]/u)[0] ?? "";
|
|
18145
19271
|
if (!SEGMENTED_LANGUAGES.has(language)) {
|
|
@@ -18149,16 +19275,32 @@ var segmentingTokenizer = (locale) => {
|
|
|
18149
19275
|
return;
|
|
18150
19276
|
}
|
|
18151
19277
|
const segmenter = new Intl.Segmenter(language, { granularity: "word" });
|
|
19278
|
+
const bigram = BIGRAM_LANGUAGES.has(language);
|
|
18152
19279
|
return {
|
|
18153
19280
|
language,
|
|
18154
19281
|
normalizationCache: new Map,
|
|
18155
19282
|
tokenize: (raw) => {
|
|
18156
19283
|
const tokens = new Set;
|
|
19284
|
+
let run = "";
|
|
19285
|
+
const flush = () => {
|
|
19286
|
+
if (run) {
|
|
19287
|
+
addBigrams(run, tokens);
|
|
19288
|
+
run = "";
|
|
19289
|
+
}
|
|
19290
|
+
};
|
|
18157
19291
|
for (const segment of segmenter.segment(raw.toLowerCase())) {
|
|
18158
|
-
if (segment.isWordLike) {
|
|
18159
|
-
|
|
19292
|
+
if (!segment.isWordLike) {
|
|
19293
|
+
flush();
|
|
19294
|
+
continue;
|
|
19295
|
+
}
|
|
19296
|
+
if (bigram && BIGRAM_SCRIPTS.test(segment.segment)) {
|
|
19297
|
+
run += segment.segment;
|
|
19298
|
+
continue;
|
|
18160
19299
|
}
|
|
19300
|
+
flush();
|
|
19301
|
+
tokens.add(segment.segment);
|
|
18161
19302
|
}
|
|
19303
|
+
flush();
|
|
18162
19304
|
return [...tokens];
|
|
18163
19305
|
}
|
|
18164
19306
|
};
|
|
@@ -18172,14 +19314,18 @@ var buildOramaIndex = async (documents, locale) => {
|
|
|
18172
19314
|
await insertMultiple(db, documents);
|
|
18173
19315
|
return db;
|
|
18174
19316
|
};
|
|
19317
|
+
var ALL_TOKENS = 0;
|
|
18175
19318
|
var queryOramaIndex = async (db, term, limit, locale) => {
|
|
18176
|
-
const
|
|
19319
|
+
const params = {
|
|
18177
19320
|
boost: BOOST,
|
|
18178
19321
|
limit,
|
|
18179
19322
|
properties: ["title", "description", "content"],
|
|
18180
19323
|
term,
|
|
18181
19324
|
...locale ? { where: { locale: { eq: locale } } } : {}
|
|
18182
|
-
}
|
|
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);
|
|
18183
19329
|
return found.hits.map((hit) => hit.document);
|
|
18184
19330
|
};
|
|
18185
19331
|
|
|
@@ -18256,13 +19402,13 @@ var excerptFor = (doc) => {
|
|
|
18256
19402
|
const head = doc.content.slice(0, EXCERPT_LENGTH).trim();
|
|
18257
19403
|
return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;
|
|
18258
19404
|
};
|
|
18259
|
-
var
|
|
19405
|
+
var text2 = (value, isError = false) => ({
|
|
18260
19406
|
content: [{ text: value, type: "text" }],
|
|
18261
19407
|
...isError ? { isError: true } : {}
|
|
18262
19408
|
});
|
|
18263
19409
|
var createIndexProvider = (documents, locale) => {
|
|
18264
19410
|
let dbPromise = null;
|
|
18265
|
-
return ()
|
|
19411
|
+
return function provideIndex() {
|
|
18266
19412
|
dbPromise ??= buildOramaIndex(documents, locale);
|
|
18267
19413
|
return dbPromise;
|
|
18268
19414
|
};
|
|
@@ -18286,18 +19432,18 @@ var buildServer = (data, index) => {
|
|
|
18286
19432
|
title: doc.title,
|
|
18287
19433
|
url: urlFor(doc.route, data)
|
|
18288
19434
|
}));
|
|
18289
|
-
return
|
|
19435
|
+
return text2(JSON.stringify(results, null, 2));
|
|
18290
19436
|
}
|
|
18291
19437
|
if (name === "get_page") {
|
|
18292
19438
|
const key = normalizeRoute2(asString2(args.route), data);
|
|
18293
19439
|
const markdown = data.pages[key];
|
|
18294
19440
|
if (markdown === undefined) {
|
|
18295
|
-
return
|
|
19441
|
+
return text2(`No page found at "${key}". Use list_pages or search_docs to find valid routes.`, true);
|
|
18296
19442
|
}
|
|
18297
|
-
return
|
|
19443
|
+
return text2(markdown);
|
|
18298
19444
|
}
|
|
18299
19445
|
if (name === "list_pages") {
|
|
18300
|
-
return
|
|
19446
|
+
return text2(JSON.stringify(data.routes.map((route) => ({
|
|
18301
19447
|
contentType: route.contentType,
|
|
18302
19448
|
description: route.description,
|
|
18303
19449
|
lastModified: route.lastModified,
|
|
@@ -18307,9 +19453,9 @@ var buildServer = (data, index) => {
|
|
|
18307
19453
|
})), null, 2));
|
|
18308
19454
|
}
|
|
18309
19455
|
if (name === "get_navigation") {
|
|
18310
|
-
return
|
|
19456
|
+
return text2(JSON.stringify(data.navigation, null, 2));
|
|
18311
19457
|
}
|
|
18312
|
-
return
|
|
19458
|
+
return text2(`Unknown tool: ${name}`, true);
|
|
18313
19459
|
});
|
|
18314
19460
|
return server;
|
|
18315
19461
|
};
|
|
@@ -18341,7 +19487,7 @@ var mcpStdioCommand = defineCommand10({
|
|
|
18341
19487
|
async run({ args }) {
|
|
18342
19488
|
let data;
|
|
18343
19489
|
try {
|
|
18344
|
-
data = JSON.parse(await
|
|
19490
|
+
data = JSON.parse(await readFile22(args.data, "utf-8"));
|
|
18345
19491
|
} catch (error) {
|
|
18346
19492
|
const detail = error instanceof Error ? error.message : String(error);
|
|
18347
19493
|
process.stderr.write(`blume mcp-stdio: cannot load the snapshot at ${args.data}: ${detail}
|
|
@@ -18353,10 +19499,10 @@ var mcpStdioCommand = defineCommand10({
|
|
|
18353
19499
|
});
|
|
18354
19500
|
|
|
18355
19501
|
// src/cli/commands/preview.ts
|
|
18356
|
-
import { existsSync as
|
|
19502
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
18357
19503
|
import { preview } from "astro";
|
|
18358
19504
|
import { defineCommand as defineCommand11 } from "citty";
|
|
18359
|
-
import { join as
|
|
19505
|
+
import { join as join40 } from "pathe";
|
|
18360
19506
|
var previewCommand = defineCommand11({
|
|
18361
19507
|
args: {
|
|
18362
19508
|
host: { description: "Network host to bind.", type: "string" },
|
|
@@ -18370,7 +19516,7 @@ var previewCommand = defineCommand11({
|
|
|
18370
19516
|
const root = process.cwd();
|
|
18371
19517
|
const { config } = await loadConfig(root);
|
|
18372
19518
|
const context = resolveProjectContext(root, config);
|
|
18373
|
-
if (!
|
|
19519
|
+
if (!existsSync23(join40(context.outDir, "astro.config.mjs"))) {
|
|
18374
19520
|
logger.error("No build found. Run `blume build` first.");
|
|
18375
19521
|
process.exit(1);
|
|
18376
19522
|
}
|
|
@@ -18388,7 +19534,7 @@ var previewCommand = defineCommand11({
|
|
|
18388
19534
|
// src/cli/commands/sync.ts
|
|
18389
19535
|
import { rm as rm4 } from "node:fs/promises";
|
|
18390
19536
|
import { defineCommand as defineCommand12 } from "citty";
|
|
18391
|
-
import { join as
|
|
19537
|
+
import { join as join41 } from "pathe";
|
|
18392
19538
|
var syncCommand = defineCommand12({
|
|
18393
19539
|
args: {
|
|
18394
19540
|
force: {
|
|
@@ -18410,7 +19556,7 @@ var syncCommand = defineCommand12({
|
|
|
18410
19556
|
if (args.force) {
|
|
18411
19557
|
const { config } = await loadConfig(root);
|
|
18412
19558
|
const context = resolveProjectContext(root, config);
|
|
18413
|
-
await rm4(
|
|
19559
|
+
await rm4(join41(context.outDir, "cache"), { force: true, recursive: true });
|
|
18414
19560
|
logger.info("Cleared source cache.");
|
|
18415
19561
|
}
|
|
18416
19562
|
const lock = readDevLock(resolveRuntimeDir(root));
|
|
@@ -18428,13 +19574,13 @@ var syncCommand = defineCommand12({
|
|
|
18428
19574
|
});
|
|
18429
19575
|
|
|
18430
19576
|
// src/cli/commands/validate.ts
|
|
18431
|
-
import { existsSync as
|
|
19577
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
18432
19578
|
import { defineCommand as defineCommand13 } from "citty";
|
|
18433
|
-
import { join as
|
|
19579
|
+
import { join as join43 } from "pathe";
|
|
18434
19580
|
|
|
18435
19581
|
// src/core/links.ts
|
|
18436
|
-
import { existsSync as
|
|
18437
|
-
import { basename as basename7, join as
|
|
19582
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
19583
|
+
import { basename as basename7, join as join42 } from "pathe";
|
|
18438
19584
|
var HTTP = /^https?:\/\//iu;
|
|
18439
19585
|
var PROTOCOL_RELATIVE = /^\/\//u;
|
|
18440
19586
|
var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
|
|
@@ -18447,7 +19593,7 @@ var decodePercent = (value) => {
|
|
|
18447
19593
|
};
|
|
18448
19594
|
var DOC_EXT = /\.(?:md|mdx)$/iu;
|
|
18449
19595
|
var FILE_EXT = /\.[a-z0-9]+$/iu;
|
|
18450
|
-
var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null &&
|
|
19596
|
+
var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null && existsSync24(join42(ctx.publicDir, resolved));
|
|
18451
19597
|
var NUMERIC_PREFIX3 = /^\d+[-_.]/u;
|
|
18452
19598
|
var isIndexPage = (page2) => /^index\.(?:md|mdx)$/iu.test(basename7(page2.navPath).replace(NUMERIC_PREFIX3, ""));
|
|
18453
19599
|
var applyRelativePart = (segments, part) => {
|
|
@@ -18658,12 +19804,12 @@ var validateCommand = defineCommand13({
|
|
|
18658
19804
|
});
|
|
18659
19805
|
extraRoutes.push(...manifest.routes.flatMap((route) => route.fallback ? [route.path] : []));
|
|
18660
19806
|
}
|
|
18661
|
-
const publicDir =
|
|
19807
|
+
const publicDir = join43(root, "public");
|
|
18662
19808
|
diagnostics.push(...await validateLinks(project.graph, {
|
|
18663
19809
|
basePath: project.config.basePath,
|
|
18664
19810
|
checkExternal: Boolean(args.external),
|
|
18665
19811
|
extraRoutes,
|
|
18666
|
-
publicDir:
|
|
19812
|
+
publicDir: existsSync25(publicDir) ? publicDir : null,
|
|
18667
19813
|
redirects: project.config.redirects
|
|
18668
19814
|
}));
|
|
18669
19815
|
} catch (error) {
|
|
@@ -18727,5 +19873,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
18727
19873
|
});
|
|
18728
19874
|
runMain(main);
|
|
18729
19875
|
|
|
18730
|
-
//# debugId=
|
|
19876
|
+
//# debugId=CCE85D85BA5F467464756E2164756E21
|
|
18731
19877
|
//# sourceMappingURL=index.js.map
|