blume 1.4.1 → 1.4.3
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 +64 -0
- package/dist/cli/index.js +700 -579
- package/dist/cli/index.js.map +57 -56
- package/dist/types/core/base-path.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +8 -0
- package/dist/types/core/data.d.ts +10 -0
- package/dist/types/core/schema.d.ts +4 -0
- package/dist/types/core/sources/types.d.ts +9 -1
- package/dist/types/openapi/references.d.ts +8 -2
- package/docs/configuration/ai.mdx +41 -9
- package/docs/content/sources.mdx +1 -1
- package/package.json +17 -7
- package/src/ai/agent-readability.ts +3 -2
- package/src/ai/api-catalog.ts +2 -2
- package/src/ai/ask-context.ts +45 -12
- package/src/ai/link-headers.ts +7 -2
- package/src/ai/llms.ts +2 -1
- package/src/ai/mcp/discovery.ts +25 -6
- package/src/ai/mcp/server.ts +108 -98
- package/src/ai/tar.ts +29 -70
- package/src/astro/examples.ts +7 -3
- package/src/astro/generate.ts +63 -34
- package/src/astro/islands.ts +7 -3
- package/src/astro/templates.ts +36 -9
- package/src/audit/agent.ts +14 -29
- package/src/audit/crawl.ts +41 -16
- package/src/audit/run.ts +10 -3
- package/src/audit/snapshot.ts +27 -2
- package/src/cli/commands/audit.ts +12 -17
- package/src/cli/commands/build.ts +15 -7
- package/src/cli/commands/dev.ts +13 -15
- package/src/cli/commands/eject.ts +4 -4
- package/src/cli/commands/eval.ts +17 -27
- package/src/cli/env.ts +13 -30
- package/src/cli/init/scaffold.ts +21 -0
- package/src/cli/report-format.ts +22 -0
- package/src/components/content/AccordionItem.astro +2 -9
- package/src/components/content/ColorItem.astro +5 -13
- package/src/components/content/Component.astro +12 -8
- package/src/components/content/Frame.astro +2 -12
- package/src/components/content/Prompt.astro +12 -31
- package/src/components/content/Tab.astro +2 -9
- package/src/components/content/Tooltip.astro +1 -9
- package/src/components/content/Update.astro +2 -9
- package/src/components/content/inline-markdown.ts +28 -0
- package/src/components/copy-feedback.ts +96 -0
- package/src/components/islands/ask-ai.tsx +78 -9
- package/src/components/layout/PageActions.astro +20 -32
- package/src/components/layout/PageLayout.astro +8 -28
- package/src/components/layout/RootLayout.astro +47 -48
- package/src/components/layout/Search.astro +56 -9
- package/src/components/layout/drawer-inert.ts +31 -0
- package/src/components/layout/search/pagefind.ts +6 -5
- package/src/components/layout/search/types.ts +32 -0
- package/src/components/openapi/panel.ts +11 -8
- package/src/components/raf-throttle.ts +21 -0
- package/src/components/slug.ts +14 -0
- package/src/core/base-path.ts +18 -1
- package/src/core/config-input.ts +8 -0
- package/src/core/data.ts +7 -0
- package/src/core/frontmatter.ts +45 -1
- package/src/core/probe.ts +7 -19
- package/src/core/project-graph.ts +12 -1
- package/src/core/schema.ts +6 -0
- package/src/core/site-url.ts +27 -0
- package/src/core/sources/cache.ts +10 -8
- package/src/core/sources/github-releases.ts +21 -1
- package/src/core/sources/normalize.ts +26 -2
- package/src/core/sources/notion.ts +27 -5
- package/src/core/sources/portable-text.ts +16 -1
- package/src/core/sources/resolve.ts +1 -0
- package/src/core/sources/types.ts +13 -1
- package/src/deploy/cloudflare-negotiation.ts +15 -1
- package/src/deploy/robots.ts +2 -1
- package/src/deploy/rss.ts +2 -1
- package/src/deploy/sitemap.ts +56 -7
- package/src/eval/agents.ts +13 -10
- package/src/eval/report.ts +1 -14
- package/src/markdown/package-commands.ts +61 -54
- package/src/og/card.ts +24 -26
- package/src/openapi/model.ts +9 -9
- package/src/openapi/parse.ts +69 -28
- package/src/openapi/references.ts +35 -12
- package/src/openapi/render-mdx.ts +64 -25
- package/src/openapi/scalar.ts +2 -2
- package/src/openapi/source.ts +28 -1
- package/src/search/documents.ts +78 -34
- package/src/search/orama-index.ts +51 -12
- package/src/theme/palette.ts +6 -2
- package/src/translate/ledger.ts +4 -2
- package/src/translate/report.ts +1 -14
- package/src/translate/run.ts +20 -35
- package/src/cli/coalesce.ts +0 -43
package/dist/cli/index.js
CHANGED
|
@@ -537,9 +537,9 @@ Next steps:
|
|
|
537
537
|
import { defineCommand as defineCommand2 } from "citty";
|
|
538
538
|
|
|
539
539
|
// src/audit/agent.ts
|
|
540
|
-
import { spawn } from "node:child_process";
|
|
541
540
|
import { mkdtemp, writeFile as writeFile2 } from "node:fs/promises";
|
|
542
541
|
import { tmpdir } from "node:os";
|
|
542
|
+
import spawn from "cross-spawn";
|
|
543
543
|
import { join as join5 } from "pathe";
|
|
544
544
|
|
|
545
545
|
// src/audit/report.ts
|
|
@@ -1463,24 +1463,43 @@ Work through every finding:
|
|
|
1463
1463
|
3. Never fix a finding by deleting a page, removing content, or hiding it from the audit; if a finding genuinely needs a human decision, leave it and say so in your summary.
|
|
1464
1464
|
|
|
1465
1465
|
When you are done, run \`blume build\` and then \`blume audit\` to verify, and repeat until the audit reports no issues.`;
|
|
1466
|
-
var spawnAgent = (
|
|
1467
|
-
const child = spawn(
|
|
1466
|
+
var spawnAgent = (bin, args) => new Promise((resolve2, reject) => {
|
|
1467
|
+
const child = spawn(bin, args, { stdio: "inherit" });
|
|
1468
1468
|
child.once("error", reject);
|
|
1469
1469
|
child.once("close", (code) => resolve2(code ?? 1));
|
|
1470
1470
|
});
|
|
1471
|
-
var WINDOWS_COMMAND_NOT_FOUND = 9009;
|
|
1472
1471
|
var launchAgent = async (bin, prompt, platform = process.platform) => {
|
|
1473
1472
|
if (platform !== "win32") {
|
|
1474
|
-
return await spawnAgent(bin, [prompt]
|
|
1473
|
+
return await spawnAgent(bin, [prompt]);
|
|
1475
1474
|
}
|
|
1476
1475
|
const dir = await mkdtemp(join5(tmpdir(), "blume-audit-"));
|
|
1477
1476
|
const promptPath = join5(dir, "prompt.md");
|
|
1478
1477
|
await writeFile2(promptPath, prompt);
|
|
1479
|
-
return await spawnAgent(
|
|
1478
|
+
return await spawnAgent(bin, [
|
|
1479
|
+
`Read ${promptPath} and follow its instructions exactly.`
|
|
1480
|
+
]);
|
|
1480
1481
|
};
|
|
1481
1482
|
|
|
1482
1483
|
// src/audit/run.ts
|
|
1483
1484
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
1485
|
+
import pMap3 from "p-map";
|
|
1486
|
+
|
|
1487
|
+
// src/core/trim.ts
|
|
1488
|
+
var trimStart = (text, char) => {
|
|
1489
|
+
let start = 0;
|
|
1490
|
+
while (start < text.length && text[start] === char) {
|
|
1491
|
+
start += 1;
|
|
1492
|
+
}
|
|
1493
|
+
return text.slice(start);
|
|
1494
|
+
};
|
|
1495
|
+
var trimEnd = (text, char) => {
|
|
1496
|
+
let end = text.length;
|
|
1497
|
+
while (end > 0 && text[end - 1] === char) {
|
|
1498
|
+
end -= 1;
|
|
1499
|
+
}
|
|
1500
|
+
return text.slice(0, end);
|
|
1501
|
+
};
|
|
1502
|
+
var trimChar = (text, char) => trimEnd(trimStart(text, char), char);
|
|
1484
1503
|
|
|
1485
1504
|
// src/core/base-path.ts
|
|
1486
1505
|
var normalizeBasePath = (input) => {
|
|
@@ -1491,9 +1510,16 @@ var normalizeBasePath = (input) => {
|
|
|
1491
1510
|
return trimmed === "" ? "" : `/${trimmed}`;
|
|
1492
1511
|
};
|
|
1493
1512
|
var normalizePath = (path) => {
|
|
1494
|
-
const trimmed = path
|
|
1513
|
+
const trimmed = trimEnd(path, "/");
|
|
1495
1514
|
return trimmed === "" ? "/" : trimmed;
|
|
1496
1515
|
};
|
|
1516
|
+
var normalizeRoute = (input) => {
|
|
1517
|
+
const trimmed = trimEnd(input.trim(), "/");
|
|
1518
|
+
if (trimmed === "") {
|
|
1519
|
+
return "/";
|
|
1520
|
+
}
|
|
1521
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
1522
|
+
};
|
|
1497
1523
|
var isInternalPath = (target) => target.startsWith("/") && !target.startsWith("//");
|
|
1498
1524
|
var withBasePath = (basePath, route) => {
|
|
1499
1525
|
if (!basePath || !isInternalPath(route)) {
|
|
@@ -1709,7 +1735,28 @@ var withYamlEngine = (options) => ({
|
|
|
1709
1735
|
...options?.engines
|
|
1710
1736
|
}
|
|
1711
1737
|
});
|
|
1712
|
-
var
|
|
1738
|
+
var opensWithThematicBreak = (input) => {
|
|
1739
|
+
const [first = "", second] = input.split(/\r?\n/u, 2);
|
|
1740
|
+
if (!/^-{3}\s*$/u.test(first)) {
|
|
1741
|
+
return false;
|
|
1742
|
+
}
|
|
1743
|
+
if (second === undefined || second.trim() === "") {
|
|
1744
|
+
return true;
|
|
1745
|
+
}
|
|
1746
|
+
return !input.includes(`
|
|
1747
|
+
---`, 1);
|
|
1748
|
+
};
|
|
1749
|
+
var passthrough = (input) => ({
|
|
1750
|
+
content: input,
|
|
1751
|
+
data: {},
|
|
1752
|
+
excerpt: "",
|
|
1753
|
+
isEmpty: false,
|
|
1754
|
+
language: "",
|
|
1755
|
+
matter: "",
|
|
1756
|
+
orig: input,
|
|
1757
|
+
stringify: () => input
|
|
1758
|
+
});
|
|
1759
|
+
var matter = Object.assign((input, options) => typeof input === "string" && opensWithThematicBreak(input) ? passthrough(input) : baseMatter(input, withYamlEngine(options)), baseMatter, {
|
|
1713
1760
|
read: (filepath, options) => baseMatter.read(filepath, withYamlEngine(options)),
|
|
1714
1761
|
stringify: (file, data, options) => baseMatter.stringify(file, data, withYamlEngine(options))
|
|
1715
1762
|
});
|
|
@@ -2428,6 +2475,7 @@ var llmsChecks = {
|
|
|
2428
2475
|
};
|
|
2429
2476
|
|
|
2430
2477
|
// src/core/probe.ts
|
|
2478
|
+
import pMap from "p-map";
|
|
2431
2479
|
var PROBE_CONCURRENCY = 8;
|
|
2432
2480
|
var PROBE_TIMEOUT_MS = 1e4;
|
|
2433
2481
|
var STATUS_NOT_FOUND = 404;
|
|
@@ -2489,20 +2537,8 @@ var gradeExternal = (result) => {
|
|
|
2489
2537
|
};
|
|
2490
2538
|
var probeAll = async (urls, options = {}) => {
|
|
2491
2539
|
const unique = [...new Set(urls)];
|
|
2492
|
-
const
|
|
2493
|
-
|
|
2494
|
-
let cursor = 0;
|
|
2495
|
-
const worker = async () => {
|
|
2496
|
-
while (cursor < unique.length) {
|
|
2497
|
-
const url = unique[cursor];
|
|
2498
|
-
cursor += 1;
|
|
2499
|
-
if (url !== undefined) {
|
|
2500
|
-
results.set(url, await probe(url, options));
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
};
|
|
2504
|
-
await Promise.all(Array.from({ length: limit }, worker));
|
|
2505
|
-
return results;
|
|
2540
|
+
const entries = await pMap(unique, async (url) => [url, await probe(url, options)], { concurrency: Math.max(1, options.concurrency ?? PROBE_CONCURRENCY) });
|
|
2541
|
+
return new Map(entries);
|
|
2506
2542
|
};
|
|
2507
2543
|
|
|
2508
2544
|
// src/audit/checks/network.ts
|
|
@@ -2996,6 +3032,8 @@ var urlChecks = {
|
|
|
2996
3032
|
// src/audit/crawl.ts
|
|
2997
3033
|
import { readFile as readFile3, stat } from "node:fs/promises";
|
|
2998
3034
|
import { XMLParser } from "fast-xml-parser";
|
|
3035
|
+
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
3036
|
+
import pMap2 from "p-map";
|
|
2999
3037
|
import { join as join9, relative as relative6 } from "pathe";
|
|
3000
3038
|
import { glob } from "tinyglobby";
|
|
3001
3039
|
|
|
@@ -3057,32 +3095,19 @@ var askBackendRuntimeDep = (ask) => {
|
|
|
3057
3095
|
return ASK_PRESETS[provider]?.runtimeDep ?? OPENAI_COMPATIBLE_DEP;
|
|
3058
3096
|
};
|
|
3059
3097
|
|
|
3060
|
-
// src/core/
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
}
|
|
3066
|
-
return text.slice(start);
|
|
3098
|
+
// src/core/site-url.ts
|
|
3099
|
+
import { joinURL } from "ufo";
|
|
3100
|
+
var siteRoot = (site) => trimEnd(site, "/");
|
|
3101
|
+
var absoluteUrl = (site, path) => {
|
|
3102
|
+
const root = siteRoot(site);
|
|
3103
|
+
return path === "/" ? `${root}/` : joinURL(root, path);
|
|
3067
3104
|
};
|
|
3068
|
-
var trimEnd = (text, char) => {
|
|
3069
|
-
let end = text.length;
|
|
3070
|
-
while (end > 0 && text[end - 1] === char) {
|
|
3071
|
-
end -= 1;
|
|
3072
|
-
}
|
|
3073
|
-
return text.slice(0, end);
|
|
3074
|
-
};
|
|
3075
|
-
var trimChar = (text, char) => trimEnd(trimStart(text, char), char);
|
|
3076
3105
|
|
|
3077
3106
|
// src/openapi/references.ts
|
|
3078
|
-
var NON_SLUG = /[
|
|
3079
|
-
var
|
|
3080
|
-
var
|
|
3081
|
-
|
|
3082
|
-
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
3083
|
-
const noTrailing = trimEnd(withSlash, "/");
|
|
3084
|
-
return noTrailing === "" ? "/" : noTrailing;
|
|
3085
|
-
};
|
|
3107
|
+
var NON_SLUG = /[^\p{L}\p{M}\p{N}]+/gu;
|
|
3108
|
+
var FORMAT_CHARS = /\p{Cf}/gu;
|
|
3109
|
+
var LEADING_MARKS = /^\p{M}+/u;
|
|
3110
|
+
var slugify = (text) => trimChar(text.normalize("NFC").toLowerCase().replace(FORMAT_CHARS, "").replace(NON_SLUG, "-"), "-").replace(LEADING_MARKS, "");
|
|
3086
3111
|
var routeSlug = (route) => slugify(trimChar(route, "/")) || "reference";
|
|
3087
3112
|
var sourcesOf = (block) => {
|
|
3088
3113
|
const sources = [...block.sources];
|
|
@@ -3182,7 +3207,7 @@ var linksetEntries = (config) => {
|
|
|
3182
3207
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
3183
3208
|
const abs = (path) => {
|
|
3184
3209
|
const based = withBasePath(deployBase, path);
|
|
3185
|
-
return site ?
|
|
3210
|
+
return site ? absoluteUrl(site, based) : based;
|
|
3186
3211
|
};
|
|
3187
3212
|
const entries = [];
|
|
3188
3213
|
for (const reference of resolveReferences(config)) {
|
|
@@ -3989,7 +4014,11 @@ ${stagedBlock}
|
|
|
3989
4014
|
export const collections = { docs${options.staged ? ", staged" : ""} };
|
|
3990
4015
|
`;
|
|
3991
4016
|
};
|
|
3992
|
-
var
|
|
4017
|
+
var ASK_FALLBACK_PROMPT = "You are a helpful documentation assistant. Answer using the project's documentation.";
|
|
4018
|
+
var askEndpointTemplate = (backend, grounded, instructions) => {
|
|
4019
|
+
const fallbackPrompt = instructions ? `${ASK_FALLBACK_PROMPT}
|
|
4020
|
+
|
|
4021
|
+
${instructions}` : ASK_FALLBACK_PROMPT;
|
|
3993
4022
|
const imports = [
|
|
3994
4023
|
'import type { APIRoute } from "astro";',
|
|
3995
4024
|
'import { streamText } from "ai";'
|
|
@@ -4015,8 +4044,9 @@ const provider = createOpenAICompatible({
|
|
|
4015
4044
|
}
|
|
4016
4045
|
if (grounded) {
|
|
4017
4046
|
imports.push('import { createAskContext } from "blume/ai/ask-context.ts";', 'import askData from "../../generated/ask-data.json";');
|
|
4047
|
+
const groundOptions = instructions ? `, { instructions: ${JSON.stringify(instructions)} }` : "";
|
|
4018
4048
|
setup += `
|
|
4019
|
-
const ground = createAskContext(askData);
|
|
4049
|
+
const ground = createAskContext(askData${groundOptions});
|
|
4020
4050
|
`;
|
|
4021
4051
|
}
|
|
4022
4052
|
const validate = ` const body = await request.json().catch(() => null);
|
|
@@ -4061,7 +4091,7 @@ const ground = createAskContext(askData);
|
|
|
4061
4091
|
},`;
|
|
4062
4092
|
const stream = grounded ? ` const instructions =
|
|
4063
4093
|
(await ground(messages, body.page)) ??
|
|
4064
|
-
|
|
4094
|
+
${JSON.stringify(fallbackPrompt)};
|
|
4065
4095
|
const result = streamText({
|
|
4066
4096
|
model: ${modelExpr},
|
|
4067
4097
|
instructions,
|
|
@@ -4070,7 +4100,7 @@ ${onError}
|
|
|
4070
4100
|
});` : ` const result = streamText({
|
|
4071
4101
|
model: ${modelExpr},
|
|
4072
4102
|
instructions:
|
|
4073
|
-
|
|
4103
|
+
${JSON.stringify(fallbackPrompt)},
|
|
4074
4104
|
messages,
|
|
4075
4105
|
${onError}
|
|
4076
4106
|
});`;
|
|
@@ -4252,7 +4282,7 @@ export function GET({ props }: { props: { route: string } }) {
|
|
|
4252
4282
|
var contentAssetsEndpointTemplate = (stagedAssetsDir) => `// Generated by Blume. Do not edit.
|
|
4253
4283
|
import { existsSync } from "node:fs";
|
|
4254
4284
|
import { readdir, readFile } from "node:fs/promises";
|
|
4255
|
-
import { join, relative, resolve } from "node:path";
|
|
4285
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
4256
4286
|
import type { APIRoute } from "astro";
|
|
4257
4287
|
import assets from "../../generated/content-assets.json";
|
|
4258
4288
|
|
|
@@ -4312,7 +4342,13 @@ const resolveAsset = (asset: string): string | null => {
|
|
|
4312
4342
|
const abs = resolve(STAGED_DIR, asset);
|
|
4313
4343
|
// Traversal guard: the dev server renders on demand, so the param is
|
|
4314
4344
|
// attacker-controlled there — never step outside the staged directory.
|
|
4315
|
-
|
|
4345
|
+
// path.relative rather than a string-prefix test: STAGED_DIR is baked in
|
|
4346
|
+
// with forward slashes while resolve() answers in the platform's
|
|
4347
|
+
// separators, so on Windows the prefix test 404'd every legitimate staged
|
|
4348
|
+
// asset — and a bare prefix also admits a sibling directory that merely
|
|
4349
|
+
// shares the name.
|
|
4350
|
+
const rel = relative(STAGED_DIR, abs);
|
|
4351
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
|
|
4316
4352
|
return null;
|
|
4317
4353
|
}
|
|
4318
4354
|
return existsSync(abs) ? abs : null;
|
|
@@ -4617,8 +4653,11 @@ const frontmatter = entry.data ?? {};
|
|
|
4617
4653
|
const seo = frontmatter.seo ?? {};
|
|
4618
4654
|
const base = data.config.site ? data.config.site.replace(/\\/$/, "") : null;
|
|
4619
4655
|
|
|
4656
|
+
// Percent-encode the route-derived path (the sitemap convention): a Unicode
|
|
4657
|
+
// slug (\`/api/größe\`) is not legal in a raw URI, and crawlers compare
|
|
4658
|
+
// canonical against the sitemap's encoded <loc> byte-for-byte.
|
|
4620
4659
|
const ogPath = data.config.og.enabled
|
|
4621
|
-
? \`/og/\${route === "/" ? "index" : route.slice(1)}.png\`
|
|
4660
|
+
? encodeURI(\`/og/\${route === "/" ? "index" : route.slice(1)}.png\`)
|
|
4622
4661
|
: null;
|
|
4623
4662
|
const ogRel = seo.image ?? ogPath;
|
|
4624
4663
|
// Absolute URLs also carry the deployment base (the page is served under it):
|
|
@@ -4637,7 +4676,7 @@ const x = { ...data.config.x, ...(seo.x?.creator ? { creator: seo.x.creator } :
|
|
|
4637
4676
|
const basedRoute = withBase(route);
|
|
4638
4677
|
const canonical =
|
|
4639
4678
|
seo.canonical ??
|
|
4640
|
-
(base ? \`\${base}\${basedRoute === "/" ? "" : basedRoute}\` : null);
|
|
4679
|
+
(base ? \`\${base}\${basedRoute === "/" ? "" : encodeURI(basedRoute)}\` : null);
|
|
4641
4680
|
|
|
4642
4681
|
// Locale resolution. With i18n on, pick the active locale's nav + dictionary,
|
|
4643
4682
|
// build hreflang alternates, and derive the language-switcher targets.
|
|
@@ -4738,6 +4777,7 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
4738
4777
|
exportPdf={${options.exportPdf}}
|
|
4739
4778
|
exportEpub={${options.exportEpub}}
|
|
4740
4779
|
feeds={data.feeds}
|
|
4780
|
+
discovery={data.config.discovery}
|
|
4741
4781
|
siteUrl={data.config.site}
|
|
4742
4782
|
pageType={frontmatter.type}
|
|
4743
4783
|
published={frontmatter.date ?? frontmatter.changelog?.date ?? null}
|
|
@@ -4940,6 +4980,7 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
4940
4980
|
exportPdf={${options.exportPdf}}
|
|
4941
4981
|
exportEpub={${options.exportEpub}}
|
|
4942
4982
|
feeds={data.feeds}
|
|
4983
|
+
discovery={data.config.discovery}
|
|
4943
4984
|
siteUrl={data.config.site}
|
|
4944
4985
|
noindex={false}
|
|
4945
4986
|
structuredDataEnabled={data.config.structuredData}
|
|
@@ -5280,6 +5321,21 @@ var visibleText = (element) => {
|
|
|
5280
5321
|
|
|
5281
5322
|
// src/audit/snapshot.ts
|
|
5282
5323
|
var CHROME = "nav, aside, header, footer";
|
|
5324
|
+
var countWords = (prose, lang) => {
|
|
5325
|
+
let segmenter;
|
|
5326
|
+
try {
|
|
5327
|
+
segmenter = new Intl.Segmenter(lang ?? undefined, { granularity: "word" });
|
|
5328
|
+
} catch {
|
|
5329
|
+
segmenter = new Intl.Segmenter(undefined, { granularity: "word" });
|
|
5330
|
+
}
|
|
5331
|
+
let count = 0;
|
|
5332
|
+
for (const segment of segmenter.segment(prose)) {
|
|
5333
|
+
if (segment.isWordLike) {
|
|
5334
|
+
count += 1;
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
return count;
|
|
5338
|
+
};
|
|
5283
5339
|
var CONTENT_ROOTS = ["main", "article", "body"];
|
|
5284
5340
|
var contentRoot = (document) => {
|
|
5285
5341
|
for (const selector of CONTENT_ROOTS) {
|
|
@@ -5339,6 +5395,7 @@ var buildSnapshot = (options) => {
|
|
|
5339
5395
|
const root = contentRoot(document);
|
|
5340
5396
|
const prose = root ? visibleText(root) : "";
|
|
5341
5397
|
const robots = document.querySelector('meta[name="robots"]');
|
|
5398
|
+
const lang = attr(document.querySelector("html") ?? document, "lang");
|
|
5342
5399
|
const { jsonld, jsonldErrors } = collectJsonLd(document);
|
|
5343
5400
|
return {
|
|
5344
5401
|
bytes: Buffer.byteLength(html, "utf-8"),
|
|
@@ -5359,7 +5416,7 @@ var buildSnapshot = (options) => {
|
|
|
5359
5416
|
indexable: !robots?.getAttribute("content")?.includes("noindex"),
|
|
5360
5417
|
jsonld,
|
|
5361
5418
|
jsonldErrors,
|
|
5362
|
-
lang
|
|
5419
|
+
lang,
|
|
5363
5420
|
links,
|
|
5364
5421
|
metaRefresh: document.querySelector('meta[http-equiv="refresh" i]')?.getAttribute("content")?.trim() ?? null,
|
|
5365
5422
|
og: prefixedMeta(document, "property", "og:"),
|
|
@@ -5372,12 +5429,13 @@ var buildSnapshot = (options) => {
|
|
|
5372
5429
|
twitter: prefixedMeta(document, "name", "twitter:"),
|
|
5373
5430
|
url,
|
|
5374
5431
|
viewport: document.querySelector('meta[name="viewport"]')?.getAttribute("content")?.trim() ?? null,
|
|
5375
|
-
wordCount: prose
|
|
5432
|
+
wordCount: countWords(prose, lang)
|
|
5376
5433
|
};
|
|
5377
5434
|
};
|
|
5378
5435
|
|
|
5379
5436
|
// src/audit/crawl.ts
|
|
5380
5437
|
var EXAMPLES_PREFIX = `${examplesRouteBase("")}/`;
|
|
5438
|
+
var CRAWL_CONCURRENCY = 16;
|
|
5381
5439
|
var fileToUrl = (staticDir, file) => {
|
|
5382
5440
|
const rel = relative6(staticDir, file).replaceAll("\\", "/");
|
|
5383
5441
|
const path = rel.replace(/(?:^|\/)index\.html$/u, "").replace(/\.html$/u, "");
|
|
@@ -5385,11 +5443,11 @@ var fileToUrl = (staticDir, file) => {
|
|
|
5385
5443
|
};
|
|
5386
5444
|
var indexFiles = async (staticDir) => {
|
|
5387
5445
|
const found = await glob("**/*", { cwd: staticDir, dot: true });
|
|
5388
|
-
const sized = await
|
|
5446
|
+
const sized = await pMap2(found, async (file) => {
|
|
5389
5447
|
const path = `/${file.replaceAll("\\", "/")}`;
|
|
5390
5448
|
const info = await stat(join9(staticDir, file));
|
|
5391
5449
|
return [path, info.size];
|
|
5392
|
-
})
|
|
5450
|
+
}, { concurrency: CRAWL_CONCURRENCY });
|
|
5393
5451
|
return new Map(sized);
|
|
5394
5452
|
};
|
|
5395
5453
|
var routeIndex = (manifest, basePath) => {
|
|
@@ -5442,15 +5500,17 @@ var parseSitemap = (file, xml, bytes) => {
|
|
|
5442
5500
|
};
|
|
5443
5501
|
var parseLlms = (file, text) => {
|
|
5444
5502
|
const entries = [];
|
|
5445
|
-
const
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5503
|
+
const collect = (node) => {
|
|
5504
|
+
if ((node.type === "link" || node.type === "image" || node.type === "definition") && node.url) {
|
|
5505
|
+
entries.push({ line: node.position?.start.line ?? 1, url: node.url });
|
|
5506
|
+
}
|
|
5507
|
+
if ("children" in node) {
|
|
5508
|
+
for (const child of node.children) {
|
|
5509
|
+
collect(child);
|
|
5451
5510
|
}
|
|
5452
5511
|
}
|
|
5453
|
-
}
|
|
5512
|
+
};
|
|
5513
|
+
collect(fromMarkdown(text));
|
|
5454
5514
|
return { entries, file };
|
|
5455
5515
|
};
|
|
5456
5516
|
var ROBOTS_DIRECTIVE = /^(?<field>[a-z-]+)\s*:\s*(?<value>.*)$/iu;
|
|
@@ -5486,7 +5546,7 @@ var crawlStaticDir = async (options) => {
|
|
|
5486
5546
|
const { staticDir, manifest, basePath } = options;
|
|
5487
5547
|
const routes = routeIndex(manifest, basePath);
|
|
5488
5548
|
const htmlFiles = await glob("**/*.html", { absolute: true, cwd: staticDir });
|
|
5489
|
-
const snapshots = await
|
|
5549
|
+
const snapshots = await pMap2(htmlFiles.toSorted(), async (file) => {
|
|
5490
5550
|
const url = fileToUrl(staticDir, file);
|
|
5491
5551
|
if (stripBasePath(basePath, url).startsWith(EXAMPLES_PREFIX)) {
|
|
5492
5552
|
return null;
|
|
@@ -5501,7 +5561,7 @@ var crawlStaticDir = async (options) => {
|
|
|
5501
5561
|
route: routes.get(url) ?? routes.get(stripBasePath(basePath, url)),
|
|
5502
5562
|
url
|
|
5503
5563
|
});
|
|
5504
|
-
})
|
|
5564
|
+
}, { concurrency: CRAWL_CONCURRENCY });
|
|
5505
5565
|
const pages = snapshots.filter((page) => page !== null);
|
|
5506
5566
|
const sitemapFile = join9(staticDir, "sitemap.xml");
|
|
5507
5567
|
const sitemapXml = await readIfPresent(sitemapFile);
|
|
@@ -5598,17 +5658,18 @@ class NoBuildError extends Error {
|
|
|
5598
5658
|
this.staticDir = staticDir;
|
|
5599
5659
|
}
|
|
5600
5660
|
}
|
|
5661
|
+
var READ_CONCURRENCY = 16;
|
|
5601
5662
|
var readSources = async (pages) => {
|
|
5602
5663
|
const paths = [
|
|
5603
5664
|
...new Set(pages.flatMap((page) => page.source ? [page.source] : []))
|
|
5604
5665
|
];
|
|
5605
|
-
const entries = await
|
|
5666
|
+
const entries = await pMap3(paths, async (path) => {
|
|
5606
5667
|
try {
|
|
5607
5668
|
return [path, await readFile4(path, "utf-8")];
|
|
5608
5669
|
} catch {
|
|
5609
5670
|
return null;
|
|
5610
5671
|
}
|
|
5611
|
-
})
|
|
5672
|
+
}, { concurrency: READ_CONCURRENCY });
|
|
5612
5673
|
return new Map(entries.filter((entry) => entry !== null));
|
|
5613
5674
|
};
|
|
5614
5675
|
var matches = (id, terms) => {
|
|
@@ -9518,6 +9579,7 @@ var sanitySourceSchema = z2.object({
|
|
|
9518
9579
|
type: z2.literal("sanity")
|
|
9519
9580
|
});
|
|
9520
9581
|
var notionSourceSchema = z2.object({
|
|
9582
|
+
concurrency: z2.number().positive().optional(),
|
|
9521
9583
|
database: z2.string(),
|
|
9522
9584
|
pollInterval: z2.number().positive().optional(),
|
|
9523
9585
|
prefix: z2.string().optional(),
|
|
@@ -9773,6 +9835,7 @@ var aiConfigSchema = z2.strictObject({
|
|
|
9773
9835
|
baseUrl: z2.url().optional(),
|
|
9774
9836
|
enabled: z2.boolean().default(false),
|
|
9775
9837
|
endpoint: askEndpointSchema.optional(),
|
|
9838
|
+
instructions: z2.string().trim().min(1).optional(),
|
|
9776
9839
|
model: z2.string().default("openai/gpt-5.5"),
|
|
9777
9840
|
provider: z2.enum(askAiProviders).default("gateway"),
|
|
9778
9841
|
suggestions: z2.array(z2.strictObject({
|
|
@@ -11230,9 +11293,10 @@ var GROUP_FOLDER2 = /^\((?<label>.+)\)$/u;
|
|
|
11230
11293
|
var WORD_SPLIT2 = /[-_]/u;
|
|
11231
11294
|
var stripNumericPrefix = (segment) => segment.replace(NUMERIC_PREFIX2, "");
|
|
11232
11295
|
var groupLabel = (segment) => segment.match(GROUP_FOLDER2)?.groups?.label ?? null;
|
|
11233
|
-
var slugify2 = (text) => text.toLowerCase().trim().replaceAll(/[^\
|
|
11296
|
+
var slugify2 = (text) => text.normalize("NFC").toLowerCase().trim().replaceAll(/[^\p{L}\p{M}\p{N}\s_-]/gu, "").replaceAll(/[\s_]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "");
|
|
11234
11297
|
var slugifyPath = (text) => text.split("/").map(slugify2).filter(Boolean).join("/");
|
|
11235
11298
|
var titleCase = (value) => value.split(WORD_SPLIT2).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
11299
|
+
var sanitizeSegment = (segment) => segment.replaceAll(/[:\p{Cc}]/gu, "");
|
|
11236
11300
|
var addRouteSegment = (part, segments, groups) => {
|
|
11237
11301
|
if (part === "") {
|
|
11238
11302
|
return;
|
|
@@ -11246,7 +11310,11 @@ var addRouteSegment = (part, segments, groups) => {
|
|
|
11246
11310
|
if (clean === "index") {
|
|
11247
11311
|
return;
|
|
11248
11312
|
}
|
|
11249
|
-
|
|
11313
|
+
const safe = sanitizeSegment(clean);
|
|
11314
|
+
if (safe === "") {
|
|
11315
|
+
return;
|
|
11316
|
+
}
|
|
11317
|
+
segments.push(safe);
|
|
11250
11318
|
};
|
|
11251
11319
|
var mapRoute = (relativePath) => {
|
|
11252
11320
|
const withoutExt = relativePath.slice(0, relativePath.length - extname2(relativePath).length);
|
|
@@ -11576,15 +11644,10 @@ var normalizeEntry = (entry, ctx) => {
|
|
|
11576
11644
|
import { isAbsolute as isAbsolute5, join as join17, resolve as resolve4 } from "pathe";
|
|
11577
11645
|
|
|
11578
11646
|
// src/core/sources/cache.ts
|
|
11647
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
11579
11648
|
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
|
|
11580
11649
|
import { join as join11 } from "pathe";
|
|
11581
|
-
var hashText = (text) =>
|
|
11582
|
-
let hash = 5381;
|
|
11583
|
-
for (let i = 0;i < text.length; i += 1) {
|
|
11584
|
-
hash = (hash * 33 + (text.codePointAt(i) ?? 0)) % 2147483647;
|
|
11585
|
-
}
|
|
11586
|
-
return hash.toString(36);
|
|
11587
|
-
};
|
|
11650
|
+
var hashText = (text) => createHash2("sha256").update(text).digest("hex").slice(0, 16);
|
|
11588
11651
|
var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
|
|
11589
11652
|
var pollingWatch = (load2, intervalSeconds, seed) => (onChange) => {
|
|
11590
11653
|
let last = "";
|
|
@@ -11661,9 +11724,6 @@ var loadWithCache = async (name, cache, fetchEntries, refresh = true) => {
|
|
|
11661
11724
|
};
|
|
11662
11725
|
|
|
11663
11726
|
// src/openapi/model.ts
|
|
11664
|
-
var NON_SLUG2 = /[^a-z0-9]+/gu;
|
|
11665
|
-
var SLUG_EDGES = /^-+|-+$/gu;
|
|
11666
|
-
var slugify3 = (text) => text.toLowerCase().replace(NON_SLUG2, "-").replace(SLUG_EDGES, "");
|
|
11667
11727
|
var HTTP_METHODS = [
|
|
11668
11728
|
"get",
|
|
11669
11729
|
"put",
|
|
@@ -11676,8 +11736,8 @@ var HTTP_METHODS = [
|
|
|
11676
11736
|
];
|
|
11677
11737
|
var UNTAGGED = "Operations";
|
|
11678
11738
|
var operationKey = (method, path, operationId) => {
|
|
11679
|
-
const fromId = operationId ?
|
|
11680
|
-
return fromId ||
|
|
11739
|
+
const fromId = operationId ? slugify(operationId) : "";
|
|
11740
|
+
return fromId || slugify(`${method}-${path}`);
|
|
11681
11741
|
};
|
|
11682
11742
|
var isOperation = (value) => typeof value === "object" && value !== null;
|
|
11683
11743
|
var tagSlugger = () => {
|
|
@@ -11688,7 +11748,7 @@ var tagSlugger = () => {
|
|
|
11688
11748
|
if (existing) {
|
|
11689
11749
|
return existing;
|
|
11690
11750
|
}
|
|
11691
|
-
const base =
|
|
11751
|
+
const base = slugify(name) || "operations";
|
|
11692
11752
|
let slug = base;
|
|
11693
11753
|
for (let suffix = 2;taken.has(slug); suffix += 1) {
|
|
11694
11754
|
slug = `${base}-${suffix}`;
|
|
@@ -11757,6 +11817,7 @@ var extractOperations = (document, baseRoute) => {
|
|
|
11757
11817
|
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
11758
11818
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
11759
11819
|
import { normalize as normalize2, upgrade } from "@scalar/openapi-parser";
|
|
11820
|
+
import pRetry, { AbortError } from "p-retry";
|
|
11760
11821
|
import { isAbsolute as isAbsolute3, join as join12 } from "pathe";
|
|
11761
11822
|
var URL_SPEC = /^https?:\/\//u;
|
|
11762
11823
|
var FETCH_TIMEOUT_MS = 15000;
|
|
@@ -11794,8 +11855,15 @@ var ensureProxyDispatcher = async () => {
|
|
|
11794
11855
|
};
|
|
11795
11856
|
var retryAfterMs = (response) => {
|
|
11796
11857
|
const header = response.headers.get("retry-after");
|
|
11797
|
-
|
|
11798
|
-
|
|
11858
|
+
if (!header) {
|
|
11859
|
+
return;
|
|
11860
|
+
}
|
|
11861
|
+
const seconds = Number(header);
|
|
11862
|
+
if (Number.isFinite(seconds)) {
|
|
11863
|
+
return seconds > 0 ? seconds * SECOND_MS : undefined;
|
|
11864
|
+
}
|
|
11865
|
+
const delta = Date.parse(header) - Date.now();
|
|
11866
|
+
return Number.isFinite(delta) && delta > 0 ? delta : undefined;
|
|
11799
11867
|
};
|
|
11800
11868
|
var attemptFetch = async (spec) => {
|
|
11801
11869
|
try {
|
|
@@ -11822,25 +11890,37 @@ var attemptFetch = async (spec) => {
|
|
|
11822
11890
|
};
|
|
11823
11891
|
}
|
|
11824
11892
|
};
|
|
11893
|
+
var retryableFetchError = (error, retryAfter) => {
|
|
11894
|
+
const wrapper = new Error(error.message, {
|
|
11895
|
+
cause: error
|
|
11896
|
+
});
|
|
11897
|
+
wrapper.name = "RetryableFetchError";
|
|
11898
|
+
wrapper.retryAfter = retryAfter;
|
|
11899
|
+
return wrapper;
|
|
11900
|
+
};
|
|
11825
11901
|
var fetchSpecText = async (spec) => {
|
|
11826
11902
|
await ensureProxyDispatcher();
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11903
|
+
return await pRetry(async () => {
|
|
11904
|
+
const attempt = await attemptFetch(spec);
|
|
11905
|
+
if ("text" in attempt) {
|
|
11906
|
+
return attempt.text;
|
|
11907
|
+
}
|
|
11908
|
+
if (!attempt.retryable) {
|
|
11909
|
+
throw new AbortError(attempt.error);
|
|
11910
|
+
}
|
|
11911
|
+
throw retryableFetchError(attempt.error, attempt.retryAfter);
|
|
11912
|
+
}, {
|
|
11913
|
+
factor: 2,
|
|
11914
|
+
maxTimeout: MAX_RETRY_WAIT_MS,
|
|
11915
|
+
minTimeout: BASE_BACKOFF_MS,
|
|
11916
|
+
onFailedAttempt: async (context) => {
|
|
11917
|
+
const { retryAfter } = context.error;
|
|
11918
|
+
if (retryAfter !== undefined && context.retriesLeft > 0) {
|
|
11919
|
+
await sleep(Math.max(0, Math.min(retryAfter, MAX_RETRY_WAIT_MS) - context.retryDelay));
|
|
11920
|
+
}
|
|
11921
|
+
},
|
|
11922
|
+
retries: MAX_ATTEMPTS - 1
|
|
11923
|
+
});
|
|
11844
11924
|
};
|
|
11845
11925
|
var cacheFileFor = (cacheDir, spec) => join12(cacheDir, `spec-${hashText(spec)}.cache`);
|
|
11846
11926
|
var readCache = async (file) => {
|
|
@@ -11900,6 +11980,8 @@ var parseSpec = async (spec, root, options = {}) => {
|
|
|
11900
11980
|
};
|
|
11901
11981
|
|
|
11902
11982
|
// src/openapi/render-mdx.ts
|
|
11983
|
+
import { fromMarkdown as fromMarkdown2 } from "mdast-util-from-markdown";
|
|
11984
|
+
import { toString as mdastToString } from "mdast-util-to-string";
|
|
11903
11985
|
var MDX_UNSAFE = /[<{}]/gu;
|
|
11904
11986
|
var ENTITIES = {
|
|
11905
11987
|
"<": "<",
|
|
@@ -11907,26 +11989,46 @@ var ENTITIES = {
|
|
|
11907
11989
|
"}": "}"
|
|
11908
11990
|
};
|
|
11909
11991
|
var MDX_ESM_KEYWORD = /^(?<keyword>import|export)\b/gmu;
|
|
11910
|
-
var BACKTICK_CODE = /(?<!`)(?<bt>`+)(?!`)[\s\S]*?(?<!`)\k<bt>(?!`)/gu;
|
|
11911
11992
|
var escapeProse = (text) => text.replace(MDX_UNSAFE, (char) => ENTITIES[char] ?? char).replace(MDX_ESM_KEYWORD, (keyword) => `&#${keyword.codePointAt(0)};${keyword.slice(1)}`);
|
|
11993
|
+
var codeSpans = (text) => {
|
|
11994
|
+
const spans = [];
|
|
11995
|
+
const collect = (node) => {
|
|
11996
|
+
if (node.type === "inlineCode" || node.type === "code") {
|
|
11997
|
+
const start = node.position?.start.offset ?? -1;
|
|
11998
|
+
const end = node.position?.end.offset ?? -1;
|
|
11999
|
+
const head = text.slice(Math.max(start, 0), Math.max(end, 0)).trimStart();
|
|
12000
|
+
if (start >= 0 && (node.type === "inlineCode" || head.startsWith("`") || head.startsWith("~"))) {
|
|
12001
|
+
spans.push([start, end]);
|
|
12002
|
+
}
|
|
12003
|
+
return;
|
|
12004
|
+
}
|
|
12005
|
+
if ("children" in node) {
|
|
12006
|
+
for (const child of node.children) {
|
|
12007
|
+
collect(child);
|
|
12008
|
+
}
|
|
12009
|
+
}
|
|
12010
|
+
};
|
|
12011
|
+
collect(fromMarkdown2(text));
|
|
12012
|
+
return spans;
|
|
12013
|
+
};
|
|
11912
12014
|
var mdxSafe = (text) => {
|
|
11913
12015
|
let out = "";
|
|
11914
12016
|
let cursor = 0;
|
|
11915
|
-
for (const
|
|
11916
|
-
const start = match.index ?? 0;
|
|
12017
|
+
for (const [start, end] of codeSpans(text)) {
|
|
11917
12018
|
out += escapeProse(text.slice(cursor, start));
|
|
11918
|
-
out +=
|
|
11919
|
-
cursor =
|
|
12019
|
+
out += text.slice(start, end);
|
|
12020
|
+
cursor = end;
|
|
11920
12021
|
}
|
|
11921
12022
|
return out + escapeProse(text.slice(cursor));
|
|
11922
12023
|
};
|
|
11923
12024
|
var META_DESCRIPTION_MAX = 160;
|
|
11924
|
-
var PARAGRAPH_BREAK = /\n\s*\n/u;
|
|
11925
|
-
var MARKDOWN_LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
|
|
11926
|
-
var MARKDOWN_MARKS = /[*_`#>]/gu;
|
|
11927
12025
|
var WHITESPACE = /\s+/gu;
|
|
11928
12026
|
var TRAILING_WORD = /\s+\S*$/u;
|
|
11929
|
-
var plainProse = (markdown) =>
|
|
12027
|
+
var plainProse = (markdown) => {
|
|
12028
|
+
const tree = fromMarkdown2(markdown);
|
|
12029
|
+
const first = tree.children.find((node) => node.type === "paragraph") ?? tree.children[0];
|
|
12030
|
+
return first ? mdastToString(first).replace(WHITESPACE, " ").trim() : "";
|
|
12031
|
+
};
|
|
11930
12032
|
var clip = (text, max) => {
|
|
11931
12033
|
if (max <= 0) {
|
|
11932
12034
|
return "";
|
|
@@ -12045,6 +12147,13 @@ var specEntries = (spec, operations, reference) => {
|
|
|
12045
12147
|
entries.push(toEntry(overviewMdx(spec, reference), base ? `${base}/index.mdx` : "index.mdx"));
|
|
12046
12148
|
return entries;
|
|
12047
12149
|
};
|
|
12150
|
+
var tagFolderMeta = (spec, tags) => {
|
|
12151
|
+
const base = routeToRef(spec.route);
|
|
12152
|
+
return Object.fromEntries(tags.map((tag) => [
|
|
12153
|
+
base ? `${base}/${tag.slug}` : tag.slug,
|
|
12154
|
+
{ title: tag.name }
|
|
12155
|
+
]));
|
|
12156
|
+
};
|
|
12048
12157
|
var openApiSource = (references, ctx) => {
|
|
12049
12158
|
let parsed = {};
|
|
12050
12159
|
const loadReference = async (reference) => {
|
|
@@ -12097,6 +12206,7 @@ var openApiSource = (references, ctx) => {
|
|
|
12097
12206
|
] : []
|
|
12098
12207
|
],
|
|
12099
12208
|
entries: specEntries(spec, operations, reference),
|
|
12209
|
+
folderMeta: tagFolderMeta(spec, tags),
|
|
12100
12210
|
slug: reference.slug,
|
|
12101
12211
|
spec
|
|
12102
12212
|
};
|
|
@@ -12118,6 +12228,7 @@ var openApiSource = (references, ctx) => {
|
|
|
12118
12228
|
severity: "warning"
|
|
12119
12229
|
})));
|
|
12120
12230
|
const data = {};
|
|
12231
|
+
const folderMeta = {};
|
|
12121
12232
|
for (const result of results) {
|
|
12122
12233
|
if ("severity" in result) {
|
|
12123
12234
|
diagnostics.push(result);
|
|
@@ -12125,10 +12236,11 @@ var openApiSource = (references, ctx) => {
|
|
|
12125
12236
|
}
|
|
12126
12237
|
data[result.slug] = result.spec;
|
|
12127
12238
|
entries.push(...result.entries);
|
|
12239
|
+
Object.assign(folderMeta, result.folderMeta);
|
|
12128
12240
|
diagnostics.push(...result.diagnostics);
|
|
12129
12241
|
}
|
|
12130
12242
|
parsed = data;
|
|
12131
|
-
return { diagnostics, entries };
|
|
12243
|
+
return { diagnostics, entries, folderMeta };
|
|
12132
12244
|
};
|
|
12133
12245
|
return {
|
|
12134
12246
|
kind: "openapi-source",
|
|
@@ -12204,15 +12316,15 @@ var filesystemSource = (options) => {
|
|
|
12204
12316
|
};
|
|
12205
12317
|
|
|
12206
12318
|
// src/core/sources/github-releases.ts
|
|
12207
|
-
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
12319
|
+
import { fromMarkdown as fromMarkdown3 } from "mdast-util-from-markdown";
|
|
12208
12320
|
import { gfmFromMarkdown } from "mdast-util-gfm";
|
|
12209
|
-
import { toString as
|
|
12321
|
+
import { toString as mdastToString2 } from "mdast-util-to-string";
|
|
12210
12322
|
import { gfm } from "micromark-extension-gfm";
|
|
12211
12323
|
var DEFAULT_BASE_URL = "https://api.github.com";
|
|
12212
12324
|
var DEFAULT_LIMIT = 100;
|
|
12213
12325
|
var PER_PAGE = 100;
|
|
12214
12326
|
var LEADING_V = /^v/iu;
|
|
12215
|
-
var
|
|
12327
|
+
var NON_SLUG2 = /[^a-z0-9]+/gu;
|
|
12216
12328
|
var EDGE_DASHES = /^-+|-+$/gu;
|
|
12217
12329
|
var DESCRIPTION_MAX = 160;
|
|
12218
12330
|
var DESCRIPTION_MIN = 110;
|
|
@@ -12220,24 +12332,35 @@ var CHANGESET_HASH = /^(?<mark>\s*(?:[-*+]|\d+[.)])\s+)[0-9a-f]{7,40}:\s+/gmu;
|
|
|
12220
12332
|
var WHITESPACE2 = /\s+/gu;
|
|
12221
12333
|
var TRAILING_FRAGMENT = /[\s,;:.—–-]+$/u;
|
|
12222
12334
|
var NON_PROSE = new Set(["code", "heading", "html", "thematicBreak"]);
|
|
12335
|
+
var graphemePrefix = (text, max) => {
|
|
12336
|
+
let end = 0;
|
|
12337
|
+
const graphemes = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
12338
|
+
for (const { index, segment } of graphemes.segment(text)) {
|
|
12339
|
+
if (index + segment.length > max) {
|
|
12340
|
+
break;
|
|
12341
|
+
}
|
|
12342
|
+
end = index + segment.length;
|
|
12343
|
+
}
|
|
12344
|
+
return text.slice(0, end);
|
|
12345
|
+
};
|
|
12223
12346
|
var releaseDescription = (body) => {
|
|
12224
|
-
const tree =
|
|
12347
|
+
const tree = fromMarkdown3(body.replaceAll(CHANGESET_HASH, "$<mark>"), {
|
|
12225
12348
|
extensions: [gfm()],
|
|
12226
12349
|
mdastExtensions: [gfmFromMarkdown()]
|
|
12227
12350
|
});
|
|
12228
|
-
const text = tree.children.filter((node) => !NON_PROSE.has(node.type)).map((node) =>
|
|
12351
|
+
const text = tree.children.filter((node) => !NON_PROSE.has(node.type)).map((node) => mdastToString2(node, { includeHtml: false, includeImageAlt: false })).join(" ").replaceAll(WHITESPACE2, " ").trim();
|
|
12229
12352
|
if (!text) {
|
|
12230
12353
|
return;
|
|
12231
12354
|
}
|
|
12232
12355
|
if (text.length <= DESCRIPTION_MAX) {
|
|
12233
12356
|
return text;
|
|
12234
12357
|
}
|
|
12235
|
-
const slice = text
|
|
12358
|
+
const slice = graphemePrefix(text, DESCRIPTION_MAX - 1);
|
|
12236
12359
|
const boundary = slice.lastIndexOf(" ");
|
|
12237
12360
|
const head = (boundary >= DESCRIPTION_MIN ? slice.slice(0, boundary) : slice).replace(TRAILING_FRAGMENT, "");
|
|
12238
12361
|
return `${head}…`;
|
|
12239
12362
|
};
|
|
12240
|
-
var slugifyTag = (tag) => tag.toLowerCase().replaceAll(
|
|
12363
|
+
var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG2, "-").replaceAll(EDGE_DASHES, "");
|
|
12241
12364
|
var githubHeaders = () => {
|
|
12242
12365
|
const headers = new Headers({ Accept: "application/vnd.github+json" });
|
|
12243
12366
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -12490,6 +12613,7 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
12490
12613
|
|
|
12491
12614
|
// src/core/sources/notion.ts
|
|
12492
12615
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
12616
|
+
import pLimit from "p-limit";
|
|
12493
12617
|
import { join as join15 } from "pathe";
|
|
12494
12618
|
|
|
12495
12619
|
// src/core/sources/assets.ts
|
|
@@ -12569,6 +12693,7 @@ var RATE_LIMITED = 429;
|
|
|
12569
12693
|
var MAX_RETRIES = 4;
|
|
12570
12694
|
var BASE_DELAY_MS = 500;
|
|
12571
12695
|
var SECOND_MS2 = 1000;
|
|
12696
|
+
var DEFAULT_CONCURRENCY = 3;
|
|
12572
12697
|
var withNotionRetry = async (call, attempt = 0) => {
|
|
12573
12698
|
try {
|
|
12574
12699
|
return await call();
|
|
@@ -12578,7 +12703,7 @@ var withNotionRetry = async (call, attempt = 0) => {
|
|
|
12578
12703
|
throw error;
|
|
12579
12704
|
}
|
|
12580
12705
|
const retryAfter = Number(error.headers?.["retry-after"]);
|
|
12581
|
-
const wait = retryAfter > 0 ? retryAfter * SECOND_MS2 : BASE_DELAY_MS * 2 ** attempt;
|
|
12706
|
+
const wait = retryAfter > 0 ? retryAfter * SECOND_MS2 : BASE_DELAY_MS * 2 ** attempt * (1 + Math.random());
|
|
12582
12707
|
await sleep2(wait);
|
|
12583
12708
|
return withNotionRetry(call, attempt + 1);
|
|
12584
12709
|
}
|
|
@@ -12642,6 +12767,8 @@ ${text}
|
|
|
12642
12767
|
};
|
|
12643
12768
|
var notionSource = (options, ctx) => {
|
|
12644
12769
|
const props = options.properties ?? {};
|
|
12770
|
+
const limit = pLimit(Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY));
|
|
12771
|
+
const notionCall = (call) => withNotionRetry(() => limit(call));
|
|
12645
12772
|
const cache = snapshotCache(ctx?.cacheDir ?? join15(".blume", "cache", options.name));
|
|
12646
12773
|
const assetsDir = ctx?.assetsDir ?? join15(".blume", "public", "blume-assets", options.name);
|
|
12647
12774
|
const assetsBaseUrl = ctx?.assetsBaseUrl ?? `/blume-assets/${options.name}`;
|
|
@@ -12662,7 +12789,7 @@ var notionSource = (options, ctx) => {
|
|
|
12662
12789
|
}
|
|
12663
12790
|
return new Client({ auth: options.token ?? process.env.NOTION_TOKEN });
|
|
12664
12791
|
};
|
|
12665
|
-
const childrenOf = (client, blockId) => collectAll((cursor) =>
|
|
12792
|
+
const childrenOf = (client, blockId) => collectAll((cursor) => notionCall(() => client.blocks.children.list({ block_id: blockId, start_cursor: cursor })));
|
|
12666
12793
|
const renderContainer = async (client, block, render) => {
|
|
12667
12794
|
const children = async (target) => {
|
|
12668
12795
|
if (!target.has_children) {
|
|
@@ -12795,7 +12922,7 @@ ${nested}`;
|
|
|
12795
12922
|
}
|
|
12796
12923
|
};
|
|
12797
12924
|
};
|
|
12798
|
-
const queryDatabase = (client, cursor) =>
|
|
12925
|
+
const queryDatabase = (client, cursor) => notionCall(() => client.databases.query({
|
|
12799
12926
|
database_id: options.database,
|
|
12800
12927
|
start_cursor: cursor
|
|
12801
12928
|
}));
|
|
@@ -12846,8 +12973,11 @@ var HEADING_STYLES = {
|
|
|
12846
12973
|
h5: "##### ",
|
|
12847
12974
|
h6: "###### "
|
|
12848
12975
|
};
|
|
12976
|
+
var MARKDOWN_SPECIALS = /[\\`*_[\]~<]/gu;
|
|
12977
|
+
var escapeText = (text) => text.replaceAll(MARKDOWN_SPECIALS, String.raw`\$&`);
|
|
12849
12978
|
var renderSpan = (span, defs) => {
|
|
12850
|
-
|
|
12979
|
+
const isCode = span.marks?.includes("code") ?? false;
|
|
12980
|
+
let text = isCode ? span.text ?? "" : escapeText(span.text ?? "");
|
|
12851
12981
|
if (!span.marks || span.marks.length === 0) {
|
|
12852
12982
|
return text;
|
|
12853
12983
|
}
|
|
@@ -13084,6 +13214,7 @@ var buildSource = (def, name, context, runtime) => {
|
|
|
13084
13214
|
}
|
|
13085
13215
|
if (def.type === "notion") {
|
|
13086
13216
|
return notionSource({
|
|
13217
|
+
concurrency: def.concurrency,
|
|
13087
13218
|
database: def.database,
|
|
13088
13219
|
name,
|
|
13089
13220
|
pollInterval: def.pollInterval,
|
|
@@ -13258,6 +13389,10 @@ var scanProject = async (root, options = {}) => {
|
|
|
13258
13389
|
Promise.all(sources.map(async (source) => ({ source, ...await source.load() }))),
|
|
13259
13390
|
discoverFolderMeta(metaSources, { localeDirs })
|
|
13260
13391
|
]);
|
|
13392
|
+
const sharedFolderMeta = new Map([
|
|
13393
|
+
...loaded.flatMap(({ folderMeta: sourceMeta }) => Object.entries(sourceMeta ?? {})),
|
|
13394
|
+
...folderMeta.shared
|
|
13395
|
+
]);
|
|
13261
13396
|
const {
|
|
13262
13397
|
diagnostics: contentDiagnostics,
|
|
13263
13398
|
droppedPages,
|
|
@@ -13280,7 +13415,7 @@ var scanProject = async (root, options = {}) => {
|
|
|
13280
13415
|
folderMeta: folderMeta.meta,
|
|
13281
13416
|
i18n: config.i18n,
|
|
13282
13417
|
navigation: config.navigation,
|
|
13283
|
-
sharedFolderMeta
|
|
13418
|
+
sharedFolderMeta
|
|
13284
13419
|
});
|
|
13285
13420
|
const manifest = buildManifest({ config, context, graph });
|
|
13286
13421
|
const i18nWarnings = config.i18n ? i18nDiagnostics(pages, config.i18n) : [];
|
|
@@ -13343,14 +13478,16 @@ var shouldFail = (result, gate) => {
|
|
|
13343
13478
|
const failing = failingSeverities(gate);
|
|
13344
13479
|
return result.diagnostics.some((d) => failing.has(d.severity));
|
|
13345
13480
|
};
|
|
13346
|
-
var launchAgentCode = async (
|
|
13481
|
+
var launchAgentCode = async (agent, prompt) => {
|
|
13482
|
+
const cli = AGENTS[agent];
|
|
13347
13483
|
try {
|
|
13348
|
-
return await launchAgent(bin, prompt);
|
|
13484
|
+
return await launchAgent(cli.bin, prompt);
|
|
13349
13485
|
} catch (error) {
|
|
13350
13486
|
if (error?.code !== "ENOENT") {
|
|
13351
13487
|
throw error;
|
|
13352
13488
|
}
|
|
13353
|
-
|
|
13489
|
+
logger.error(`${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`);
|
|
13490
|
+
return process.exit(1);
|
|
13354
13491
|
}
|
|
13355
13492
|
};
|
|
13356
13493
|
var auditCommand = defineCommand2({
|
|
@@ -13458,11 +13595,7 @@ var auditCommand = defineCommand2({
|
|
|
13458
13595
|
process.stderr.write(` Handing ${count} finding${count === 1 ? "" : "s"} to ${cli.name}…
|
|
13459
13596
|
|
|
13460
13597
|
`);
|
|
13461
|
-
const code = await launchAgentCode(
|
|
13462
|
-
if (code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
13463
|
-
logger.error(`${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`);
|
|
13464
|
-
process.exit(1);
|
|
13465
|
-
}
|
|
13598
|
+
const code = await launchAgentCode(agent, fixPrompt(report));
|
|
13466
13599
|
if (code !== 0) {
|
|
13467
13600
|
process.exit(code);
|
|
13468
13601
|
}
|
|
@@ -13508,7 +13641,7 @@ var buildRssFeeds = (project) => {
|
|
|
13508
13641
|
if (!(rss.enabled && site)) {
|
|
13509
13642
|
return [];
|
|
13510
13643
|
}
|
|
13511
|
-
const base = site
|
|
13644
|
+
const base = siteRoot(site);
|
|
13512
13645
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
13513
13646
|
const rootLink = `${base}${deployBase}`;
|
|
13514
13647
|
const feeds = [];
|
|
@@ -13588,7 +13721,7 @@ var askApiUrl = (endpoint, site, abs) => {
|
|
|
13588
13721
|
if (!endpoint) {
|
|
13589
13722
|
return abs("/api/ask");
|
|
13590
13723
|
}
|
|
13591
|
-
return site && endpoint.startsWith("/") ?
|
|
13724
|
+
return site && endpoint.startsWith("/") ? absoluteUrl(site, endpoint) : endpoint;
|
|
13592
13725
|
};
|
|
13593
13726
|
var wellKnownArtifacts = (config, abs) => {
|
|
13594
13727
|
const artifacts = {};
|
|
@@ -13612,7 +13745,7 @@ var buildAgentReadability = (project) => {
|
|
|
13612
13745
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
13613
13746
|
const abs = (path) => {
|
|
13614
13747
|
const based = withBasePath(deployBase, path);
|
|
13615
|
-
return site ?
|
|
13748
|
+
return site ? absoluteUrl(site, based) : based;
|
|
13616
13749
|
};
|
|
13617
13750
|
const negotiates = config.deployment.output === "server" && (config.deployment.adapter === "vercel" || config.deployment.adapter === "cloudflare");
|
|
13618
13751
|
const artifacts = {
|
|
@@ -14077,7 +14210,7 @@ var applyAgentVisibility = (markdown) => applyAudienceVisibility(markdown, "agen
|
|
|
14077
14210
|
// src/ai/llms.ts
|
|
14078
14211
|
var pageUrl = (route, site, base = "") => {
|
|
14079
14212
|
const path = withBasePath(base, route);
|
|
14080
|
-
return encodeURI(site ?
|
|
14213
|
+
return encodeURI(site ? absoluteUrl(site, path) : path);
|
|
14081
14214
|
};
|
|
14082
14215
|
var eligiblePages = (project) => project.graph.pages.filter((page) => !(page.meta.ai.exclude || page.meta.draft || page.meta.sidebar.hidden || page.meta.seo.noindex && page.source.name !== "openapi") && (project.config.ai.llmsTxt.openapi || page.source.name !== "openapi"));
|
|
14083
14216
|
var indexedNavigations = (project) => {
|
|
@@ -14251,49 +14384,16 @@ var markdownRoutePaths = (project) => {
|
|
|
14251
14384
|
};
|
|
14252
14385
|
|
|
14253
14386
|
// src/ai/skills.ts
|
|
14254
|
-
import { createHash as
|
|
14387
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
14255
14388
|
import { readdir, readFile as readFile11, stat as stat2 } from "node:fs/promises";
|
|
14256
14389
|
import { join as join18 } from "pathe";
|
|
14257
14390
|
|
|
14258
14391
|
// src/ai/tar.ts
|
|
14259
14392
|
import { gzipSync } from "node:zlib";
|
|
14260
|
-
|
|
14393
|
+
import { createTar } from "nanotar";
|
|
14261
14394
|
var NAME_MAX = 100;
|
|
14262
14395
|
var encoder = new TextEncoder;
|
|
14263
|
-
var octal = (header, offset, length, value) => {
|
|
14264
|
-
const text = value.toString(8).padStart(length - 1, "0");
|
|
14265
|
-
header.set(encoder.encode(text), offset);
|
|
14266
|
-
header[offset + length - 1] = 0;
|
|
14267
|
-
};
|
|
14268
|
-
var text = (header, offset, value) => {
|
|
14269
|
-
header.set(encoder.encode(value), offset);
|
|
14270
|
-
};
|
|
14271
|
-
var fileHeader = (entry) => {
|
|
14272
|
-
const header = new Uint8Array(BLOCK);
|
|
14273
|
-
text(header, 0, entry.path);
|
|
14274
|
-
octal(header, 100, 8, entry.executable ? 493 : 420);
|
|
14275
|
-
octal(header, 108, 8, 0);
|
|
14276
|
-
octal(header, 116, 8, 0);
|
|
14277
|
-
octal(header, 124, 12, entry.content.byteLength);
|
|
14278
|
-
octal(header, 136, 12, 0);
|
|
14279
|
-
text(header, 156, "0");
|
|
14280
|
-
text(header, 257, "ustar");
|
|
14281
|
-
header[262] = 0;
|
|
14282
|
-
text(header, 263, "00");
|
|
14283
|
-
octal(header, 329, 8, 0);
|
|
14284
|
-
octal(header, 337, 8, 0);
|
|
14285
|
-
header.fill(32, 148, 156);
|
|
14286
|
-
let sum = 0;
|
|
14287
|
-
for (const byte of header) {
|
|
14288
|
-
sum += byte;
|
|
14289
|
-
}
|
|
14290
|
-
text(header, 148, sum.toString(8).padStart(6, "0"));
|
|
14291
|
-
header[154] = 0;
|
|
14292
|
-
header[155] = 32;
|
|
14293
|
-
return header;
|
|
14294
|
-
};
|
|
14295
14396
|
var buildTarGz = (entries) => {
|
|
14296
|
-
const blocks = [];
|
|
14297
14397
|
for (const entry of entries) {
|
|
14298
14398
|
if (encoder.encode(entry.path).byteLength > NAME_MAX) {
|
|
14299
14399
|
throw new Error(`tar path exceeds ${NAME_MAX} bytes: ${entry.path}`);
|
|
@@ -14301,20 +14401,19 @@ var buildTarGz = (entries) => {
|
|
|
14301
14401
|
if (entry.path.startsWith("/") || entry.path.split("/").includes("..")) {
|
|
14302
14402
|
throw new Error(`tar path must be archive-relative: ${entry.path}`);
|
|
14303
14403
|
}
|
|
14304
|
-
blocks.push(fileHeader(entry), entry.content);
|
|
14305
|
-
const overhang = entry.content.byteLength % BLOCK;
|
|
14306
|
-
if (overhang > 0) {
|
|
14307
|
-
blocks.push(new Uint8Array(BLOCK - overhang));
|
|
14308
|
-
}
|
|
14309
|
-
}
|
|
14310
|
-
blocks.push(new Uint8Array(BLOCK * 2));
|
|
14311
|
-
const total = blocks.reduce((sum, block) => sum + block.byteLength, 0);
|
|
14312
|
-
const tar = new Uint8Array(total);
|
|
14313
|
-
let offset = 0;
|
|
14314
|
-
for (const block of blocks) {
|
|
14315
|
-
tar.set(block, offset);
|
|
14316
|
-
offset += block.byteLength;
|
|
14317
14404
|
}
|
|
14405
|
+
const tar = createTar(entries.map((entry) => ({
|
|
14406
|
+
attrs: {
|
|
14407
|
+
gid: 0,
|
|
14408
|
+
group: "",
|
|
14409
|
+
mode: entry.executable ? "755" : "644",
|
|
14410
|
+
mtime: 0,
|
|
14411
|
+
uid: 0,
|
|
14412
|
+
user: ""
|
|
14413
|
+
},
|
|
14414
|
+
data: entry.content,
|
|
14415
|
+
name: entry.path
|
|
14416
|
+
})));
|
|
14318
14417
|
return new Uint8Array(gzipSync(tar, { level: 9 }));
|
|
14319
14418
|
};
|
|
14320
14419
|
|
|
@@ -14324,7 +14423,7 @@ var AGENT_SKILLS_SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema
|
|
|
14324
14423
|
var SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
14325
14424
|
var SKILL_NAME_MAX = 64;
|
|
14326
14425
|
var DESCRIPTION_MAX2 = 1024;
|
|
14327
|
-
var sha256 = (content) => `sha256:${
|
|
14426
|
+
var sha256 = (content) => `sha256:${createHash3("sha256").update(content).digest("hex")}`;
|
|
14328
14427
|
var collectEntries = async (dir, prefix = "") => {
|
|
14329
14428
|
const entries = [];
|
|
14330
14429
|
const items = await readdir(dir, { withFileTypes: true });
|
|
@@ -14585,6 +14684,20 @@ const redirectFor = (pathname) => {
|
|
|
14585
14684
|
return Object.hasOwn(REDIRECTS, path) ? REDIRECTS[path] : null;
|
|
14586
14685
|
};
|
|
14587
14686
|
|
|
14687
|
+
// \`_redirects\` semantics, which the static layer applies to these same
|
|
14688
|
+
// paths: the request's query string is forwarded unless the destination
|
|
14689
|
+
// carries its own, and a destination fragment stays after the query.
|
|
14690
|
+
const redirectLocation = (destination, search) => {
|
|
14691
|
+
const hashIndex = destination.indexOf("#");
|
|
14692
|
+
const bare = hashIndex === -1 ? destination : destination.slice(0, hashIndex);
|
|
14693
|
+
if (!search || bare.includes("?")) {
|
|
14694
|
+
return destination;
|
|
14695
|
+
}
|
|
14696
|
+
return hashIndex === -1
|
|
14697
|
+
? bare + search
|
|
14698
|
+
: bare + search + destination.slice(hashIndex);
|
|
14699
|
+
};
|
|
14700
|
+
|
|
14588
14701
|
const parseAccept = (accept) =>
|
|
14589
14702
|
accept.split(",").map((part) => {
|
|
14590
14703
|
const segments = part.trim().split(";");
|
|
@@ -14664,7 +14777,7 @@ export default {
|
|
|
14664
14777
|
const redirect = redirectFor(url.pathname);
|
|
14665
14778
|
if (redirect !== null) {
|
|
14666
14779
|
return new Response(null, {
|
|
14667
|
-
headers: { location: redirect[0] },
|
|
14780
|
+
headers: { location: redirectLocation(redirect[0], url.search) },
|
|
14668
14781
|
status: redirect[1],
|
|
14669
14782
|
});
|
|
14670
14783
|
}
|
|
@@ -14837,7 +14950,7 @@ var buildRobots = (project) => {
|
|
|
14837
14950
|
const { site } = config.deployment;
|
|
14838
14951
|
if (site && config.seo.sitemap) {
|
|
14839
14952
|
const sitemapPath = withBasePath(normalizeBasePath(config.deployment.base), "/sitemap.xml");
|
|
14840
|
-
lines.push("", `Sitemap: ${site
|
|
14953
|
+
lines.push("", `Sitemap: ${absoluteUrl(site, sitemapPath)}`);
|
|
14841
14954
|
}
|
|
14842
14955
|
return `${lines.join(`
|
|
14843
14956
|
`)}
|
|
@@ -14932,12 +15045,19 @@ var lastmodTag = (value) => {
|
|
|
14932
15045
|
const date = new Date(value);
|
|
14933
15046
|
return Number.isNaN(date.getTime()) ? "" : `<lastmod>${date.toISOString().slice(0, 10)}</lastmod>`;
|
|
14934
15047
|
};
|
|
14935
|
-
var
|
|
15048
|
+
var URLS_PER_FILE = 50000;
|
|
15049
|
+
var renderUrlset = (urls) => `<?xml version="1.0" encoding="UTF-8"?>
|
|
15050
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
15051
|
+
${urls.join(`
|
|
15052
|
+
`)}
|
|
15053
|
+
</urlset>
|
|
15054
|
+
`;
|
|
15055
|
+
var buildSitemapFiles = (project) => {
|
|
14936
15056
|
const { site } = project.config.deployment;
|
|
14937
15057
|
if (!(site && project.config.seo.sitemap)) {
|
|
14938
15058
|
return null;
|
|
14939
15059
|
}
|
|
14940
|
-
const base = site
|
|
15060
|
+
const base = siteRoot(site);
|
|
14941
15061
|
const deployBase = normalizeBasePath(project.config.deployment.base);
|
|
14942
15062
|
const seen = new Set;
|
|
14943
15063
|
const urls = [];
|
|
@@ -14964,12 +15084,27 @@ var buildSitemap = (project) => {
|
|
|
14964
15084
|
pushUrl(withBasePath(deployBase, route));
|
|
14965
15085
|
}
|
|
14966
15086
|
urls.sort();
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
15087
|
+
if (urls.length <= URLS_PER_FILE) {
|
|
15088
|
+
return [{ name: "sitemap.xml", xml: renderUrlset(urls) }];
|
|
15089
|
+
}
|
|
15090
|
+
const chunks = [];
|
|
15091
|
+
const references = [];
|
|
15092
|
+
for (let start = 0;start < urls.length; start += URLS_PER_FILE) {
|
|
15093
|
+
const name = `sitemap-${chunks.length + 1}.xml`;
|
|
15094
|
+
chunks.push({
|
|
15095
|
+
name,
|
|
15096
|
+
xml: renderUrlset(urls.slice(start, start + URLS_PER_FILE))
|
|
15097
|
+
});
|
|
15098
|
+
const loc = escapeXml2(encodeURI(`${base}${withBasePath(deployBase, `/${name}`)}`));
|
|
15099
|
+
references.push(` <sitemap><loc>${loc}</loc></sitemap>`);
|
|
15100
|
+
}
|
|
15101
|
+
const index = `<?xml version="1.0" encoding="UTF-8"?>
|
|
15102
|
+
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
15103
|
+
${references.join(`
|
|
14970
15104
|
`)}
|
|
14971
|
-
</
|
|
15105
|
+
</sitemapindex>
|
|
14972
15106
|
`;
|
|
15107
|
+
return [{ name: "sitemap.xml", xml: index }, ...chunks];
|
|
14973
15108
|
};
|
|
14974
15109
|
|
|
14975
15110
|
// src/deploy/vercel-negotiation.ts
|
|
@@ -15079,6 +15214,11 @@ var buildSearchIndex = async (outDir) => {
|
|
|
15079
15214
|
return result.page_count;
|
|
15080
15215
|
};
|
|
15081
15216
|
|
|
15217
|
+
// src/search/documents.ts
|
|
15218
|
+
import { fromMarkdown as fromMarkdown4 } from "mdast-util-from-markdown";
|
|
15219
|
+
import { gfmFromMarkdown as gfmFromMarkdown2 } from "mdast-util-gfm";
|
|
15220
|
+
import { gfm as gfm2 } from "micromark-extension-gfm";
|
|
15221
|
+
|
|
15082
15222
|
// src/search/facets.ts
|
|
15083
15223
|
var pageFacets = (page, config) => {
|
|
15084
15224
|
const declared = config.content.types[page.contentType]?.facets;
|
|
@@ -15096,25 +15236,60 @@ var pageFacets = (page, config) => {
|
|
|
15096
15236
|
};
|
|
15097
15237
|
|
|
15098
15238
|
// src/search/documents.ts
|
|
15099
|
-
var CODE_FENCE2 = /```[\s\S]*?```/gu;
|
|
15100
|
-
var INLINE_CODE2 = /`(?<code>[^`]+)`/gu;
|
|
15101
15239
|
var HTML_OR_JSX = /<\/?[a-zA-Z][^\n<>]*>|<\/?>/gu;
|
|
15102
|
-
var IMAGE = /!\[[^\]]*\]\([^)]*\)/gu;
|
|
15103
|
-
var LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
|
|
15104
|
-
var HEADING_MARK = /^#{1,6}\s+/gmu;
|
|
15105
|
-
var MARKDOWN_PUNCT = /[*_~>]+/gu;
|
|
15106
15240
|
var WHITESPACE3 = /\s+/gu;
|
|
15107
|
-
var
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15241
|
+
var INLINE_PARENTS = new Set([
|
|
15242
|
+
"delete",
|
|
15243
|
+
"emphasis",
|
|
15244
|
+
"footnoteReference",
|
|
15245
|
+
"link",
|
|
15246
|
+
"linkReference",
|
|
15247
|
+
"strong"
|
|
15248
|
+
]);
|
|
15249
|
+
var collectText = (node, out) => {
|
|
15250
|
+
switch (node.type) {
|
|
15251
|
+
case "code":
|
|
15252
|
+
case "image":
|
|
15253
|
+
case "imageReference": {
|
|
15254
|
+
return;
|
|
15255
|
+
}
|
|
15256
|
+
case "inlineCode": {
|
|
15257
|
+
out.push(node.value);
|
|
15258
|
+
return;
|
|
15259
|
+
}
|
|
15260
|
+
case "html": {
|
|
15261
|
+
out.push(node.value.replaceAll(HTML_OR_JSX, " "));
|
|
15262
|
+
return;
|
|
15263
|
+
}
|
|
15264
|
+
case "break": {
|
|
15265
|
+
out.push(" ");
|
|
15266
|
+
return;
|
|
15267
|
+
}
|
|
15268
|
+
default: {
|
|
15269
|
+
break;
|
|
15270
|
+
}
|
|
15271
|
+
}
|
|
15272
|
+
if ("value" in node) {
|
|
15273
|
+
out.push(node.value);
|
|
15274
|
+
return;
|
|
15275
|
+
}
|
|
15276
|
+
if ("children" in node) {
|
|
15277
|
+
for (const child of node.children) {
|
|
15278
|
+
collectText(child, out);
|
|
15279
|
+
}
|
|
15280
|
+
if (!INLINE_PARENTS.has(node.type)) {
|
|
15281
|
+
out.push(" ");
|
|
15282
|
+
}
|
|
15115
15283
|
}
|
|
15116
|
-
|
|
15117
|
-
|
|
15284
|
+
};
|
|
15285
|
+
var toPlainText = (markdown) => {
|
|
15286
|
+
const tree = fromMarkdown4(markdown, {
|
|
15287
|
+
extensions: [gfm2()],
|
|
15288
|
+
mdastExtensions: [gfmFromMarkdown2()]
|
|
15289
|
+
});
|
|
15290
|
+
const out = [];
|
|
15291
|
+
collectText(tree, out);
|
|
15292
|
+
return out.join("").replaceAll(WHITESPACE3, " ").trim();
|
|
15118
15293
|
};
|
|
15119
15294
|
var buildCrumbIndex = (sidebar) => {
|
|
15120
15295
|
const index = new Map;
|
|
@@ -15434,7 +15609,7 @@ var refuseIfDevRunning = (root, action, options = {}) => {
|
|
|
15434
15609
|
};
|
|
15435
15610
|
|
|
15436
15611
|
// src/astro/generate.ts
|
|
15437
|
-
import { createHash as
|
|
15612
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
15438
15613
|
import { existsSync as existsSync16, readFileSync as readFileSync8, realpathSync } from "node:fs";
|
|
15439
15614
|
import {
|
|
15440
15615
|
lstat,
|
|
@@ -15447,6 +15622,8 @@ import {
|
|
|
15447
15622
|
} from "node:fs/promises";
|
|
15448
15623
|
import { createRequire as createRequire4 } from "node:module";
|
|
15449
15624
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
15625
|
+
import { imageSize as imageSize2 } from "image-size";
|
|
15626
|
+
import pMap6 from "p-map";
|
|
15450
15627
|
import { basename as basename3, dirname as dirname11, join as join28, normalize as normalize3, relative as relative14, resolve as resolve8 } from "pathe";
|
|
15451
15628
|
import { glob as glob7 } from "tinyglobby";
|
|
15452
15629
|
|
|
@@ -15555,7 +15732,7 @@ var MCP_TOOLS = [
|
|
|
15555
15732
|
// src/ai/mcp/discovery.ts
|
|
15556
15733
|
var serverUrl = (input) => {
|
|
15557
15734
|
const path = withBasePath(input.base, input.route);
|
|
15558
|
-
return input.site ?
|
|
15735
|
+
return input.site ? absoluteUrl(input.site, path) : path;
|
|
15559
15736
|
};
|
|
15560
15737
|
var buildMcpDiscovery = (input) => ({
|
|
15561
15738
|
servers: [
|
|
@@ -15568,7 +15745,10 @@ var buildMcpDiscovery = (input) => ({
|
|
|
15568
15745
|
});
|
|
15569
15746
|
var SERVER_CARD_SCHEMA = "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json";
|
|
15570
15747
|
var CARD_TEXT_MAX = 100;
|
|
15571
|
-
var truncate = (
|
|
15748
|
+
var truncate = (text) => text.length > CARD_TEXT_MAX ? `${text.slice(0, CARD_TEXT_MAX - 1)}…` : text;
|
|
15749
|
+
var NON_ASCII_SLUG = /[^a-z0-9]+/gu;
|
|
15750
|
+
var COMBINING_MARKS = /\p{M}+/gu;
|
|
15751
|
+
var asciiSlugify = (text) => trimChar(text.normalize("NFKD").replace(COMBINING_MARKS, "").toLowerCase().replace(NON_ASCII_SLUG, "-"), "-");
|
|
15572
15752
|
var reverseDnsName = (input) => {
|
|
15573
15753
|
let namespace = "localhost";
|
|
15574
15754
|
if (input.site) {
|
|
@@ -15576,7 +15756,7 @@ var reverseDnsName = (input) => {
|
|
|
15576
15756
|
namespace = new URL(input.site).hostname.split(".").toReversed().join(".");
|
|
15577
15757
|
} catch {}
|
|
15578
15758
|
}
|
|
15579
|
-
return `${namespace}/${
|
|
15759
|
+
return `${namespace}/${asciiSlugify(input.name) || "docs"}`;
|
|
15580
15760
|
};
|
|
15581
15761
|
var HTTP_URL2 = /^https?:\/\//u;
|
|
15582
15762
|
var buildMcpServerCard = (input) => {
|
|
@@ -15599,7 +15779,7 @@ var buildMcpServerCard = (input) => {
|
|
|
15599
15779
|
transports: [{ endpoint: url, type: "streamable-http" }],
|
|
15600
15780
|
url,
|
|
15601
15781
|
version: input.version,
|
|
15602
|
-
...input.site ? { websiteUrl:
|
|
15782
|
+
...input.site ? { websiteUrl: siteRoot(input.site) } : {}
|
|
15603
15783
|
};
|
|
15604
15784
|
};
|
|
15605
15785
|
|
|
@@ -15934,9 +16114,9 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
15934
16114
|
import { mkdir as mkdir6 } from "node:fs/promises";
|
|
15935
16115
|
import { dirname as dirname9 } from "pathe";
|
|
15936
16116
|
import writeFileAtomic from "write-file-atomic";
|
|
15937
|
-
var writeTextAtomic = async (path,
|
|
16117
|
+
var writeTextAtomic = async (path, text) => {
|
|
15938
16118
|
await mkdir6(dirname9(path), { recursive: true });
|
|
15939
|
-
await writeFileAtomic(path,
|
|
16119
|
+
await writeFileAtomic(path, text, { encoding: "utf-8", fsync: false });
|
|
15940
16120
|
};
|
|
15941
16121
|
|
|
15942
16122
|
// src/core/tsconfig-aliases.ts
|
|
@@ -16183,9 +16363,8 @@ ${dark}`;
|
|
|
16183
16363
|
|
|
16184
16364
|
// src/openapi/scalar.ts
|
|
16185
16365
|
var URL_SPEC2 = /^https?:\/\//u;
|
|
16186
|
-
var ROUTE_EDGES = /^\/+|\/+$/gu;
|
|
16187
16366
|
var referencePagePath = (route) => {
|
|
16188
|
-
const segments = route
|
|
16367
|
+
const segments = trimChar(route, "/");
|
|
16189
16368
|
return `${segments === "" ? "index" : segments}.astro`;
|
|
16190
16369
|
};
|
|
16191
16370
|
var darkModeConfig = (mode) => {
|
|
@@ -17227,14 +17406,17 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
|
|
|
17227
17406
|
|
|
17228
17407
|
// src/astro/examples.ts
|
|
17229
17408
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
17409
|
+
import pMap5 from "p-map";
|
|
17230
17410
|
import { join as join27, relative as relative13 } from "pathe";
|
|
17231
17411
|
import { glob as glob6 } from "tinyglobby";
|
|
17232
17412
|
|
|
17233
17413
|
// src/astro/islands.ts
|
|
17234
17414
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
17415
|
+
import pMap4 from "p-map";
|
|
17235
17416
|
import { basename as basename2, join as join26 } from "pathe";
|
|
17236
17417
|
import { glob as glob5 } from "tinyglobby";
|
|
17237
17418
|
var DEFAULT_CLIENT = "visible";
|
|
17419
|
+
var READ_CONCURRENCY2 = 16;
|
|
17238
17420
|
var VALID_MODES = new Set([
|
|
17239
17421
|
"idle",
|
|
17240
17422
|
"load",
|
|
@@ -17268,7 +17450,9 @@ var discoverIslands = async (root) => {
|
|
|
17268
17450
|
onlyFiles: true
|
|
17269
17451
|
});
|
|
17270
17452
|
const files = matches2.toSorted();
|
|
17271
|
-
const sources = await
|
|
17453
|
+
const sources = await pMap4(files, (file) => readFile14(file, "utf-8"), {
|
|
17454
|
+
concurrency: READ_CONCURRENCY2
|
|
17455
|
+
});
|
|
17272
17456
|
const islands = [];
|
|
17273
17457
|
const warnings = [];
|
|
17274
17458
|
const seen = new Map;
|
|
@@ -17313,6 +17497,7 @@ var FRAMEWORK_BY_EXT3 = {
|
|
|
17313
17497
|
};
|
|
17314
17498
|
var EXAMPLE_FILE = /\.(?<ext>astro|jsx|svelte|tsx|vue)$/u;
|
|
17315
17499
|
var DEFAULT_EXAMPLE_GLOB = "**/*.{astro,jsx,svelte,tsx,vue}";
|
|
17500
|
+
var READ_CONCURRENCY3 = 16;
|
|
17316
17501
|
var GLOB_MAGIC = /[!*?[\]{}]/u;
|
|
17317
17502
|
var splitGlobBase = (pattern) => {
|
|
17318
17503
|
const segments = pattern.split("/");
|
|
@@ -17331,7 +17516,9 @@ var discoverExamples = async (root, pattern = "examples") => {
|
|
|
17331
17516
|
onlyFiles: true
|
|
17332
17517
|
});
|
|
17333
17518
|
const files = matches2.toSorted();
|
|
17334
|
-
const sources = await
|
|
17519
|
+
const sources = await pMap5(files, (file) => readFile15(file, "utf-8"), {
|
|
17520
|
+
concurrency: READ_CONCURRENCY3
|
|
17521
|
+
});
|
|
17335
17522
|
const examples = [];
|
|
17336
17523
|
const warnings = [];
|
|
17337
17524
|
const seen = new Map;
|
|
@@ -17540,6 +17727,18 @@ var searchProviderWarnings = (provider, root, pkgDir = packageRoot()) => {
|
|
|
17540
17727
|
}
|
|
17541
17728
|
return warnings;
|
|
17542
17729
|
};
|
|
17730
|
+
var askProviderWarnings = (ask, root, pkgDir = packageRoot()) => {
|
|
17731
|
+
if (!ask?.enabled || ask.endpoint) {
|
|
17732
|
+
return [];
|
|
17733
|
+
}
|
|
17734
|
+
const dep = askBackendRuntimeDep(ask);
|
|
17735
|
+
if (!dep || canResolveFrom(root, dep) || canResolveFrom(pkgDir, dep)) {
|
|
17736
|
+
return [];
|
|
17737
|
+
}
|
|
17738
|
+
return [
|
|
17739
|
+
`Ask AI provider "${ask.provider}" needs "${dep}", which isn't installed. Run \`npm install ${dep}\` (or your package manager's equivalent).`
|
|
17740
|
+
];
|
|
17741
|
+
};
|
|
17543
17742
|
var examplesCssFile = (root, config) => config.examples.css ? join28(root, config.examples.css) : null;
|
|
17544
17743
|
var writeExamplesPreview = async (options) => {
|
|
17545
17744
|
const { config, hasExamples, root, srcDir, write } = options;
|
|
@@ -17570,16 +17769,19 @@ var detectNeedsReact = async (root) => {
|
|
|
17570
17769
|
return matches2.length > 0;
|
|
17571
17770
|
};
|
|
17572
17771
|
var containsMath = (content) => content.includes("$$") || content.includes("<Math");
|
|
17772
|
+
var READ_CONCURRENCY4 = 16;
|
|
17573
17773
|
var detectUsesMath = async (root, staged = []) => {
|
|
17574
17774
|
const files = await glob7(["**/*.{md,mdx}"], {
|
|
17575
17775
|
cwd: root,
|
|
17576
17776
|
ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
|
|
17577
17777
|
onlyFiles: true
|
|
17578
17778
|
});
|
|
17579
|
-
const contents = await
|
|
17779
|
+
const contents = await pMap6(files, (file) => readOptional(join28(root, file)), {
|
|
17780
|
+
concurrency: READ_CONCURRENCY4
|
|
17781
|
+
});
|
|
17580
17782
|
return [...contents, ...staged].some(containsMath);
|
|
17581
17783
|
};
|
|
17582
|
-
var hashConfigSource = (source) =>
|
|
17784
|
+
var hashConfigSource = (source) => createHash4("sha256").update(source).digest("hex");
|
|
17583
17785
|
var loadIntegrationBridge = async (config, context) => {
|
|
17584
17786
|
if (config.integrations.length === 0 || !context.configFile) {
|
|
17585
17787
|
return;
|
|
@@ -17629,35 +17831,25 @@ var collectStaged = (project) => {
|
|
|
17629
17831
|
var writeStagedContent = async (out, staged) => {
|
|
17630
17832
|
const contentDir = stagedContentDir(out);
|
|
17631
17833
|
const written = new Set;
|
|
17632
|
-
await Promise.all([...staged].map(async ([entryId,
|
|
17834
|
+
await Promise.all([...staged].map(async ([entryId, text]) => {
|
|
17633
17835
|
const path = join28(contentDir, entryId);
|
|
17634
17836
|
written.add(normalize3(path));
|
|
17635
|
-
await writeIfChanged(path,
|
|
17837
|
+
await writeIfChanged(path, text);
|
|
17636
17838
|
}));
|
|
17637
17839
|
if (existsSync16(contentDir)) {
|
|
17638
17840
|
await pruneOrphans(contentDir, written);
|
|
17639
17841
|
}
|
|
17640
17842
|
};
|
|
17641
|
-
var SVG_ROOT = /<svg\b(?<attributes>[^>]*)>/u;
|
|
17642
|
-
var SVG_WIDTH = /\bwidth\s*=\s*["'](?<value>[^"']+)["']/u;
|
|
17643
|
-
var SVG_HEIGHT = /\bheight\s*=\s*["'](?<value>[^"']+)["']/u;
|
|
17644
|
-
var SVG_LENGTH = /^\s*(?<value>[\d.]+)(?:px)?\s*$/u;
|
|
17645
|
-
var SVG_VIEW_BOX = /\bviewBox\s*=\s*["'][\d.-]+[\s,]+[\d.-]+[\s,]+(?<width>[\d.]+)[\s,]+(?<height>[\d.]+)["']/u;
|
|
17646
|
-
var parseSvgLength = (value) => {
|
|
17647
|
-
const length = Number(value?.match(SVG_LENGTH)?.groups?.value);
|
|
17648
|
-
return length > 0 ? length : undefined;
|
|
17649
|
-
};
|
|
17650
17843
|
var svgDimensions = (svg) => {
|
|
17651
|
-
|
|
17652
|
-
|
|
17653
|
-
|
|
17654
|
-
|
|
17655
|
-
|
|
17844
|
+
if (!svg) {
|
|
17845
|
+
return;
|
|
17846
|
+
}
|
|
17847
|
+
try {
|
|
17848
|
+
const { height, width } = imageSize2(Buffer.from(svg));
|
|
17849
|
+
return height && width ? { height, width } : undefined;
|
|
17850
|
+
} catch {
|
|
17851
|
+
return;
|
|
17656
17852
|
}
|
|
17657
|
-
const viewBox = attributes?.match(SVG_VIEW_BOX);
|
|
17658
|
-
const viewBoxWidth = Number(viewBox?.groups?.width);
|
|
17659
|
-
const viewBoxHeight = Number(viewBox?.groups?.height);
|
|
17660
|
-
return viewBoxWidth > 0 && viewBoxHeight > 0 ? { height: viewBoxHeight, width: viewBoxWidth } : undefined;
|
|
17661
17853
|
};
|
|
17662
17854
|
var readLogoSvg = (project, source) => {
|
|
17663
17855
|
if (!source?.toLowerCase().endsWith(".svg")) {
|
|
@@ -17676,7 +17868,7 @@ var resolveLogo = (project) => {
|
|
|
17676
17868
|
return null;
|
|
17677
17869
|
}
|
|
17678
17870
|
const config = typeof logo === "string" ? { image: logo } : logo;
|
|
17679
|
-
const { href, image: source, text
|
|
17871
|
+
const { href, image: source, text } = config;
|
|
17680
17872
|
const image = typeof source === "string" ? { light: source } : source;
|
|
17681
17873
|
const light = image?.light ?? image?.dark;
|
|
17682
17874
|
const dark = image?.dark ?? image?.light;
|
|
@@ -17685,12 +17877,12 @@ var resolveLogo = (project) => {
|
|
|
17685
17877
|
const lightSvg = readLogoSvg(project, light);
|
|
17686
17878
|
const darkSvg = dark === light ? lightSvg : readLogoSvg(project, dark);
|
|
17687
17879
|
if (light && light === dark && lightSvg) {
|
|
17688
|
-
return { alt, href: brandHref, svg: lightSvg, text
|
|
17880
|
+
return { alt, href: brandHref, svg: lightSvg, text };
|
|
17689
17881
|
}
|
|
17690
17882
|
const lightDimensions = svgDimensions(lightSvg);
|
|
17691
17883
|
const darkDimensions = svgDimensions(darkSvg);
|
|
17692
17884
|
const dimensions = lightDimensions || darkDimensions ? { dark: darkDimensions, light: lightDimensions } : undefined;
|
|
17693
|
-
return { alt, dark, dimensions, href: brandHref, light, text
|
|
17885
|
+
return { alt, dark, dimensions, href: brandHref, light, text };
|
|
17694
17886
|
};
|
|
17695
17887
|
var FAVICON_CANDIDATES = [
|
|
17696
17888
|
"icon.svg",
|
|
@@ -17836,6 +18028,10 @@ var buildRuntimeData = (project) => {
|
|
|
17836
18028
|
codeWrap: config.markdown.code.wrap,
|
|
17837
18029
|
dateFormat: config.dateFormat,
|
|
17838
18030
|
description: config.description,
|
|
18031
|
+
discovery: {
|
|
18032
|
+
agentReadability: config.seo.agentReadability,
|
|
18033
|
+
llmsTxt: config.ai.llmsTxt.enabled
|
|
18034
|
+
},
|
|
17839
18035
|
favicon: resolveFavicon(project),
|
|
17840
18036
|
feedback: config.feedback,
|
|
17841
18037
|
i18n: i18n ? {
|
|
@@ -17974,7 +18170,7 @@ var writeAskFiles = async (project, srcDir, write) => {
|
|
|
17974
18170
|
await write(join28(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
|
|
17975
18171
|
`);
|
|
17976
18172
|
}
|
|
17977
|
-
await write(join28(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
|
|
18173
|
+
await write(join28(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded, ask.instructions));
|
|
17978
18174
|
};
|
|
17979
18175
|
var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
17980
18176
|
if (routeIsTaken(pages, contentPages, "/404")) {
|
|
@@ -18211,7 +18407,7 @@ var generateRuntime = async (project) => {
|
|
|
18211
18407
|
...islandDiscovery.islands.map((island) => island.name),
|
|
18212
18408
|
...overrideTags
|
|
18213
18409
|
]);
|
|
18214
|
-
warnings.push(...validateUsedComponents(project.graph.pages, knownComponentTags, new Set(registry.map((item) => item.name))).map(diagnosticWarning), ...searchProviderWarnings(config.search.provider, context.root), ...deploymentAdapterWarnings(config.deployment, context.root), ...islandFrameworkWarnings(frameworks, context.root));
|
|
18410
|
+
warnings.push(...validateUsedComponents(project.graph.pages, knownComponentTags, new Set(registry.map((item) => item.name))).map(diagnosticWarning), ...searchProviderWarnings(config.search.provider, context.root), ...askProviderWarnings(config.ai.ask, context.root), ...deploymentAdapterWarnings(config.deployment, context.root), ...islandFrameworkWarnings(frameworks, context.root));
|
|
18215
18411
|
if (hasScalarReferences(config)) {
|
|
18216
18412
|
const references = await buildReferenceFiles({
|
|
18217
18413
|
config,
|
|
@@ -18235,37 +18431,24 @@ var generateRuntime = async (project) => {
|
|
|
18235
18431
|
};
|
|
18236
18432
|
|
|
18237
18433
|
// src/cli/env.ts
|
|
18238
|
-
import { existsSync as existsSync17
|
|
18239
|
-
import {
|
|
18434
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
18435
|
+
import { config } from "dotenv";
|
|
18240
18436
|
import { dirname as dirname12, join as join29, resolve as resolve9 } from "pathe";
|
|
18241
|
-
var applyEnv = (parsed) => {
|
|
18242
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
18243
|
-
if (!(key in process.env)) {
|
|
18244
|
-
process.env[key] = value;
|
|
18245
|
-
}
|
|
18246
|
-
}
|
|
18247
|
-
};
|
|
18248
|
-
var loadFile = (path) => {
|
|
18249
|
-
try {
|
|
18250
|
-
if (existsSync17(path)) {
|
|
18251
|
-
applyEnv(parse2(readFileSync9(path, "utf-8")));
|
|
18252
|
-
}
|
|
18253
|
-
} catch {}
|
|
18254
|
-
};
|
|
18255
18437
|
var loadEnvFiles = (startDir) => {
|
|
18438
|
+
const paths = [];
|
|
18256
18439
|
let dir = resolve9(startDir);
|
|
18257
18440
|
let done = false;
|
|
18258
18441
|
while (!done) {
|
|
18259
|
-
|
|
18260
|
-
loadFile(join29(dir, ".env"));
|
|
18442
|
+
paths.push(join29(dir, ".env.local"), join29(dir, ".env"));
|
|
18261
18443
|
const parent = dirname12(dir);
|
|
18262
18444
|
done = existsSync17(join29(dir, ".git")) || parent === dir;
|
|
18263
18445
|
dir = parent;
|
|
18264
18446
|
}
|
|
18447
|
+
config({ path: paths, quiet: true });
|
|
18265
18448
|
};
|
|
18266
18449
|
|
|
18267
18450
|
// src/cli/required-secrets.ts
|
|
18268
|
-
var checkRequiredSecrets = (
|
|
18451
|
+
var checkRequiredSecrets = (config2) => {
|
|
18269
18452
|
const diagnostics = [];
|
|
18270
18453
|
const requireSecret = (feature, env, note) => {
|
|
18271
18454
|
if (process.env[env]) {
|
|
@@ -18279,15 +18462,15 @@ var checkRequiredSecrets = (config) => {
|
|
|
18279
18462
|
suggestion: `Set ${env} in .env.local for local dev, or in your host's environment for production.`
|
|
18280
18463
|
});
|
|
18281
18464
|
};
|
|
18282
|
-
if (
|
|
18283
|
-
const backend = resolveAskBackend(
|
|
18465
|
+
if (config2.ai.ask?.enabled && !config2.ai.ask.endpoint) {
|
|
18466
|
+
const backend = resolveAskBackend(config2.ai.ask);
|
|
18284
18467
|
if (backend.kind === "gateway") {
|
|
18285
18468
|
requireSecret("Ask AI (AI Gateway)", "AI_GATEWAY_API_KEY", "on Vercel the gateway can also authenticate via OIDC");
|
|
18286
18469
|
} else {
|
|
18287
18470
|
requireSecret("Ask AI", backend.apiKeyEnv);
|
|
18288
18471
|
}
|
|
18289
18472
|
}
|
|
18290
|
-
if (
|
|
18473
|
+
if (config2.search.provider === "mixedbread") {
|
|
18291
18474
|
requireSecret("Mixedbread search", "MIXEDBREAD_API_KEY");
|
|
18292
18475
|
}
|
|
18293
18476
|
return diagnostics;
|
|
@@ -18359,9 +18542,9 @@ var validateBudgetFlags = (args) => {
|
|
|
18359
18542
|
}
|
|
18360
18543
|
}
|
|
18361
18544
|
};
|
|
18362
|
-
var emitRedirectFiles = async (
|
|
18363
|
-
const redirects = platformRedirects(
|
|
18364
|
-
if (redirects.length === 0 ||
|
|
18545
|
+
var emitRedirectFiles = async (config2, distDir) => {
|
|
18546
|
+
const redirects = platformRedirects(config2);
|
|
18547
|
+
if (redirects.length === 0 || config2.deployment.output !== "static") {
|
|
18365
18548
|
return;
|
|
18366
18549
|
}
|
|
18367
18550
|
await writeFile7(join30(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
|
|
@@ -18373,11 +18556,11 @@ var emitRedirectFiles = async (config, distDir) => {
|
|
|
18373
18556
|
logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
|
|
18374
18557
|
};
|
|
18375
18558
|
var emitHeaderFiles = async (project, distDir) => {
|
|
18376
|
-
const { config } = project;
|
|
18377
|
-
if (!readsHeaderFiles(
|
|
18559
|
+
const { config: config2 } = project;
|
|
18560
|
+
if (!readsHeaderFiles(config2.deployment) || existsSync18(join30(project.context.root, "public", "_headers"))) {
|
|
18378
18561
|
return;
|
|
18379
18562
|
}
|
|
18380
|
-
const ours = buildNetlifyHeaders(
|
|
18563
|
+
const ours = buildNetlifyHeaders(config2, buildHomeLinkHeader(config2, markdownRoutePaths(project)));
|
|
18381
18564
|
const target = join30(distDir, "_headers");
|
|
18382
18565
|
const existing = existsSync18(target) ? await readFile17(target, "utf-8") : "";
|
|
18383
18566
|
await writeFile7(target, existing ? `${existing.trimEnd()}
|
|
@@ -18414,15 +18597,15 @@ var emitAgentSkills = async (project, distDir) => {
|
|
|
18414
18597
|
await writeFile7(join30(outDir, "index.json"), buildSkillsIndex(skills, project.config), "utf-8");
|
|
18415
18598
|
logger.success(`Published ${skills.length} agent skill(s) (.well-known/agent-skills/index.json)`);
|
|
18416
18599
|
};
|
|
18417
|
-
var emitWellKnownFiles = async (
|
|
18600
|
+
var emitWellKnownFiles = async (config2, distDir) => {
|
|
18418
18601
|
const files = [
|
|
18419
18602
|
{
|
|
18420
|
-
content: buildSignaturesDirectory(
|
|
18603
|
+
content: buildSignaturesDirectory(config2),
|
|
18421
18604
|
label: "Web Bot Auth",
|
|
18422
18605
|
path: SIGNATURES_DIRECTORY_PATH
|
|
18423
18606
|
},
|
|
18424
18607
|
{
|
|
18425
|
-
content: buildApiCatalog(
|
|
18608
|
+
content: buildApiCatalog(config2),
|
|
18426
18609
|
label: "RFC 9727",
|
|
18427
18610
|
path: API_CATALOG_PATH
|
|
18428
18611
|
}
|
|
@@ -18438,18 +18621,18 @@ var emitWellKnownFiles = async (config, distDir) => {
|
|
|
18438
18621
|
}
|
|
18439
18622
|
};
|
|
18440
18623
|
var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
18441
|
-
const { config } = project;
|
|
18624
|
+
const { config: config2 } = project;
|
|
18442
18625
|
const configPath = join30(root, ".vercel", "output", "config.json");
|
|
18443
18626
|
if (!existsSync18(configPath)) {
|
|
18444
18627
|
return;
|
|
18445
18628
|
}
|
|
18446
18629
|
const overrides = {
|
|
18447
|
-
...hasApiCatalog(
|
|
18448
|
-
...
|
|
18630
|
+
...hasApiCatalog(config2) ? { [API_CATALOG_PATH.slice(1)]: API_CATALOG_TYPE } : {},
|
|
18631
|
+
...config2.ai.webBotAuth.keys.length > 0 ? { [SIGNATURES_DIRECTORY_PATH.slice(1)]: SIGNATURES_DIRECTORY_TYPE } : {}
|
|
18449
18632
|
};
|
|
18450
18633
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
18451
18634
|
const home = rawMarkdown["/"];
|
|
18452
|
-
const injected = injectNegotiationRoutes(await readFile17(configPath, "utf-8"), routePaths, buildHomeLinkHeader(
|
|
18635
|
+
const injected = injectNegotiationRoutes(await readFile17(configPath, "utf-8"), routePaths, buildHomeLinkHeader(config2, routePaths), overrides, home ? markdownTokenCount(agentMarkdown(home)) : undefined);
|
|
18453
18636
|
if (injected === null) {
|
|
18454
18637
|
logger.warn("Could not wire Accept: text/markdown negotiation into .vercel/output/config.json — raw Markdown stays available at the .md URLs.");
|
|
18455
18638
|
return;
|
|
@@ -18459,7 +18642,7 @@ var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
|
18459
18642
|
};
|
|
18460
18643
|
var warnCloudflareNegotiationSkipped = () => logger.warn("Could not wire Accept: text/markdown negotiation into dist/server/wrangler.json — raw Markdown stays available at the .md URLs.");
|
|
18461
18644
|
var emitCloudflareNegotiation = async (project, routePaths) => {
|
|
18462
|
-
const { config, context } = project;
|
|
18645
|
+
const { config: config2, context } = project;
|
|
18463
18646
|
const serverDir = join30(context.distDir ?? join30(context.root, "dist"), "server");
|
|
18464
18647
|
const wranglerPath = join30(serverDir, "wrangler.json");
|
|
18465
18648
|
if (!existsSync18(wranglerPath)) {
|
|
@@ -18469,11 +18652,11 @@ var emitCloudflareNegotiation = async (project, routePaths) => {
|
|
|
18469
18652
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
18470
18653
|
const home = rawMarkdown["/"];
|
|
18471
18654
|
const injected = injectWorkerNegotiation(await readFile17(wranglerPath, "utf-8"), {
|
|
18472
|
-
base:
|
|
18655
|
+
base: config2.deployment.base,
|
|
18473
18656
|
contentRoutePaths: project.manifest.routes.map((route) => route.path),
|
|
18474
|
-
homeLinkHeader: buildHomeLinkHeader(
|
|
18657
|
+
homeLinkHeader: buildHomeLinkHeader(config2, routePaths),
|
|
18475
18658
|
homeTokens: home ? markdownTokenCount(agentMarkdown(home)) : undefined,
|
|
18476
|
-
redirects: platformRedirects(
|
|
18659
|
+
redirects: platformRedirects(config2),
|
|
18477
18660
|
routePaths
|
|
18478
18661
|
});
|
|
18479
18662
|
if (injected === null) {
|
|
@@ -18549,20 +18732,20 @@ var runClientAssetChecks = async (staticDir, args) => {
|
|
|
18549
18732
|
process.exit(1);
|
|
18550
18733
|
}
|
|
18551
18734
|
};
|
|
18552
|
-
var isolatedOutputDir = (
|
|
18553
|
-
const { adapter, output } =
|
|
18735
|
+
var isolatedOutputDir = (config2, context) => {
|
|
18736
|
+
const { adapter, output } = config2.deployment;
|
|
18554
18737
|
if (output === "server" && adapter === "vercel") {
|
|
18555
18738
|
return join30(context.outDir, ".vercel", "output");
|
|
18556
18739
|
}
|
|
18557
18740
|
return context.distDir ?? join30(context.outDir, "dist");
|
|
18558
18741
|
};
|
|
18559
|
-
var isolatedStaticDir = (
|
|
18560
|
-
const { adapter, output } =
|
|
18561
|
-
const outputDir = isolatedOutputDir(
|
|
18742
|
+
var isolatedStaticDir = (config2, context) => {
|
|
18743
|
+
const { adapter, output } = config2.deployment;
|
|
18744
|
+
const outputDir = isolatedOutputDir(config2, context);
|
|
18562
18745
|
if (output === "server" && adapter === "vercel") {
|
|
18563
18746
|
return join30(outputDir, "static");
|
|
18564
18747
|
}
|
|
18565
|
-
if (servesClientSubdir(
|
|
18748
|
+
if (servesClientSubdir(config2.deployment)) {
|
|
18566
18749
|
return join30(outputDir, "client");
|
|
18567
18750
|
}
|
|
18568
18751
|
return outputDir;
|
|
@@ -18603,10 +18786,10 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
18603
18786
|
if (project.config.ai.llmsTxt.enabled) {
|
|
18604
18787
|
await publishLlmsFiles(project, distDir);
|
|
18605
18788
|
}
|
|
18606
|
-
const
|
|
18607
|
-
if (
|
|
18608
|
-
await writeFile7(join30(distDir,
|
|
18609
|
-
logger.success("Generated sitemap.xml");
|
|
18789
|
+
const sitemapFiles = buildSitemapFiles(project);
|
|
18790
|
+
if (sitemapFiles && !existsSync18(join30(distDir, "sitemap.xml"))) {
|
|
18791
|
+
await Promise.all(sitemapFiles.map((file) => writeFile7(join30(distDir, file.name), file.xml, "utf-8")));
|
|
18792
|
+
logger.success(sitemapFiles.length === 1 ? "Generated sitemap.xml" : `Generated sitemap.xml (index of ${sitemapFiles.length - 1} sitemap files)`);
|
|
18610
18793
|
}
|
|
18611
18794
|
const robots = buildRobots(project);
|
|
18612
18795
|
if (robots && !existsSync18(join30(distDir, "robots.txt"))) {
|
|
@@ -18623,19 +18806,19 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
18623
18806
|
await emitAgentSkills(project, distDir);
|
|
18624
18807
|
await emitRedirectFiles(project.config, distDir);
|
|
18625
18808
|
await emitHeaderFiles(project, distDir);
|
|
18626
|
-
const { config } = project;
|
|
18627
|
-
const features = serverFeatures(
|
|
18628
|
-
const sitemapNote =
|
|
18809
|
+
const { config: config2 } = project;
|
|
18810
|
+
const features = serverFeatures(config2);
|
|
18811
|
+
const sitemapNote = config2.seo.sitemap ? "no (set deployment.site)" : "no (seo.sitemap is false)";
|
|
18629
18812
|
logger.box([
|
|
18630
|
-
`Output ${
|
|
18631
|
-
`Adapter ${
|
|
18632
|
-
`Site ${
|
|
18633
|
-
`Search ${
|
|
18634
|
-
`Redirects ${
|
|
18635
|
-
`Sitemap ${
|
|
18813
|
+
`Output ${config2.deployment.output}`,
|
|
18814
|
+
`Adapter ${config2.deployment.adapter ?? "none"}`,
|
|
18815
|
+
`Site ${config2.deployment.site ?? "not set"}`,
|
|
18816
|
+
`Search ${config2.search.provider}`,
|
|
18817
|
+
`Redirects ${config2.redirects.length}`,
|
|
18818
|
+
`Sitemap ${sitemapFiles ? "yes" : sitemapNote}`,
|
|
18636
18819
|
`Robots ${robots ? "yes" : "no"}`,
|
|
18637
18820
|
`Agent JSON ${agentReadability ? "yes" : "no"}`,
|
|
18638
|
-
`LLM files ${
|
|
18821
|
+
`LLM files ${config2.ai.llmsTxt.enabled ? "yes" : "no"}`,
|
|
18639
18822
|
`Server features ${features.length > 0 ? features.join(", ") : "none"}`
|
|
18640
18823
|
].join(`
|
|
18641
18824
|
`));
|
|
@@ -18808,6 +18991,7 @@ var checkCommand = defineCommand4({
|
|
|
18808
18991
|
import { dev } from "astro";
|
|
18809
18992
|
import { watch } from "chokidar";
|
|
18810
18993
|
import { defineCommand as defineCommand5 } from "citty";
|
|
18994
|
+
import { debounce } from "perfect-debounce";
|
|
18811
18995
|
|
|
18812
18996
|
// src/astro/integration.ts
|
|
18813
18997
|
var overlayServer = null;
|
|
@@ -18862,29 +19046,6 @@ var parsePort = (value) => {
|
|
|
18862
19046
|
return port;
|
|
18863
19047
|
};
|
|
18864
19048
|
|
|
18865
|
-
// src/cli/coalesce.ts
|
|
18866
|
-
var coalescedRunner = (task) => {
|
|
18867
|
-
let inFlight = null;
|
|
18868
|
-
let pending = false;
|
|
18869
|
-
const cycle = async () => {
|
|
18870
|
-
try {
|
|
18871
|
-
do {
|
|
18872
|
-
pending = false;
|
|
18873
|
-
await task();
|
|
18874
|
-
} while (pending);
|
|
18875
|
-
} finally {
|
|
18876
|
-
inFlight = null;
|
|
18877
|
-
}
|
|
18878
|
-
};
|
|
18879
|
-
return () => {
|
|
18880
|
-
if (inFlight) {
|
|
18881
|
-
pending = true;
|
|
18882
|
-
return;
|
|
18883
|
-
}
|
|
18884
|
-
inFlight = cycle();
|
|
18885
|
-
};
|
|
18886
|
-
};
|
|
18887
|
-
|
|
18888
19049
|
// src/cli/commands/dev.ts
|
|
18889
19050
|
var normalizeHost = (host) => host === "" ? true : host ?? false;
|
|
18890
19051
|
var routeSignature = (routes) => routes.map((route) => `${route.path} ${route.entryId}`).toSorted().join(`
|
|
@@ -18952,7 +19113,7 @@ var devCommand = defineCommand5({
|
|
|
18952
19113
|
}
|
|
18953
19114
|
showBlumeErrorOverlay(project.diagnostics);
|
|
18954
19115
|
let lastSignature = routeSignature(project.manifest.routes);
|
|
18955
|
-
const
|
|
19116
|
+
const regenerate = debounce(async () => {
|
|
18956
19117
|
try {
|
|
18957
19118
|
const next = await scanProject(root, {
|
|
18958
19119
|
devServerUrl,
|
|
@@ -18975,16 +19136,9 @@ var devCommand = defineCommand5({
|
|
|
18975
19136
|
} catch (error) {
|
|
18976
19137
|
logger.error(`Regeneration failed: ${error.message}`);
|
|
18977
19138
|
}
|
|
18978
|
-
});
|
|
18979
|
-
let timer = null;
|
|
18980
|
-
const regenerate = () => {
|
|
18981
|
-
if (timer) {
|
|
18982
|
-
clearTimeout(timer);
|
|
18983
|
-
}
|
|
18984
|
-
timer = setTimeout(runRegenerate, 80);
|
|
18985
|
-
};
|
|
19139
|
+
}, 80);
|
|
18986
19140
|
if (boundPort !== port) {
|
|
18987
|
-
|
|
19141
|
+
regenerate();
|
|
18988
19142
|
}
|
|
18989
19143
|
const dirTargets = [project.context.pagesRoot].filter((target) => target !== null);
|
|
18990
19144
|
const fileTargets = [
|
|
@@ -19013,14 +19167,14 @@ var devCommand = defineCommand5({
|
|
|
19013
19167
|
});
|
|
19014
19168
|
|
|
19015
19169
|
// src/cli/commands/doctor.ts
|
|
19016
|
-
import { readFileSync as
|
|
19170
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
19017
19171
|
import { defineCommand as defineCommand6 } from "citty";
|
|
19018
19172
|
import { join as join32 } from "pathe";
|
|
19019
19173
|
import { satisfies } from "semver";
|
|
19020
19174
|
var FALLBACK_NODE_RANGE = ">=22.12.0";
|
|
19021
19175
|
var supportedNodeRange = () => {
|
|
19022
19176
|
try {
|
|
19023
|
-
const pkg = JSON.parse(
|
|
19177
|
+
const pkg = JSON.parse(readFileSync9(join32(packageRoot(), "package.json"), "utf-8"));
|
|
19024
19178
|
return pkg.engines?.node || FALLBACK_NODE_RANGE;
|
|
19025
19179
|
} catch {
|
|
19026
19180
|
return FALLBACK_NODE_RANGE;
|
|
@@ -19051,9 +19205,9 @@ var doctorCommand = defineCommand6({
|
|
|
19051
19205
|
try {
|
|
19052
19206
|
const project = await scanProject(root, { mode: "build" });
|
|
19053
19207
|
diagnostics.push(...project.diagnostics);
|
|
19054
|
-
const { config } = project;
|
|
19055
|
-
const features = serverFeatures(
|
|
19056
|
-
if (features.length > 0 &&
|
|
19208
|
+
const { config: config2 } = project;
|
|
19209
|
+
const features = serverFeatures(config2);
|
|
19210
|
+
if (features.length > 0 && config2.deployment.output === "static") {
|
|
19057
19211
|
diagnostics.push({
|
|
19058
19212
|
code: "BLUME_SERVER_FEATURE_REQUIRED",
|
|
19059
19213
|
message: `${features.join(", ")} require server output.`,
|
|
@@ -19061,7 +19215,7 @@ var doctorCommand = defineCommand6({
|
|
|
19061
19215
|
suggestion: 'Set deployment.output to "server".'
|
|
19062
19216
|
});
|
|
19063
19217
|
}
|
|
19064
|
-
if (
|
|
19218
|
+
if (config2.deployment.output === "server" && !config2.deployment.adapter) {
|
|
19065
19219
|
diagnostics.push({
|
|
19066
19220
|
code: "BLUME_ADAPTER_REQUIRED",
|
|
19067
19221
|
message: "Server output requires an adapter.",
|
|
@@ -19071,8 +19225,8 @@ var doctorCommand = defineCommand6({
|
|
|
19071
19225
|
}
|
|
19072
19226
|
if (!args.json) {
|
|
19073
19227
|
logger.info(`Pages: ${project.graph.pages.length}`);
|
|
19074
|
-
logger.info(`Output: ${
|
|
19075
|
-
logger.info(`Search: ${
|
|
19228
|
+
logger.info(`Output: ${config2.deployment.output}`);
|
|
19229
|
+
logger.info(`Search: ${config2.search.provider}`);
|
|
19076
19230
|
}
|
|
19077
19231
|
} catch (error) {
|
|
19078
19232
|
if (error instanceof BlumeError) {
|
|
@@ -19218,16 +19372,16 @@ var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
|
|
|
19218
19372
|
path: join33(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
|
|
19219
19373
|
}
|
|
19220
19374
|
] : [];
|
|
19221
|
-
var ejectIntegrationBridge = (
|
|
19375
|
+
var ejectIntegrationBridge = (config2, root, configFile) => config2.integrations.length > 0 && configFile ? { configFile: toPosix(relative15(root, configFile)) } : undefined;
|
|
19222
19376
|
var eject = async (root) => {
|
|
19223
19377
|
const project = await scanProject(root, { mode: "build" });
|
|
19224
|
-
const { context, config } = project;
|
|
19378
|
+
const { context, config: config2 } = project;
|
|
19225
19379
|
const srcDir = join33(root, "src");
|
|
19226
19380
|
const genDir = join33(srcDir, "generated");
|
|
19227
|
-
const askEnabled =
|
|
19228
|
-
const exportPdf =
|
|
19229
|
-
const exportEpub =
|
|
19230
|
-
const examples = await discoverExamples(root,
|
|
19381
|
+
const askEnabled = config2.ai.ask?.enabled ?? false;
|
|
19382
|
+
const exportPdf = config2.export.pdf;
|
|
19383
|
+
const exportEpub = config2.export.epub;
|
|
19384
|
+
const examples = await discoverExamples(root, config2.examples.source);
|
|
19231
19385
|
project.examples = exampleMarkdownLookup(examples.examples);
|
|
19232
19386
|
const [
|
|
19233
19387
|
pages,
|
|
@@ -19242,7 +19396,7 @@ var eject = async (root) => {
|
|
|
19242
19396
|
detectNeedsReact(root),
|
|
19243
19397
|
detectUsesMath(root),
|
|
19244
19398
|
context.themeFile ? readFile18(context.themeFile, "utf-8") : Promise.resolve(""),
|
|
19245
|
-
readExamplesCss(root,
|
|
19399
|
+
readExamplesCss(root, config2.examples.css),
|
|
19246
19400
|
buildRawMarkdown(project),
|
|
19247
19401
|
discoverIslands(root)
|
|
19248
19402
|
]);
|
|
@@ -19274,13 +19428,13 @@ var eject = async (root) => {
|
|
|
19274
19428
|
{
|
|
19275
19429
|
content: astroConfigTemplate({
|
|
19276
19430
|
askPath: "./src/generated/Ask.astro",
|
|
19277
|
-
config,
|
|
19431
|
+
config: config2,
|
|
19278
19432
|
contentRoutes: project.manifest.routes.map((route) => route.path),
|
|
19279
19433
|
context: relContext,
|
|
19280
19434
|
dataPath: "./src/generated/data.json",
|
|
19281
19435
|
examplesPath: "./src/generated/examples.ts",
|
|
19282
19436
|
examplesThemePath: "./src/generated/examples.css",
|
|
19283
|
-
integrationBridge: ejectIntegrationBridge(
|
|
19437
|
+
integrationBridge: ejectIntegrationBridge(config2, root, context.configFile),
|
|
19284
19438
|
needsReact,
|
|
19285
19439
|
needsSvelte,
|
|
19286
19440
|
needsVue,
|
|
@@ -19299,7 +19453,7 @@ var eject = async (root) => {
|
|
|
19299
19453
|
{ content: envTemplate(), path: join33(srcDir, "env.d.ts") },
|
|
19300
19454
|
{
|
|
19301
19455
|
content: contentConfigTemplate({
|
|
19302
|
-
config,
|
|
19456
|
+
config: config2,
|
|
19303
19457
|
context: relContext,
|
|
19304
19458
|
staged: hasStaged,
|
|
19305
19459
|
stagedBase: stagedDir
|
|
@@ -19324,12 +19478,12 @@ var eject = async (root) => {
|
|
|
19324
19478
|
path: join33(genDir, "islands.ts")
|
|
19325
19479
|
},
|
|
19326
19480
|
{
|
|
19327
|
-
content: exampleMapTemplate(examples.examples,
|
|
19481
|
+
content: exampleMapTemplate(examples.examples, config2.basePath),
|
|
19328
19482
|
path: join33(genDir, "examples.ts")
|
|
19329
19483
|
},
|
|
19330
19484
|
{
|
|
19331
19485
|
content: examplesEntryTemplate({
|
|
19332
|
-
configTokens: buildThemeCss(
|
|
19486
|
+
configTokens: buildThemeCss(config2.theme),
|
|
19333
19487
|
sources: ["../../**/*.{astro,jsx,svelte,ts,tsx,vue}"],
|
|
19334
19488
|
userCss: userExamplesCss
|
|
19335
19489
|
}),
|
|
@@ -19337,7 +19491,7 @@ var eject = async (root) => {
|
|
|
19337
19491
|
},
|
|
19338
19492
|
{
|
|
19339
19493
|
content: tailwindEntryTemplate({
|
|
19340
|
-
configTokens: buildThemeCss(
|
|
19494
|
+
configTokens: buildThemeCss(config2.theme),
|
|
19341
19495
|
sources: [
|
|
19342
19496
|
blumeSourceGlob(root, genDir),
|
|
19343
19497
|
"../../**/*.{astro,mdx,ts,tsx}"
|
|
@@ -19374,9 +19528,9 @@ var eject = async (root) => {
|
|
|
19374
19528
|
if (askEnabled) {
|
|
19375
19529
|
files.push(...await askFiles(project, srcDir, genDir));
|
|
19376
19530
|
}
|
|
19377
|
-
if (
|
|
19531
|
+
if (config2.seo.og.enabled) {
|
|
19378
19532
|
files.push({
|
|
19379
|
-
content: ogEndpointTemplate(customOgRoutes(pages,
|
|
19533
|
+
content: ogEndpointTemplate(customOgRoutes(pages, config2.title, config2.seo.og.titles)),
|
|
19380
19534
|
path: join33(srcDir, "pages", "og", "[...slug].png.ts")
|
|
19381
19535
|
});
|
|
19382
19536
|
}
|
|
@@ -19393,10 +19547,10 @@ var eject = async (root) => {
|
|
|
19393
19547
|
});
|
|
19394
19548
|
}
|
|
19395
19549
|
files.push({
|
|
19396
|
-
content: searchClientTemplate(
|
|
19550
|
+
content: searchClientTemplate(config2),
|
|
19397
19551
|
path: join33(genDir, "search-client.ts")
|
|
19398
19552
|
});
|
|
19399
|
-
if (servesStaticIndex(
|
|
19553
|
+
if (servesStaticIndex(config2.search.provider)) {
|
|
19400
19554
|
const documents = await buildSearchDocuments(project);
|
|
19401
19555
|
files.push({
|
|
19402
19556
|
content: `${JSON.stringify(documents)}
|
|
@@ -19407,9 +19561,9 @@ var eject = async (root) => {
|
|
|
19407
19561
|
path: join33(srcDir, "pages", "blume-search.json.ts")
|
|
19408
19562
|
});
|
|
19409
19563
|
}
|
|
19410
|
-
if (
|
|
19564
|
+
if (config2.search.provider === "mixedbread") {
|
|
19411
19565
|
files.push({
|
|
19412
|
-
content: mixedbreadSearchEndpointTemplate(
|
|
19566
|
+
content: mixedbreadSearchEndpointTemplate(config2.search.mixedbread?.storeId ?? ""),
|
|
19413
19567
|
path: join33(srcDir, "pages", "api", "search.ts")
|
|
19414
19568
|
});
|
|
19415
19569
|
}
|
|
@@ -19426,9 +19580,9 @@ var eject = async (root) => {
|
|
|
19426
19580
|
});
|
|
19427
19581
|
}
|
|
19428
19582
|
const warnings = [];
|
|
19429
|
-
if (hasScalarReferences(
|
|
19583
|
+
if (hasScalarReferences(config2)) {
|
|
19430
19584
|
const references = await buildReferenceFiles({
|
|
19431
|
-
config,
|
|
19585
|
+
config: config2,
|
|
19432
19586
|
contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
|
|
19433
19587
|
root
|
|
19434
19588
|
});
|
|
@@ -19446,7 +19600,7 @@ var eject = async (root) => {
|
|
|
19446
19600
|
})), ...examples.examples.map((example) => ({
|
|
19447
19601
|
content: exampleWrapperTemplate(example),
|
|
19448
19602
|
path: join33(genDir, "examples", `${exampleSlug(example.path)}.astro`)
|
|
19449
|
-
})), ...examplesPreviewFiles(srcDir,
|
|
19603
|
+
})), ...examplesPreviewFiles(srcDir, config2.basePath, examples.examples.length > 0));
|
|
19450
19604
|
for (const [entryId, content] of staged) {
|
|
19451
19605
|
files.push({ content, path: join33(root, stagedDir, entryId) });
|
|
19452
19606
|
}
|
|
@@ -19468,27 +19622,27 @@ var eject = async (root) => {
|
|
|
19468
19622
|
// src/cli/eject-scripts.ts
|
|
19469
19623
|
import { readFile as readFile19, writeFile as writeFile9 } from "node:fs/promises";
|
|
19470
19624
|
import { join as join34 } from "pathe";
|
|
19471
|
-
var droppedArtifactNotices = (
|
|
19625
|
+
var droppedArtifactNotices = (config2) => {
|
|
19472
19626
|
const notices = [];
|
|
19473
|
-
if (
|
|
19627
|
+
if (config2.search.provider === "pagefind") {
|
|
19474
19628
|
notices.push('the Pagefind search index — the search UI loads it from the built site, so search will break in production. Add a post-build step: `"build": "astro build && pagefind --site dist"` (with `pagefind` installed as a devDependency).');
|
|
19475
19629
|
}
|
|
19476
|
-
if (searchProviderMeta(
|
|
19477
|
-
notices.push(`the hosted ${
|
|
19630
|
+
if (searchProviderMeta(config2.search.provider).syncs) {
|
|
19631
|
+
notices.push(`the hosted ${config2.search.provider} index sync — new and updated pages stop being pushed; re-upload your search records after each build with the provider's API or CLI.`);
|
|
19478
19632
|
}
|
|
19479
|
-
if (
|
|
19633
|
+
if (config2.ai.llmsTxt.enabled) {
|
|
19480
19634
|
notices.push("llms.txt and llms-full.txt");
|
|
19481
19635
|
}
|
|
19482
|
-
if (
|
|
19636
|
+
if (config2.deployment.site && config2.seo.sitemap) {
|
|
19483
19637
|
notices.push("sitemap.xml — recreate it with the @astrojs/sitemap integration.");
|
|
19484
19638
|
}
|
|
19485
|
-
if (
|
|
19639
|
+
if (config2.seo.robots) {
|
|
19486
19640
|
notices.push("robots.txt — recreate it as a public/robots.txt file.");
|
|
19487
19641
|
}
|
|
19488
|
-
if (
|
|
19642
|
+
if (config2.seo.agentReadability) {
|
|
19489
19643
|
notices.push("agent-readability.json");
|
|
19490
19644
|
}
|
|
19491
|
-
if (
|
|
19645
|
+
if (config2.redirects.length > 0 && config2.deployment.output === "static") {
|
|
19492
19646
|
notices.push("the platform redirect files (_redirects, vercel.json) — your redirects still work as Astro-generated meta-refresh pages.");
|
|
19493
19647
|
}
|
|
19494
19648
|
return notices;
|
|
@@ -19515,6 +19669,7 @@ var updatePackageScripts = async (root) => {
|
|
|
19515
19669
|
// src/cli/init/scaffold.ts
|
|
19516
19670
|
import { existsSync as existsSync21 } from "node:fs";
|
|
19517
19671
|
import { mkdir as mkdir10, writeFile as writeFile10 } from "node:fs/promises";
|
|
19672
|
+
import { detect } from "package-manager-detector/detect";
|
|
19518
19673
|
import { basename as basename4, dirname as dirname14, isAbsolute as isAbsolute9, join as join35, relative as relative16 } from "pathe";
|
|
19519
19674
|
|
|
19520
19675
|
// src/core/package-json.ts
|
|
@@ -19643,6 +19798,11 @@ var detectPackageManager = (userAgent) => {
|
|
|
19643
19798
|
const name = userAgent?.split("/")[0];
|
|
19644
19799
|
return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : "npm";
|
|
19645
19800
|
};
|
|
19801
|
+
var detectProjectPackageManager = async (root) => {
|
|
19802
|
+
const detected = await detect({ cwd: root });
|
|
19803
|
+
const name = detected?.name;
|
|
19804
|
+
return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : detectPackageManager(process.env.npm_config_user_agent);
|
|
19805
|
+
};
|
|
19646
19806
|
var validateContentDir = (root, dir) => isAbsolute9(dir) || relative16(root, join35(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
|
|
19647
19807
|
var titleize = (raw) => {
|
|
19648
19808
|
const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
|
|
@@ -19787,8 +19947,8 @@ var ejectCommand = defineCommand7({
|
|
|
19787
19947
|
refuseIfDevRunning(root, "ejecting");
|
|
19788
19948
|
let notices = [];
|
|
19789
19949
|
try {
|
|
19790
|
-
const { config } = await loadConfig(root);
|
|
19791
|
-
notices = droppedArtifactNotices(
|
|
19950
|
+
const { config: config2 } = await loadConfig(root);
|
|
19951
|
+
notices = droppedArtifactNotices(config2);
|
|
19792
19952
|
} catch {}
|
|
19793
19953
|
if (!args.yes) {
|
|
19794
19954
|
logger.warn("Eject is one-way: it writes astro.config.mjs, src/, and (if absent) tsconfig.json, rewrites your package.json scripts, and removes .blume. An existing tsconfig.json is left untouched.");
|
|
@@ -19807,7 +19967,7 @@ var ejectCommand = defineCommand7({
|
|
|
19807
19967
|
`);
|
|
19808
19968
|
}
|
|
19809
19969
|
reportDroppedArtifacts(notices);
|
|
19810
|
-
const pm =
|
|
19970
|
+
const pm = await detectProjectPackageManager(root);
|
|
19811
19971
|
const { build: build2, dev: dev2 } = commandsFor(pm);
|
|
19812
19972
|
logger.box(`Your project is now a standalone Astro app.
|
|
19813
19973
|
|
|
@@ -19892,6 +20052,20 @@ import { mkdtemp as mkdtemp2, writeFile as writeFile11 } from "node:fs/promises"
|
|
|
19892
20052
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
19893
20053
|
import { colors as colors4 } from "consola/utils";
|
|
19894
20054
|
import { join as join36, relative as relative18 } from "pathe";
|
|
20055
|
+
|
|
20056
|
+
// src/cli/report-format.ts
|
|
20057
|
+
var seconds = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
20058
|
+
var money = (cost) => cost === undefined ? "" : `$${cost.toFixed(2)}`;
|
|
20059
|
+
var duration = (ms) => {
|
|
20060
|
+
if (ms < 60000) {
|
|
20061
|
+
return seconds(ms);
|
|
20062
|
+
}
|
|
20063
|
+
const minutes = Math.floor(ms / 60000);
|
|
20064
|
+
const rest = Math.round(ms % 60000 / 1000);
|
|
20065
|
+
return `${minutes}m ${rest}s`;
|
|
20066
|
+
};
|
|
20067
|
+
|
|
20068
|
+
// src/eval/report.ts
|
|
19895
20069
|
var GLYPH2 = {
|
|
19896
20070
|
error: "!",
|
|
19897
20071
|
fail: "✖",
|
|
@@ -19905,16 +20079,6 @@ var STATUS_COLOR = {
|
|
|
19905
20079
|
skip: colors4.dim
|
|
19906
20080
|
};
|
|
19907
20081
|
var ID_PAD = 28;
|
|
19908
|
-
var seconds = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
19909
|
-
var money = (cost) => cost === undefined ? "" : `$${cost.toFixed(2)}`;
|
|
19910
|
-
var duration = (ms) => {
|
|
19911
|
-
if (ms < 60000) {
|
|
19912
|
-
return seconds(ms);
|
|
19913
|
-
}
|
|
19914
|
-
const minutes = Math.floor(ms / 60000);
|
|
19915
|
-
const rest = Math.round(ms % 60000 / 1000);
|
|
19916
|
-
return `${minutes}m ${rest}s`;
|
|
19917
|
-
};
|
|
19918
20082
|
var questionLine = (result) => {
|
|
19919
20083
|
const color = STATUS_COLOR[result.status];
|
|
19920
20084
|
const glyph = color(GLYPH2[result.status]);
|
|
@@ -19999,8 +20163,8 @@ import { tmpdir as tmpdir3 } from "node:os";
|
|
|
19999
20163
|
import { join as join38 } from "pathe";
|
|
20000
20164
|
|
|
20001
20165
|
// src/eval/agents.ts
|
|
20002
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
20003
20166
|
import { readFile as readFile20, writeFile as writeFile12 } from "node:fs/promises";
|
|
20167
|
+
import spawn2 from "cross-spawn";
|
|
20004
20168
|
import { join as join37 } from "pathe";
|
|
20005
20169
|
import { z as z3 } from "zod";
|
|
20006
20170
|
var KILL_GRACE_MS = 5000;
|
|
@@ -20024,19 +20188,17 @@ var DISALLOWED_TOOLS = [
|
|
|
20024
20188
|
"Task"
|
|
20025
20189
|
];
|
|
20026
20190
|
var runAgentHeadless = (bin, args, options) => new Promise((resolve11, reject) => {
|
|
20027
|
-
const platform = options.platform ?? process.platform;
|
|
20028
20191
|
const child = spawn2(bin, args, {
|
|
20029
20192
|
cwd: options.cwd,
|
|
20030
|
-
shell: platform === "win32",
|
|
20031
20193
|
stdio: ["pipe", "pipe", "pipe"]
|
|
20032
20194
|
});
|
|
20033
20195
|
let stdout = "";
|
|
20034
20196
|
let stderr = "";
|
|
20035
20197
|
let timedOut = false;
|
|
20036
|
-
child.stdout
|
|
20198
|
+
child.stdout?.on("data", (chunk) => {
|
|
20037
20199
|
stdout += chunk.toString("utf-8");
|
|
20038
20200
|
});
|
|
20039
|
-
child.stderr
|
|
20201
|
+
child.stderr?.on("data", (chunk) => {
|
|
20040
20202
|
stderr += chunk.toString("utf-8");
|
|
20041
20203
|
});
|
|
20042
20204
|
const deadline = setTimeout(() => {
|
|
@@ -20060,7 +20222,7 @@ var runAgentHeadless = (bin, args, options) => new Promise((resolve11, reject) =
|
|
|
20060
20222
|
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
20061
20223
|
}
|
|
20062
20224
|
});
|
|
20063
|
-
child.stdin
|
|
20225
|
+
child.stdin?.end(options.prompt);
|
|
20064
20226
|
});
|
|
20065
20227
|
var writeMcpConfig = async (dir, snapshotPath, launcher) => {
|
|
20066
20228
|
const resolved = launcher ?? {
|
|
@@ -20068,12 +20230,12 @@ var writeMcpConfig = async (dir, snapshotPath, launcher) => {
|
|
|
20068
20230
|
command: process.execPath
|
|
20069
20231
|
};
|
|
20070
20232
|
const configPath = join37(dir, "mcp-config.json");
|
|
20071
|
-
const
|
|
20233
|
+
const config2 = {
|
|
20072
20234
|
mcpServers: {
|
|
20073
20235
|
[MCP_SERVER_NAME]: { args: resolved.args, command: resolved.command }
|
|
20074
20236
|
}
|
|
20075
20237
|
};
|
|
20076
|
-
await writeFile12(configPath, JSON.stringify(
|
|
20238
|
+
await writeFile12(configPath, JSON.stringify(config2, null, 2));
|
|
20077
20239
|
return {
|
|
20078
20240
|
configPath,
|
|
20079
20241
|
serverArgs: resolved.args,
|
|
@@ -20175,17 +20337,17 @@ var readAgentOutput = async (kind, result, lastMessagePath) => {
|
|
|
20175
20337
|
text: payload.data.result
|
|
20176
20338
|
};
|
|
20177
20339
|
}
|
|
20178
|
-
let
|
|
20340
|
+
let text;
|
|
20179
20341
|
try {
|
|
20180
20342
|
const raw = await readFile20(lastMessagePath, "utf-8");
|
|
20181
|
-
|
|
20343
|
+
text = raw.trim();
|
|
20182
20344
|
} catch {
|
|
20183
20345
|
return { detail: "no last message written", isError: true, text: "" };
|
|
20184
20346
|
}
|
|
20185
|
-
if (
|
|
20347
|
+
if (text === "") {
|
|
20186
20348
|
return { detail: "empty last message", isError: true, text: "" };
|
|
20187
20349
|
}
|
|
20188
|
-
return { isError: false, text
|
|
20350
|
+
return { isError: false, text };
|
|
20189
20351
|
};
|
|
20190
20352
|
var verdictSchema = z3.object({
|
|
20191
20353
|
missing: z3.array(z3.string()).default([]),
|
|
@@ -20193,15 +20355,15 @@ var verdictSchema = z3.object({
|
|
|
20193
20355
|
pass: z3.boolean(),
|
|
20194
20356
|
score: z3.number().min(0).max(1).optional()
|
|
20195
20357
|
});
|
|
20196
|
-
var parseVerdict = (
|
|
20197
|
-
const start =
|
|
20198
|
-
const end =
|
|
20358
|
+
var parseVerdict = (text) => {
|
|
20359
|
+
const start = text.indexOf("{");
|
|
20360
|
+
const end = text.lastIndexOf("}");
|
|
20199
20361
|
if (start === -1 || end <= start) {
|
|
20200
20362
|
return;
|
|
20201
20363
|
}
|
|
20202
20364
|
let parsed;
|
|
20203
20365
|
try {
|
|
20204
|
-
parsed = JSON.parse(
|
|
20366
|
+
parsed = JSON.parse(text.slice(start, end + 1));
|
|
20205
20367
|
} catch {
|
|
20206
20368
|
return;
|
|
20207
20369
|
}
|
|
@@ -20480,21 +20642,21 @@ var runEval = async (options) => {
|
|
|
20480
20642
|
var DEFAULT_FILE = "evals.yaml";
|
|
20481
20643
|
var DEFAULT_TIMEOUT_S = 180;
|
|
20482
20644
|
var isAgentKind = (value) => (value in AGENTS);
|
|
20483
|
-
var
|
|
20645
|
+
var notInstalled = (agent) => {
|
|
20646
|
+
const cli = AGENTS[agent];
|
|
20647
|
+
logger.error(`${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`);
|
|
20648
|
+
return process.exit(1);
|
|
20649
|
+
};
|
|
20650
|
+
var launchAgentCode2 = async (agent, prompt) => {
|
|
20484
20651
|
try {
|
|
20485
|
-
return await launchAgent(bin, prompt);
|
|
20652
|
+
return await launchAgent(AGENTS[agent].bin, prompt);
|
|
20486
20653
|
} catch (error) {
|
|
20487
20654
|
if (error?.code !== "ENOENT") {
|
|
20488
20655
|
throw error;
|
|
20489
20656
|
}
|
|
20490
|
-
return
|
|
20657
|
+
return notInstalled(agent);
|
|
20491
20658
|
}
|
|
20492
20659
|
};
|
|
20493
|
-
var notInstalled = (agent) => {
|
|
20494
|
-
const cli = AGENTS[agent];
|
|
20495
|
-
logger.error(`${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`);
|
|
20496
|
-
return process.exit(1);
|
|
20497
|
-
};
|
|
20498
20660
|
var passFraction = (result) => {
|
|
20499
20661
|
const ran = result.results.length - result.counts.skip;
|
|
20500
20662
|
return ran === 0 ? 1 : result.counts.pass / ran;
|
|
@@ -20534,10 +20696,7 @@ var runFixHandoff = async (agent, result, root, threshold) => {
|
|
|
20534
20696
|
process.stderr.write(` Handing ${count} failed question${count === 1 ? "" : "s"} to ${cli.name}…
|
|
20535
20697
|
|
|
20536
20698
|
`);
|
|
20537
|
-
const code = await launchAgentCode2(
|
|
20538
|
-
if (code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
20539
|
-
notInstalled(agent);
|
|
20540
|
-
}
|
|
20699
|
+
const code = await launchAgentCode2(agent, evalFixPrompt(report));
|
|
20541
20700
|
if (code !== 0) {
|
|
20542
20701
|
process.exit(code);
|
|
20543
20702
|
}
|
|
@@ -20548,10 +20707,7 @@ var runInit = async (agent, file) => {
|
|
|
20548
20707
|
logger.error(`${file} already exists — edit it directly, or pass --file to draft elsewhere.`);
|
|
20549
20708
|
process.exit(1);
|
|
20550
20709
|
}
|
|
20551
|
-
const code = await launchAgentCode2(
|
|
20552
|
-
if (code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
20553
|
-
notInstalled(agent);
|
|
20554
|
-
}
|
|
20710
|
+
const code = await launchAgentCode2(agent, initPrompt(file));
|
|
20555
20711
|
if (code !== 0) {
|
|
20556
20712
|
process.exit(code);
|
|
20557
20713
|
}
|
|
@@ -20909,6 +21065,7 @@ import {
|
|
|
20909
21065
|
CallToolRequestSchema,
|
|
20910
21066
|
ListToolsRequestSchema
|
|
20911
21067
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
21068
|
+
import { z as z5 } from "zod";
|
|
20912
21069
|
|
|
20913
21070
|
// src/search/orama-index.ts
|
|
20914
21071
|
import { create, insertMultiple, search } from "@orama/orama";
|
|
@@ -20926,6 +21083,7 @@ var BOOST = { description: 2, title: 3 };
|
|
|
20926
21083
|
var SEGMENTED_LANGUAGES = new Set(["ja", "ko", "th", "zh"]);
|
|
20927
21084
|
var BIGRAM_LANGUAGES = new Set(["ja", "zh"]);
|
|
20928
21085
|
var BIGRAM_SCRIPTS = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}々〆〇ー゙゚]+$/u;
|
|
21086
|
+
var TERM = /[\p{L}\p{M}\p{N}]+(?:(?:['’](?=\p{L})|(?<=\p{N})[.,](?=\p{N}))[\p{L}\p{M}\p{N}]+)*/gu;
|
|
20929
21087
|
var addBigrams = (run, tokens) => {
|
|
20930
21088
|
const characters = [...run];
|
|
20931
21089
|
if (characters.length === 1) {
|
|
@@ -20962,17 +21120,30 @@ var segmentingTokenizer = (locale) => {
|
|
|
20962
21120
|
run = "";
|
|
20963
21121
|
}
|
|
20964
21122
|
};
|
|
20965
|
-
|
|
21123
|
+
const take = (term) => {
|
|
21124
|
+
if (bigram && BIGRAM_SCRIPTS.test(term)) {
|
|
21125
|
+
run += term;
|
|
21126
|
+
return;
|
|
21127
|
+
}
|
|
21128
|
+
flush();
|
|
21129
|
+
tokens.add(term);
|
|
21130
|
+
};
|
|
21131
|
+
for (const segment of segmenter.segment(raw.normalize("NFC").toLowerCase())) {
|
|
20966
21132
|
if (!segment.isWordLike) {
|
|
20967
21133
|
flush();
|
|
20968
21134
|
continue;
|
|
20969
21135
|
}
|
|
20970
|
-
|
|
20971
|
-
|
|
20972
|
-
|
|
21136
|
+
let end = 0;
|
|
21137
|
+
for (const match of segment.segment.matchAll(TERM)) {
|
|
21138
|
+
if (match.index > end) {
|
|
21139
|
+
flush();
|
|
21140
|
+
}
|
|
21141
|
+
take(match[0]);
|
|
21142
|
+
end = match.index + match[0].length;
|
|
21143
|
+
}
|
|
21144
|
+
if (end < segment.segment.length) {
|
|
21145
|
+
flush();
|
|
20973
21146
|
}
|
|
20974
|
-
flush();
|
|
20975
|
-
tokens.add(segment.segment);
|
|
20976
21147
|
}
|
|
20977
21148
|
flush();
|
|
20978
21149
|
return [...tokens];
|
|
@@ -21013,78 +21184,54 @@ var queryOramaIndex = async (db, term, limit, filters) => {
|
|
|
21013
21184
|
var DEFAULT_SEARCH_LIMIT = 8;
|
|
21014
21185
|
var MAX_SEARCH_LIMIT = 20;
|
|
21015
21186
|
var EXCERPT_LENGTH = 200;
|
|
21016
|
-
var
|
|
21017
|
-
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
description: 'Only include pages matching every facet, key → required value (e.g. `{"status": "enforced"}`). Facets are metadata the site declares per content type; `list_pages` shows each page\'s facet values. Omit for no facet filtering.',
|
|
21024
|
-
type: "object"
|
|
21025
|
-
};
|
|
21026
|
-
var INPUT_SCHEMAS = {
|
|
21027
|
-
get_navigation: { properties: {}, type: "object" },
|
|
21028
|
-
get_page: {
|
|
21029
|
-
properties: {
|
|
21030
|
-
route: {
|
|
21031
|
-
description: "The page route, e.g. `/guides/install`.",
|
|
21032
|
-
type: "string"
|
|
21033
|
-
}
|
|
21034
|
-
},
|
|
21035
|
-
required: ["route"],
|
|
21036
|
-
type: "object"
|
|
21037
|
-
},
|
|
21038
|
-
list_pages: {
|
|
21039
|
-
properties: {
|
|
21040
|
-
contentTypes: CONTENT_TYPES_SCHEMA,
|
|
21041
|
-
filters: FILTERS_SCHEMA
|
|
21042
|
-
},
|
|
21043
|
-
type: "object"
|
|
21044
|
-
},
|
|
21045
|
-
search_docs: {
|
|
21046
|
-
properties: {
|
|
21047
|
-
contentTypes: CONTENT_TYPES_SCHEMA,
|
|
21048
|
-
filters: FILTERS_SCHEMA,
|
|
21049
|
-
limit: {
|
|
21050
|
-
description: `Maximum hits to return (default ${DEFAULT_SEARCH_LIMIT}).`,
|
|
21051
|
-
maximum: MAX_SEARCH_LIMIT,
|
|
21052
|
-
minimum: 1,
|
|
21053
|
-
type: "integer"
|
|
21054
|
-
},
|
|
21055
|
-
query: { description: "The search query.", type: "string" }
|
|
21056
|
-
},
|
|
21057
|
-
required: ["query"],
|
|
21058
|
-
type: "object"
|
|
21187
|
+
var contentTypesField = z5.preprocess((value) => {
|
|
21188
|
+
const list2 = (Array.isArray(value) ? value : [value]).filter((entry) => typeof entry === "string");
|
|
21189
|
+
return list2.length > 0 ? list2 : undefined;
|
|
21190
|
+
}, z5.array(z5.string()).optional().describe('Only include pages of these content types (frontmatter `type`, e.g. `["doc", "rfc"]`). `list_pages` shows each page\'s type. Omit to include every type.'));
|
|
21191
|
+
var filtersField = z5.preprocess((value) => {
|
|
21192
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
21193
|
+
return;
|
|
21059
21194
|
}
|
|
21195
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
21196
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
21197
|
+
}, z5.record(z5.string(), z5.string()).optional().describe('Only include pages matching every facet, key → required value (e.g. `{"status": "enforced"}`). Facets are metadata the site declares per content type; `list_pages` shows each page\'s facet values. Omit for no facet filtering.'));
|
|
21198
|
+
var limitField = z5.preprocess((value) => {
|
|
21199
|
+
const num = typeof value === "number" ? value : Number(value);
|
|
21200
|
+
return Number.isFinite(num) ? Math.min(Math.max(Math.trunc(num), 1), MAX_SEARCH_LIMIT) : undefined;
|
|
21201
|
+
}, z5.int().min(1).max(MAX_SEARCH_LIMIT).optional().describe(`Maximum hits to return (default ${DEFAULT_SEARCH_LIMIT}).`));
|
|
21202
|
+
var textField = (description) => z5.preprocess((value) => typeof value === "string" ? value : "", z5.string().describe(description));
|
|
21203
|
+
var TOOL_INPUTS = {
|
|
21204
|
+
get_navigation: z5.object({}),
|
|
21205
|
+
get_page: z5.object({
|
|
21206
|
+
route: textField("The page route, e.g. `/guides/install`.")
|
|
21207
|
+
}),
|
|
21208
|
+
list_pages: z5.object({
|
|
21209
|
+
contentTypes: contentTypesField,
|
|
21210
|
+
filters: filtersField
|
|
21211
|
+
}),
|
|
21212
|
+
search_docs: z5.object({
|
|
21213
|
+
contentTypes: contentTypesField,
|
|
21214
|
+
filters: filtersField,
|
|
21215
|
+
limit: limitField,
|
|
21216
|
+
query: textField("The search query.")
|
|
21217
|
+
})
|
|
21218
|
+
};
|
|
21219
|
+
var inputSchemaFor = (schema) => {
|
|
21220
|
+
const {
|
|
21221
|
+
$schema: _dialect,
|
|
21222
|
+
additionalProperties: _closed,
|
|
21223
|
+
...rest
|
|
21224
|
+
} = z5.toJSONSchema(schema);
|
|
21225
|
+
return rest;
|
|
21060
21226
|
};
|
|
21061
21227
|
var TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
|
|
21062
21228
|
annotations: tool.annotations,
|
|
21063
21229
|
description: tool.description,
|
|
21064
|
-
inputSchema:
|
|
21230
|
+
inputSchema: inputSchemaFor(TOOL_INPUTS[tool.name]),
|
|
21065
21231
|
name: tool.name,
|
|
21066
21232
|
title: tool.title
|
|
21067
21233
|
}));
|
|
21068
|
-
var asString2 = (value) => typeof value === "string" ? value : "";
|
|
21069
|
-
var asContentTypes = (value) => {
|
|
21070
|
-
const list2 = Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [value].filter((entry) => typeof entry === "string");
|
|
21071
|
-
return list2.length > 0 ? list2 : undefined;
|
|
21072
|
-
};
|
|
21073
|
-
var asFacetFilters = (value) => {
|
|
21074
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
21075
|
-
return;
|
|
21076
|
-
}
|
|
21077
|
-
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
21078
|
-
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
21079
|
-
};
|
|
21080
21234
|
var matchesFacets = (facets, filters) => Object.entries(filters).every(([key, value]) => facets?.[key] === value);
|
|
21081
|
-
var asLimit = (value) => {
|
|
21082
|
-
const num = typeof value === "number" ? value : Number(value);
|
|
21083
|
-
if (!Number.isFinite(num)) {
|
|
21084
|
-
return DEFAULT_SEARCH_LIMIT;
|
|
21085
|
-
}
|
|
21086
|
-
return Math.min(Math.max(Math.trunc(num), 1), MAX_SEARCH_LIMIT);
|
|
21087
|
-
};
|
|
21088
21235
|
var normalizeRoute2 = (input, data) => {
|
|
21089
21236
|
let value = input.trim();
|
|
21090
21237
|
if (/^https?:\/\//iu.test(value)) {
|
|
@@ -21095,15 +21242,12 @@ var normalizeRoute2 = (input, data) => {
|
|
|
21095
21242
|
try {
|
|
21096
21243
|
value = decodeURI(value);
|
|
21097
21244
|
} catch {}
|
|
21098
|
-
const
|
|
21099
|
-
|
|
21100
|
-
const withSlash = noSuffix.startsWith("/") ? noSuffix : `/${noSuffix}`;
|
|
21101
|
-
const based = stripBasePath(data.base, withSlash);
|
|
21102
|
-
return based === "" ? "/" : based;
|
|
21245
|
+
const noSuffix = trimEnd(value, "/").replace(/\.mdx?$/u, "");
|
|
21246
|
+
return stripBasePath(data.base, normalizeRoute(noSuffix));
|
|
21103
21247
|
};
|
|
21104
21248
|
var urlFor = (route, data) => {
|
|
21105
21249
|
const path = withBasePath(data.base, route);
|
|
21106
|
-
return data.site ?
|
|
21250
|
+
return data.site ? absoluteUrl(data.site, path) : path;
|
|
21107
21251
|
};
|
|
21108
21252
|
var excerptFor = (doc) => {
|
|
21109
21253
|
if (doc.description) {
|
|
@@ -21112,7 +21256,7 @@ var excerptFor = (doc) => {
|
|
|
21112
21256
|
const head = doc.content.slice(0, EXCERPT_LENGTH).trim();
|
|
21113
21257
|
return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;
|
|
21114
21258
|
};
|
|
21115
|
-
var
|
|
21259
|
+
var text = (value, isError = false) => ({
|
|
21116
21260
|
content: [{ text: value, type: "text" }],
|
|
21117
21261
|
...isError ? { isError: true } : {}
|
|
21118
21262
|
});
|
|
@@ -21134,10 +21278,11 @@ var buildServer = (data, index) => {
|
|
|
21134
21278
|
server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
21135
21279
|
const { arguments: args = {}, name } = request2.params;
|
|
21136
21280
|
if (name === "search_docs") {
|
|
21281
|
+
const input = TOOL_INPUTS.search_docs.parse(args);
|
|
21137
21282
|
const db = await index();
|
|
21138
|
-
const hits = await queryOramaIndex(db,
|
|
21139
|
-
contentTypes:
|
|
21140
|
-
facets:
|
|
21283
|
+
const hits = await queryOramaIndex(db, input.query, input.limit ?? DEFAULT_SEARCH_LIMIT, {
|
|
21284
|
+
contentTypes: input.contentTypes,
|
|
21285
|
+
facets: input.filters
|
|
21141
21286
|
});
|
|
21142
21287
|
const results = hits.map((doc) => ({
|
|
21143
21288
|
contentType: doc.contentType,
|
|
@@ -21147,21 +21292,21 @@ var buildServer = (data, index) => {
|
|
|
21147
21292
|
title: doc.title,
|
|
21148
21293
|
url: urlFor(doc.route, data)
|
|
21149
21294
|
}));
|
|
21150
|
-
return
|
|
21295
|
+
return text(JSON.stringify(results, null, 2));
|
|
21151
21296
|
}
|
|
21152
21297
|
if (name === "get_page") {
|
|
21153
|
-
const
|
|
21298
|
+
const input = TOOL_INPUTS.get_page.parse(args);
|
|
21299
|
+
const key = normalizeRoute2(input.route, data);
|
|
21154
21300
|
const markdown = data.pages[key];
|
|
21155
21301
|
if (markdown === undefined) {
|
|
21156
|
-
return
|
|
21302
|
+
return text(`No page found at "${key}". Use list_pages or search_docs to find valid routes.`, true);
|
|
21157
21303
|
}
|
|
21158
|
-
return
|
|
21304
|
+
return text(markdown);
|
|
21159
21305
|
}
|
|
21160
21306
|
if (name === "list_pages") {
|
|
21161
|
-
const contentTypes =
|
|
21162
|
-
const filters = asFacetFilters(args.filters);
|
|
21307
|
+
const { contentTypes, filters } = TOOL_INPUTS.list_pages.parse(args);
|
|
21163
21308
|
const routes = data.routes.filter((route) => (!contentTypes || contentTypes.includes(route.contentType)) && (!filters || matchesFacets(route.facets, filters)));
|
|
21164
|
-
return
|
|
21309
|
+
return text(JSON.stringify(routes.map((route) => ({
|
|
21165
21310
|
contentType: route.contentType,
|
|
21166
21311
|
description: route.description,
|
|
21167
21312
|
facets: route.facets,
|
|
@@ -21172,9 +21317,9 @@ var buildServer = (data, index) => {
|
|
|
21172
21317
|
})), null, 2));
|
|
21173
21318
|
}
|
|
21174
21319
|
if (name === "get_navigation") {
|
|
21175
|
-
return
|
|
21320
|
+
return text(JSON.stringify(data.navigation, null, 2));
|
|
21176
21321
|
}
|
|
21177
|
-
return
|
|
21322
|
+
return text(`Unknown tool: ${name}`, true);
|
|
21178
21323
|
});
|
|
21179
21324
|
return server;
|
|
21180
21325
|
};
|
|
@@ -21233,8 +21378,8 @@ var previewCommand = defineCommand11({
|
|
|
21233
21378
|
},
|
|
21234
21379
|
async run({ args }) {
|
|
21235
21380
|
const root = process.cwd();
|
|
21236
|
-
const { config } = await loadConfig(root);
|
|
21237
|
-
const context = resolveProjectContext(root,
|
|
21381
|
+
const { config: config2 } = await loadConfig(root);
|
|
21382
|
+
const context = resolveProjectContext(root, config2);
|
|
21238
21383
|
if (!existsSync23(join40(context.outDir, "astro.config.mjs"))) {
|
|
21239
21384
|
logger.error("No build found. Run `blume build` first.");
|
|
21240
21385
|
process.exit(1);
|
|
@@ -21273,8 +21418,8 @@ var syncCommand = defineCommand12({
|
|
|
21273
21418
|
async run({ args }) {
|
|
21274
21419
|
const root = process.cwd();
|
|
21275
21420
|
if (args.force) {
|
|
21276
|
-
const { config } = await loadConfig(root);
|
|
21277
|
-
const context = resolveProjectContext(root,
|
|
21421
|
+
const { config: config2 } = await loadConfig(root);
|
|
21422
|
+
const context = resolveProjectContext(root, config2);
|
|
21278
21423
|
await rm4(join41(context.outDir, "cache"), { force: true, recursive: true });
|
|
21279
21424
|
logger.info("Cleared source cache.");
|
|
21280
21425
|
}
|
|
@@ -21321,20 +21466,20 @@ var codexArgs2 = (lastMessagePath) => [
|
|
|
21321
21466
|
var translateAgentArgs = (kind, lastMessagePath) => kind === "claude" ? claudeArgs2() : codexArgs2(lastMessagePath);
|
|
21322
21467
|
|
|
21323
21468
|
// src/translate/ledger.ts
|
|
21324
|
-
import { createHash as
|
|
21469
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
21325
21470
|
import { readFile as readFile23 } from "node:fs/promises";
|
|
21326
21471
|
import { join as join42 } from "pathe";
|
|
21327
|
-
import { z as
|
|
21472
|
+
import { z as z6 } from "zod";
|
|
21328
21473
|
var LEDGER_FILE = "blume.translations.json";
|
|
21329
|
-
var ledgerSchema =
|
|
21330
|
-
files:
|
|
21331
|
-
version:
|
|
21474
|
+
var ledgerSchema = z6.object({
|
|
21475
|
+
files: z6.record(z6.string(), z6.record(z6.string(), z6.string())),
|
|
21476
|
+
version: z6.literal(1)
|
|
21332
21477
|
});
|
|
21333
21478
|
var emptyLedger = () => ({
|
|
21334
21479
|
files: {},
|
|
21335
21480
|
version: 1
|
|
21336
21481
|
});
|
|
21337
|
-
var hashSource = (
|
|
21482
|
+
var hashSource = (text2) => createHash5("sha256").update(text2).digest("hex").slice(0, 16);
|
|
21338
21483
|
var readLedger = async (root) => {
|
|
21339
21484
|
let raw;
|
|
21340
21485
|
try {
|
|
@@ -21421,16 +21566,6 @@ var SPINNER_FRAMES = [
|
|
|
21421
21566
|
];
|
|
21422
21567
|
var SPINNER_INTERVAL_MS = 80;
|
|
21423
21568
|
var REWRITE = `\r${ESC}[K`;
|
|
21424
|
-
var seconds2 = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
21425
|
-
var money2 = (cost) => cost === undefined ? "" : `$${cost.toFixed(2)}`;
|
|
21426
|
-
var duration2 = (ms) => {
|
|
21427
|
-
if (ms < 60000) {
|
|
21428
|
-
return seconds2(ms);
|
|
21429
|
-
}
|
|
21430
|
-
const minutes = Math.floor(ms / 60000);
|
|
21431
|
-
const rest = Math.round(ms % 60000 / 1000);
|
|
21432
|
-
return `${minutes}m ${rest}s`;
|
|
21433
|
-
};
|
|
21434
21569
|
var itemLabel = (item) => item.kind === "page" ? `${item.sourceRel} → ${item.locale}` : `meta title${item.entries.length === 1 ? "" : "s"} (${item.entries.length}) → ${item.locale}`;
|
|
21435
21570
|
var spinnerLine = (active, done, total, frame) => {
|
|
21436
21571
|
const first = active[0];
|
|
@@ -21442,7 +21577,7 @@ var itemEndLine = (result) => {
|
|
|
21442
21577
|
const glyph = color(GLYPH3[result.status]);
|
|
21443
21578
|
const label = itemLabel(result.item);
|
|
21444
21579
|
if (result.status === "translated") {
|
|
21445
|
-
const cells = [
|
|
21580
|
+
const cells = [seconds(result.durationMs), money(result.costUsd)].filter((cell) => cell !== "").join(" ");
|
|
21446
21581
|
return ` ${glyph} ${label} ${colors5.dim(cells)}`;
|
|
21447
21582
|
}
|
|
21448
21583
|
const word = result.status === "partial" ? "partial" : "failed";
|
|
@@ -21457,8 +21592,8 @@ var translateSummaryLine = (result, workList) => {
|
|
|
21457
21592
|
counts.partial > 0 ? `${counts.partial} partial` : "",
|
|
21458
21593
|
workList.untracked.length > 0 ? `${workList.untracked.length} adopted` : "",
|
|
21459
21594
|
workList.upToDate > 0 ? `${workList.upToDate} already up to date` : "",
|
|
21460
|
-
|
|
21461
|
-
|
|
21595
|
+
duration(result.durationMs),
|
|
21596
|
+
money(result.costUsd)
|
|
21462
21597
|
].filter((part) => part !== "");
|
|
21463
21598
|
return parts.join(" · ");
|
|
21464
21599
|
};
|
|
@@ -21599,6 +21734,8 @@ var createProgressRenderer = (options) => {
|
|
|
21599
21734
|
import { existsSync as existsSync24 } from "node:fs";
|
|
21600
21735
|
import { mkdtemp as mkdtemp4, readFile as readFile25 } from "node:fs/promises";
|
|
21601
21736
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
21737
|
+
import pLimit2 from "p-limit";
|
|
21738
|
+
import pMap7 from "p-map";
|
|
21602
21739
|
import { join as join44 } from "pathe";
|
|
21603
21740
|
|
|
21604
21741
|
// src/translate/meta.ts
|
|
@@ -21719,12 +21856,12 @@ ${JSON.stringify(titles, null, 2)}`;
|
|
|
21719
21856
|
// src/translate/validate.ts
|
|
21720
21857
|
var FRONTMATTER_OPEN = /^---\r?\n/u;
|
|
21721
21858
|
var FENCE_LINE = /^\s*(?:```|~~~)/u;
|
|
21722
|
-
var stripOuterFence = (
|
|
21723
|
-
const trimmed =
|
|
21859
|
+
var stripOuterFence = (text2) => {
|
|
21860
|
+
const trimmed = text2.trim();
|
|
21724
21861
|
const match = trimmed.match(/^(?<fence>`{3,}|~{3,})[^\n]*\n(?<inner>[\s\S]*?)\n\k<fence>\s*$/u);
|
|
21725
21862
|
return match?.groups?.inner ?? trimmed;
|
|
21726
21863
|
};
|
|
21727
|
-
var countFenceLines = (
|
|
21864
|
+
var countFenceLines = (text2) => text2.split(`
|
|
21728
21865
|
`).filter((line) => FENCE_LINE.test(line)).length;
|
|
21729
21866
|
var getPath2 = (data, path) => {
|
|
21730
21867
|
let value = data;
|
|
@@ -21743,8 +21880,8 @@ var setPath = (data, path, value) => {
|
|
|
21743
21880
|
}
|
|
21744
21881
|
parent[path.at(-1)] = value;
|
|
21745
21882
|
};
|
|
21746
|
-
var ensureTrailingNewline = (
|
|
21747
|
-
`) ?
|
|
21883
|
+
var ensureTrailingNewline = (text2) => text2.endsWith(`
|
|
21884
|
+
`) ? text2 : `${text2}
|
|
21748
21885
|
`;
|
|
21749
21886
|
var validateTranslation = (sourceText, agentText) => {
|
|
21750
21887
|
const candidate = stripOuterFence(agentText);
|
|
@@ -21823,11 +21960,6 @@ var metaDirKey = (dir) => dir === "" ? "." : dir;
|
|
|
21823
21960
|
var invokeAgent = async (context, prompt, index) => {
|
|
21824
21961
|
const messagePath = join44(context.dir, `message-${index}.txt`);
|
|
21825
21962
|
const result = await context.run(context.bin, translateAgentArgs(context.kind, messagePath), { cwd: context.dir, prompt, timeoutMs: context.timeoutMs });
|
|
21826
|
-
if (!result.timedOut && result.code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
21827
|
-
const missing = new Error(`${context.bin} was not found on PATH`);
|
|
21828
|
-
missing.code = "ENOENT";
|
|
21829
|
-
throw missing;
|
|
21830
|
-
}
|
|
21831
21963
|
return await readAgentOutput(context.kind, result, messagePath);
|
|
21832
21964
|
};
|
|
21833
21965
|
var runPageItem = async (item, index, context, ledger) => {
|
|
@@ -21916,37 +22048,26 @@ var runTranslate = async (options) => {
|
|
|
21916
22048
|
timeoutMs: options.timeoutMs ?? DEFAULT_TRANSLATE_TIMEOUT_MS
|
|
21917
22049
|
};
|
|
21918
22050
|
const { items } = options.workList;
|
|
21919
|
-
const results = Array.from({ length: items.length });
|
|
21920
22051
|
const concurrency = Math.max(1, Math.min(options.concurrency ?? 1, items.length || 1));
|
|
21921
|
-
|
|
21922
|
-
const persist = () =>
|
|
21923
|
-
|
|
21924
|
-
|
|
21925
|
-
|
|
21926
|
-
|
|
21927
|
-
|
|
21928
|
-
|
|
21929
|
-
|
|
21930
|
-
|
|
21931
|
-
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
21935
|
-
|
|
21936
|
-
|
|
21937
|
-
|
|
21938
|
-
|
|
21939
|
-
|
|
21940
|
-
await persist();
|
|
21941
|
-
options.onProgress?.({
|
|
21942
|
-
index,
|
|
21943
|
-
kind: "item-end",
|
|
21944
|
-
result,
|
|
21945
|
-
total: items.length
|
|
21946
|
-
});
|
|
21947
|
-
}
|
|
21948
|
-
};
|
|
21949
|
-
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
22052
|
+
const persistLimit = pLimit2(1);
|
|
22053
|
+
const persist = () => persistLimit(() => options.persistLedger?.());
|
|
22054
|
+
const results = await pMap7(items, async (item, index) => {
|
|
22055
|
+
options.onProgress?.({
|
|
22056
|
+
index,
|
|
22057
|
+
item,
|
|
22058
|
+
kind: "item-start",
|
|
22059
|
+
total: items.length
|
|
22060
|
+
});
|
|
22061
|
+
const result = await (item.kind === "page" ? runPageItem(item, index, context, options.ledger) : runMetaItem(item, index, context, options.ledger));
|
|
22062
|
+
await persist();
|
|
22063
|
+
options.onProgress?.({
|
|
22064
|
+
index,
|
|
22065
|
+
kind: "item-end",
|
|
22066
|
+
result,
|
|
22067
|
+
total: items.length
|
|
22068
|
+
});
|
|
22069
|
+
return result;
|
|
22070
|
+
}, { concurrency });
|
|
21950
22071
|
const diagnostics = [];
|
|
21951
22072
|
for (const result of results) {
|
|
21952
22073
|
if (result.status !== "translated") {
|
|
@@ -22100,7 +22221,7 @@ var computeWorkList = async (project, ledger, options = {}) => {
|
|
|
22100
22221
|
|
|
22101
22222
|
// src/cli/commands/translate.ts
|
|
22102
22223
|
var DEFAULT_TIMEOUT_S2 = DEFAULT_TRANSLATE_TIMEOUT_MS / 1000;
|
|
22103
|
-
var
|
|
22224
|
+
var DEFAULT_CONCURRENCY2 = 4;
|
|
22104
22225
|
var MAX_CONCURRENCY = 16;
|
|
22105
22226
|
var parseFlags2 = (args) => {
|
|
22106
22227
|
const agents = Object.keys(AGENTS).filter((kind) => args[kind]);
|
|
@@ -22121,7 +22242,7 @@ var parseFlags2 = (args) => {
|
|
|
22121
22242
|
logger.error(`Invalid --timeout "${args.timeout}" (whole seconds).`);
|
|
22122
22243
|
process.exit(1);
|
|
22123
22244
|
}
|
|
22124
|
-
const concurrency = args.concurrency === undefined ?
|
|
22245
|
+
const concurrency = args.concurrency === undefined ? DEFAULT_CONCURRENCY2 : Number(args.concurrency);
|
|
22125
22246
|
if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency > MAX_CONCURRENCY) {
|
|
22126
22247
|
logger.error(`Invalid --concurrency "${args.concurrency}" (use 1..${MAX_CONCURRENCY}).`);
|
|
22127
22248
|
process.exit(1);
|
|
@@ -22168,7 +22289,7 @@ var translateCommand = defineCommand13({
|
|
|
22168
22289
|
type: "boolean"
|
|
22169
22290
|
},
|
|
22170
22291
|
concurrency: {
|
|
22171
|
-
description: `Parallel agent sessions. Defaults to ${
|
|
22292
|
+
description: `Parallel agent sessions. Defaults to ${DEFAULT_CONCURRENCY2}, max ${MAX_CONCURRENCY}.`,
|
|
22172
22293
|
type: "string"
|
|
22173
22294
|
},
|
|
22174
22295
|
force: {
|
|
@@ -22587,5 +22708,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
22587
22708
|
});
|
|
22588
22709
|
runMain(main);
|
|
22589
22710
|
|
|
22590
|
-
//# debugId=
|
|
22711
|
+
//# debugId=07BF7082AA0A5EEC64756E2164756E21
|
|
22591
22712
|
//# sourceMappingURL=index.js.map
|