blume 1.4.2 → 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 +54 -0
- package/dist/cli/index.js +694 -579
- package/dist/cli/index.js.map +56 -55
- package/dist/types/core/base-path.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +8 -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 +26 -8
- package/docs/content/sources.mdx +1 -1
- package/package.json +11 -1
- 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/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 +59 -34
- package/src/astro/islands.ts +7 -3
- package/src/astro/templates.ts +34 -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 +6 -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/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.
|
|
@@ -5282,6 +5321,21 @@ var visibleText = (element) => {
|
|
|
5282
5321
|
|
|
5283
5322
|
// src/audit/snapshot.ts
|
|
5284
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
|
+
};
|
|
5285
5339
|
var CONTENT_ROOTS = ["main", "article", "body"];
|
|
5286
5340
|
var contentRoot = (document) => {
|
|
5287
5341
|
for (const selector of CONTENT_ROOTS) {
|
|
@@ -5341,6 +5395,7 @@ var buildSnapshot = (options) => {
|
|
|
5341
5395
|
const root = contentRoot(document);
|
|
5342
5396
|
const prose = root ? visibleText(root) : "";
|
|
5343
5397
|
const robots = document.querySelector('meta[name="robots"]');
|
|
5398
|
+
const lang = attr(document.querySelector("html") ?? document, "lang");
|
|
5344
5399
|
const { jsonld, jsonldErrors } = collectJsonLd(document);
|
|
5345
5400
|
return {
|
|
5346
5401
|
bytes: Buffer.byteLength(html, "utf-8"),
|
|
@@ -5361,7 +5416,7 @@ var buildSnapshot = (options) => {
|
|
|
5361
5416
|
indexable: !robots?.getAttribute("content")?.includes("noindex"),
|
|
5362
5417
|
jsonld,
|
|
5363
5418
|
jsonldErrors,
|
|
5364
|
-
lang
|
|
5419
|
+
lang,
|
|
5365
5420
|
links,
|
|
5366
5421
|
metaRefresh: document.querySelector('meta[http-equiv="refresh" i]')?.getAttribute("content")?.trim() ?? null,
|
|
5367
5422
|
og: prefixedMeta(document, "property", "og:"),
|
|
@@ -5374,12 +5429,13 @@ var buildSnapshot = (options) => {
|
|
|
5374
5429
|
twitter: prefixedMeta(document, "name", "twitter:"),
|
|
5375
5430
|
url,
|
|
5376
5431
|
viewport: document.querySelector('meta[name="viewport"]')?.getAttribute("content")?.trim() ?? null,
|
|
5377
|
-
wordCount: prose
|
|
5432
|
+
wordCount: countWords(prose, lang)
|
|
5378
5433
|
};
|
|
5379
5434
|
};
|
|
5380
5435
|
|
|
5381
5436
|
// src/audit/crawl.ts
|
|
5382
5437
|
var EXAMPLES_PREFIX = `${examplesRouteBase("")}/`;
|
|
5438
|
+
var CRAWL_CONCURRENCY = 16;
|
|
5383
5439
|
var fileToUrl = (staticDir, file) => {
|
|
5384
5440
|
const rel = relative6(staticDir, file).replaceAll("\\", "/");
|
|
5385
5441
|
const path = rel.replace(/(?:^|\/)index\.html$/u, "").replace(/\.html$/u, "");
|
|
@@ -5387,11 +5443,11 @@ var fileToUrl = (staticDir, file) => {
|
|
|
5387
5443
|
};
|
|
5388
5444
|
var indexFiles = async (staticDir) => {
|
|
5389
5445
|
const found = await glob("**/*", { cwd: staticDir, dot: true });
|
|
5390
|
-
const sized = await
|
|
5446
|
+
const sized = await pMap2(found, async (file) => {
|
|
5391
5447
|
const path = `/${file.replaceAll("\\", "/")}`;
|
|
5392
5448
|
const info = await stat(join9(staticDir, file));
|
|
5393
5449
|
return [path, info.size];
|
|
5394
|
-
})
|
|
5450
|
+
}, { concurrency: CRAWL_CONCURRENCY });
|
|
5395
5451
|
return new Map(sized);
|
|
5396
5452
|
};
|
|
5397
5453
|
var routeIndex = (manifest, basePath) => {
|
|
@@ -5444,15 +5500,17 @@ var parseSitemap = (file, xml, bytes) => {
|
|
|
5444
5500
|
};
|
|
5445
5501
|
var parseLlms = (file, text) => {
|
|
5446
5502
|
const entries = [];
|
|
5447
|
-
const
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
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);
|
|
5453
5510
|
}
|
|
5454
5511
|
}
|
|
5455
|
-
}
|
|
5512
|
+
};
|
|
5513
|
+
collect(fromMarkdown(text));
|
|
5456
5514
|
return { entries, file };
|
|
5457
5515
|
};
|
|
5458
5516
|
var ROBOTS_DIRECTIVE = /^(?<field>[a-z-]+)\s*:\s*(?<value>.*)$/iu;
|
|
@@ -5488,7 +5546,7 @@ var crawlStaticDir = async (options) => {
|
|
|
5488
5546
|
const { staticDir, manifest, basePath } = options;
|
|
5489
5547
|
const routes = routeIndex(manifest, basePath);
|
|
5490
5548
|
const htmlFiles = await glob("**/*.html", { absolute: true, cwd: staticDir });
|
|
5491
|
-
const snapshots = await
|
|
5549
|
+
const snapshots = await pMap2(htmlFiles.toSorted(), async (file) => {
|
|
5492
5550
|
const url = fileToUrl(staticDir, file);
|
|
5493
5551
|
if (stripBasePath(basePath, url).startsWith(EXAMPLES_PREFIX)) {
|
|
5494
5552
|
return null;
|
|
@@ -5503,7 +5561,7 @@ var crawlStaticDir = async (options) => {
|
|
|
5503
5561
|
route: routes.get(url) ?? routes.get(stripBasePath(basePath, url)),
|
|
5504
5562
|
url
|
|
5505
5563
|
});
|
|
5506
|
-
})
|
|
5564
|
+
}, { concurrency: CRAWL_CONCURRENCY });
|
|
5507
5565
|
const pages = snapshots.filter((page) => page !== null);
|
|
5508
5566
|
const sitemapFile = join9(staticDir, "sitemap.xml");
|
|
5509
5567
|
const sitemapXml = await readIfPresent(sitemapFile);
|
|
@@ -5600,17 +5658,18 @@ class NoBuildError extends Error {
|
|
|
5600
5658
|
this.staticDir = staticDir;
|
|
5601
5659
|
}
|
|
5602
5660
|
}
|
|
5661
|
+
var READ_CONCURRENCY = 16;
|
|
5603
5662
|
var readSources = async (pages) => {
|
|
5604
5663
|
const paths = [
|
|
5605
5664
|
...new Set(pages.flatMap((page) => page.source ? [page.source] : []))
|
|
5606
5665
|
];
|
|
5607
|
-
const entries = await
|
|
5666
|
+
const entries = await pMap3(paths, async (path) => {
|
|
5608
5667
|
try {
|
|
5609
5668
|
return [path, await readFile4(path, "utf-8")];
|
|
5610
5669
|
} catch {
|
|
5611
5670
|
return null;
|
|
5612
5671
|
}
|
|
5613
|
-
})
|
|
5672
|
+
}, { concurrency: READ_CONCURRENCY });
|
|
5614
5673
|
return new Map(entries.filter((entry) => entry !== null));
|
|
5615
5674
|
};
|
|
5616
5675
|
var matches = (id, terms) => {
|
|
@@ -9520,6 +9579,7 @@ var sanitySourceSchema = z2.object({
|
|
|
9520
9579
|
type: z2.literal("sanity")
|
|
9521
9580
|
});
|
|
9522
9581
|
var notionSourceSchema = z2.object({
|
|
9582
|
+
concurrency: z2.number().positive().optional(),
|
|
9523
9583
|
database: z2.string(),
|
|
9524
9584
|
pollInterval: z2.number().positive().optional(),
|
|
9525
9585
|
prefix: z2.string().optional(),
|
|
@@ -9775,6 +9835,7 @@ var aiConfigSchema = z2.strictObject({
|
|
|
9775
9835
|
baseUrl: z2.url().optional(),
|
|
9776
9836
|
enabled: z2.boolean().default(false),
|
|
9777
9837
|
endpoint: askEndpointSchema.optional(),
|
|
9838
|
+
instructions: z2.string().trim().min(1).optional(),
|
|
9778
9839
|
model: z2.string().default("openai/gpt-5.5"),
|
|
9779
9840
|
provider: z2.enum(askAiProviders).default("gateway"),
|
|
9780
9841
|
suggestions: z2.array(z2.strictObject({
|
|
@@ -11232,9 +11293,10 @@ var GROUP_FOLDER2 = /^\((?<label>.+)\)$/u;
|
|
|
11232
11293
|
var WORD_SPLIT2 = /[-_]/u;
|
|
11233
11294
|
var stripNumericPrefix = (segment) => segment.replace(NUMERIC_PREFIX2, "");
|
|
11234
11295
|
var groupLabel = (segment) => segment.match(GROUP_FOLDER2)?.groups?.label ?? null;
|
|
11235
|
-
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, "");
|
|
11236
11297
|
var slugifyPath = (text) => text.split("/").map(slugify2).filter(Boolean).join("/");
|
|
11237
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, "");
|
|
11238
11300
|
var addRouteSegment = (part, segments, groups) => {
|
|
11239
11301
|
if (part === "") {
|
|
11240
11302
|
return;
|
|
@@ -11248,7 +11310,11 @@ var addRouteSegment = (part, segments, groups) => {
|
|
|
11248
11310
|
if (clean === "index") {
|
|
11249
11311
|
return;
|
|
11250
11312
|
}
|
|
11251
|
-
|
|
11313
|
+
const safe = sanitizeSegment(clean);
|
|
11314
|
+
if (safe === "") {
|
|
11315
|
+
return;
|
|
11316
|
+
}
|
|
11317
|
+
segments.push(safe);
|
|
11252
11318
|
};
|
|
11253
11319
|
var mapRoute = (relativePath) => {
|
|
11254
11320
|
const withoutExt = relativePath.slice(0, relativePath.length - extname2(relativePath).length);
|
|
@@ -11578,15 +11644,10 @@ var normalizeEntry = (entry, ctx) => {
|
|
|
11578
11644
|
import { isAbsolute as isAbsolute5, join as join17, resolve as resolve4 } from "pathe";
|
|
11579
11645
|
|
|
11580
11646
|
// src/core/sources/cache.ts
|
|
11647
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
11581
11648
|
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
|
|
11582
11649
|
import { join as join11 } from "pathe";
|
|
11583
|
-
var hashText = (text) =>
|
|
11584
|
-
let hash = 5381;
|
|
11585
|
-
for (let i = 0;i < text.length; i += 1) {
|
|
11586
|
-
hash = (hash * 33 + (text.codePointAt(i) ?? 0)) % 2147483647;
|
|
11587
|
-
}
|
|
11588
|
-
return hash.toString(36);
|
|
11589
|
-
};
|
|
11650
|
+
var hashText = (text) => createHash2("sha256").update(text).digest("hex").slice(0, 16);
|
|
11590
11651
|
var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
|
|
11591
11652
|
var pollingWatch = (load2, intervalSeconds, seed) => (onChange) => {
|
|
11592
11653
|
let last = "";
|
|
@@ -11663,9 +11724,6 @@ var loadWithCache = async (name, cache, fetchEntries, refresh = true) => {
|
|
|
11663
11724
|
};
|
|
11664
11725
|
|
|
11665
11726
|
// src/openapi/model.ts
|
|
11666
|
-
var NON_SLUG2 = /[^a-z0-9]+/gu;
|
|
11667
|
-
var SLUG_EDGES = /^-+|-+$/gu;
|
|
11668
|
-
var slugify3 = (text) => text.toLowerCase().replace(NON_SLUG2, "-").replace(SLUG_EDGES, "");
|
|
11669
11727
|
var HTTP_METHODS = [
|
|
11670
11728
|
"get",
|
|
11671
11729
|
"put",
|
|
@@ -11678,8 +11736,8 @@ var HTTP_METHODS = [
|
|
|
11678
11736
|
];
|
|
11679
11737
|
var UNTAGGED = "Operations";
|
|
11680
11738
|
var operationKey = (method, path, operationId) => {
|
|
11681
|
-
const fromId = operationId ?
|
|
11682
|
-
return fromId ||
|
|
11739
|
+
const fromId = operationId ? slugify(operationId) : "";
|
|
11740
|
+
return fromId || slugify(`${method}-${path}`);
|
|
11683
11741
|
};
|
|
11684
11742
|
var isOperation = (value) => typeof value === "object" && value !== null;
|
|
11685
11743
|
var tagSlugger = () => {
|
|
@@ -11690,7 +11748,7 @@ var tagSlugger = () => {
|
|
|
11690
11748
|
if (existing) {
|
|
11691
11749
|
return existing;
|
|
11692
11750
|
}
|
|
11693
|
-
const base =
|
|
11751
|
+
const base = slugify(name) || "operations";
|
|
11694
11752
|
let slug = base;
|
|
11695
11753
|
for (let suffix = 2;taken.has(slug); suffix += 1) {
|
|
11696
11754
|
slug = `${base}-${suffix}`;
|
|
@@ -11759,6 +11817,7 @@ var extractOperations = (document, baseRoute) => {
|
|
|
11759
11817
|
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
11760
11818
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
11761
11819
|
import { normalize as normalize2, upgrade } from "@scalar/openapi-parser";
|
|
11820
|
+
import pRetry, { AbortError } from "p-retry";
|
|
11762
11821
|
import { isAbsolute as isAbsolute3, join as join12 } from "pathe";
|
|
11763
11822
|
var URL_SPEC = /^https?:\/\//u;
|
|
11764
11823
|
var FETCH_TIMEOUT_MS = 15000;
|
|
@@ -11796,8 +11855,15 @@ var ensureProxyDispatcher = async () => {
|
|
|
11796
11855
|
};
|
|
11797
11856
|
var retryAfterMs = (response) => {
|
|
11798
11857
|
const header = response.headers.get("retry-after");
|
|
11799
|
-
|
|
11800
|
-
|
|
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;
|
|
11801
11867
|
};
|
|
11802
11868
|
var attemptFetch = async (spec) => {
|
|
11803
11869
|
try {
|
|
@@ -11824,25 +11890,37 @@ var attemptFetch = async (spec) => {
|
|
|
11824
11890
|
};
|
|
11825
11891
|
}
|
|
11826
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
|
+
};
|
|
11827
11901
|
var fetchSpecText = async (spec) => {
|
|
11828
11902
|
await ensureProxyDispatcher();
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
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
|
+
});
|
|
11846
11924
|
};
|
|
11847
11925
|
var cacheFileFor = (cacheDir, spec) => join12(cacheDir, `spec-${hashText(spec)}.cache`);
|
|
11848
11926
|
var readCache = async (file) => {
|
|
@@ -11902,6 +11980,8 @@ var parseSpec = async (spec, root, options = {}) => {
|
|
|
11902
11980
|
};
|
|
11903
11981
|
|
|
11904
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";
|
|
11905
11985
|
var MDX_UNSAFE = /[<{}]/gu;
|
|
11906
11986
|
var ENTITIES = {
|
|
11907
11987
|
"<": "<",
|
|
@@ -11909,26 +11989,46 @@ var ENTITIES = {
|
|
|
11909
11989
|
"}": "}"
|
|
11910
11990
|
};
|
|
11911
11991
|
var MDX_ESM_KEYWORD = /^(?<keyword>import|export)\b/gmu;
|
|
11912
|
-
var BACKTICK_CODE = /(?<!`)(?<bt>`+)(?!`)[\s\S]*?(?<!`)\k<bt>(?!`)/gu;
|
|
11913
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
|
+
};
|
|
11914
12014
|
var mdxSafe = (text) => {
|
|
11915
12015
|
let out = "";
|
|
11916
12016
|
let cursor = 0;
|
|
11917
|
-
for (const
|
|
11918
|
-
const start = match.index ?? 0;
|
|
12017
|
+
for (const [start, end] of codeSpans(text)) {
|
|
11919
12018
|
out += escapeProse(text.slice(cursor, start));
|
|
11920
|
-
out +=
|
|
11921
|
-
cursor =
|
|
12019
|
+
out += text.slice(start, end);
|
|
12020
|
+
cursor = end;
|
|
11922
12021
|
}
|
|
11923
12022
|
return out + escapeProse(text.slice(cursor));
|
|
11924
12023
|
};
|
|
11925
12024
|
var META_DESCRIPTION_MAX = 160;
|
|
11926
|
-
var PARAGRAPH_BREAK = /\n\s*\n/u;
|
|
11927
|
-
var MARKDOWN_LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
|
|
11928
|
-
var MARKDOWN_MARKS = /[*_`#>]/gu;
|
|
11929
12025
|
var WHITESPACE = /\s+/gu;
|
|
11930
12026
|
var TRAILING_WORD = /\s+\S*$/u;
|
|
11931
|
-
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
|
+
};
|
|
11932
12032
|
var clip = (text, max) => {
|
|
11933
12033
|
if (max <= 0) {
|
|
11934
12034
|
return "";
|
|
@@ -12047,6 +12147,13 @@ var specEntries = (spec, operations, reference) => {
|
|
|
12047
12147
|
entries.push(toEntry(overviewMdx(spec, reference), base ? `${base}/index.mdx` : "index.mdx"));
|
|
12048
12148
|
return entries;
|
|
12049
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
|
+
};
|
|
12050
12157
|
var openApiSource = (references, ctx) => {
|
|
12051
12158
|
let parsed = {};
|
|
12052
12159
|
const loadReference = async (reference) => {
|
|
@@ -12099,6 +12206,7 @@ var openApiSource = (references, ctx) => {
|
|
|
12099
12206
|
] : []
|
|
12100
12207
|
],
|
|
12101
12208
|
entries: specEntries(spec, operations, reference),
|
|
12209
|
+
folderMeta: tagFolderMeta(spec, tags),
|
|
12102
12210
|
slug: reference.slug,
|
|
12103
12211
|
spec
|
|
12104
12212
|
};
|
|
@@ -12120,6 +12228,7 @@ var openApiSource = (references, ctx) => {
|
|
|
12120
12228
|
severity: "warning"
|
|
12121
12229
|
})));
|
|
12122
12230
|
const data = {};
|
|
12231
|
+
const folderMeta = {};
|
|
12123
12232
|
for (const result of results) {
|
|
12124
12233
|
if ("severity" in result) {
|
|
12125
12234
|
diagnostics.push(result);
|
|
@@ -12127,10 +12236,11 @@ var openApiSource = (references, ctx) => {
|
|
|
12127
12236
|
}
|
|
12128
12237
|
data[result.slug] = result.spec;
|
|
12129
12238
|
entries.push(...result.entries);
|
|
12239
|
+
Object.assign(folderMeta, result.folderMeta);
|
|
12130
12240
|
diagnostics.push(...result.diagnostics);
|
|
12131
12241
|
}
|
|
12132
12242
|
parsed = data;
|
|
12133
|
-
return { diagnostics, entries };
|
|
12243
|
+
return { diagnostics, entries, folderMeta };
|
|
12134
12244
|
};
|
|
12135
12245
|
return {
|
|
12136
12246
|
kind: "openapi-source",
|
|
@@ -12206,15 +12316,15 @@ var filesystemSource = (options) => {
|
|
|
12206
12316
|
};
|
|
12207
12317
|
|
|
12208
12318
|
// src/core/sources/github-releases.ts
|
|
12209
|
-
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
12319
|
+
import { fromMarkdown as fromMarkdown3 } from "mdast-util-from-markdown";
|
|
12210
12320
|
import { gfmFromMarkdown } from "mdast-util-gfm";
|
|
12211
|
-
import { toString as
|
|
12321
|
+
import { toString as mdastToString2 } from "mdast-util-to-string";
|
|
12212
12322
|
import { gfm } from "micromark-extension-gfm";
|
|
12213
12323
|
var DEFAULT_BASE_URL = "https://api.github.com";
|
|
12214
12324
|
var DEFAULT_LIMIT = 100;
|
|
12215
12325
|
var PER_PAGE = 100;
|
|
12216
12326
|
var LEADING_V = /^v/iu;
|
|
12217
|
-
var
|
|
12327
|
+
var NON_SLUG2 = /[^a-z0-9]+/gu;
|
|
12218
12328
|
var EDGE_DASHES = /^-+|-+$/gu;
|
|
12219
12329
|
var DESCRIPTION_MAX = 160;
|
|
12220
12330
|
var DESCRIPTION_MIN = 110;
|
|
@@ -12222,24 +12332,35 @@ var CHANGESET_HASH = /^(?<mark>\s*(?:[-*+]|\d+[.)])\s+)[0-9a-f]{7,40}:\s+/gmu;
|
|
|
12222
12332
|
var WHITESPACE2 = /\s+/gu;
|
|
12223
12333
|
var TRAILING_FRAGMENT = /[\s,;:.—–-]+$/u;
|
|
12224
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
|
+
};
|
|
12225
12346
|
var releaseDescription = (body) => {
|
|
12226
|
-
const tree =
|
|
12347
|
+
const tree = fromMarkdown3(body.replaceAll(CHANGESET_HASH, "$<mark>"), {
|
|
12227
12348
|
extensions: [gfm()],
|
|
12228
12349
|
mdastExtensions: [gfmFromMarkdown()]
|
|
12229
12350
|
});
|
|
12230
|
-
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();
|
|
12231
12352
|
if (!text) {
|
|
12232
12353
|
return;
|
|
12233
12354
|
}
|
|
12234
12355
|
if (text.length <= DESCRIPTION_MAX) {
|
|
12235
12356
|
return text;
|
|
12236
12357
|
}
|
|
12237
|
-
const slice = text
|
|
12358
|
+
const slice = graphemePrefix(text, DESCRIPTION_MAX - 1);
|
|
12238
12359
|
const boundary = slice.lastIndexOf(" ");
|
|
12239
12360
|
const head = (boundary >= DESCRIPTION_MIN ? slice.slice(0, boundary) : slice).replace(TRAILING_FRAGMENT, "");
|
|
12240
12361
|
return `${head}…`;
|
|
12241
12362
|
};
|
|
12242
|
-
var slugifyTag = (tag) => tag.toLowerCase().replaceAll(
|
|
12363
|
+
var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG2, "-").replaceAll(EDGE_DASHES, "");
|
|
12243
12364
|
var githubHeaders = () => {
|
|
12244
12365
|
const headers = new Headers({ Accept: "application/vnd.github+json" });
|
|
12245
12366
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -12492,6 +12613,7 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
12492
12613
|
|
|
12493
12614
|
// src/core/sources/notion.ts
|
|
12494
12615
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
12616
|
+
import pLimit from "p-limit";
|
|
12495
12617
|
import { join as join15 } from "pathe";
|
|
12496
12618
|
|
|
12497
12619
|
// src/core/sources/assets.ts
|
|
@@ -12571,6 +12693,7 @@ var RATE_LIMITED = 429;
|
|
|
12571
12693
|
var MAX_RETRIES = 4;
|
|
12572
12694
|
var BASE_DELAY_MS = 500;
|
|
12573
12695
|
var SECOND_MS2 = 1000;
|
|
12696
|
+
var DEFAULT_CONCURRENCY = 3;
|
|
12574
12697
|
var withNotionRetry = async (call, attempt = 0) => {
|
|
12575
12698
|
try {
|
|
12576
12699
|
return await call();
|
|
@@ -12580,7 +12703,7 @@ var withNotionRetry = async (call, attempt = 0) => {
|
|
|
12580
12703
|
throw error;
|
|
12581
12704
|
}
|
|
12582
12705
|
const retryAfter = Number(error.headers?.["retry-after"]);
|
|
12583
|
-
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());
|
|
12584
12707
|
await sleep2(wait);
|
|
12585
12708
|
return withNotionRetry(call, attempt + 1);
|
|
12586
12709
|
}
|
|
@@ -12644,6 +12767,8 @@ ${text}
|
|
|
12644
12767
|
};
|
|
12645
12768
|
var notionSource = (options, ctx) => {
|
|
12646
12769
|
const props = options.properties ?? {};
|
|
12770
|
+
const limit = pLimit(Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY));
|
|
12771
|
+
const notionCall = (call) => withNotionRetry(() => limit(call));
|
|
12647
12772
|
const cache = snapshotCache(ctx?.cacheDir ?? join15(".blume", "cache", options.name));
|
|
12648
12773
|
const assetsDir = ctx?.assetsDir ?? join15(".blume", "public", "blume-assets", options.name);
|
|
12649
12774
|
const assetsBaseUrl = ctx?.assetsBaseUrl ?? `/blume-assets/${options.name}`;
|
|
@@ -12664,7 +12789,7 @@ var notionSource = (options, ctx) => {
|
|
|
12664
12789
|
}
|
|
12665
12790
|
return new Client({ auth: options.token ?? process.env.NOTION_TOKEN });
|
|
12666
12791
|
};
|
|
12667
|
-
const childrenOf = (client, blockId) => collectAll((cursor) =>
|
|
12792
|
+
const childrenOf = (client, blockId) => collectAll((cursor) => notionCall(() => client.blocks.children.list({ block_id: blockId, start_cursor: cursor })));
|
|
12668
12793
|
const renderContainer = async (client, block, render) => {
|
|
12669
12794
|
const children = async (target) => {
|
|
12670
12795
|
if (!target.has_children) {
|
|
@@ -12797,7 +12922,7 @@ ${nested}`;
|
|
|
12797
12922
|
}
|
|
12798
12923
|
};
|
|
12799
12924
|
};
|
|
12800
|
-
const queryDatabase = (client, cursor) =>
|
|
12925
|
+
const queryDatabase = (client, cursor) => notionCall(() => client.databases.query({
|
|
12801
12926
|
database_id: options.database,
|
|
12802
12927
|
start_cursor: cursor
|
|
12803
12928
|
}));
|
|
@@ -12848,8 +12973,11 @@ var HEADING_STYLES = {
|
|
|
12848
12973
|
h5: "##### ",
|
|
12849
12974
|
h6: "###### "
|
|
12850
12975
|
};
|
|
12976
|
+
var MARKDOWN_SPECIALS = /[\\`*_[\]~<]/gu;
|
|
12977
|
+
var escapeText = (text) => text.replaceAll(MARKDOWN_SPECIALS, String.raw`\$&`);
|
|
12851
12978
|
var renderSpan = (span, defs) => {
|
|
12852
|
-
|
|
12979
|
+
const isCode = span.marks?.includes("code") ?? false;
|
|
12980
|
+
let text = isCode ? span.text ?? "" : escapeText(span.text ?? "");
|
|
12853
12981
|
if (!span.marks || span.marks.length === 0) {
|
|
12854
12982
|
return text;
|
|
12855
12983
|
}
|
|
@@ -13086,6 +13214,7 @@ var buildSource = (def, name, context, runtime) => {
|
|
|
13086
13214
|
}
|
|
13087
13215
|
if (def.type === "notion") {
|
|
13088
13216
|
return notionSource({
|
|
13217
|
+
concurrency: def.concurrency,
|
|
13089
13218
|
database: def.database,
|
|
13090
13219
|
name,
|
|
13091
13220
|
pollInterval: def.pollInterval,
|
|
@@ -13260,6 +13389,10 @@ var scanProject = async (root, options = {}) => {
|
|
|
13260
13389
|
Promise.all(sources.map(async (source) => ({ source, ...await source.load() }))),
|
|
13261
13390
|
discoverFolderMeta(metaSources, { localeDirs })
|
|
13262
13391
|
]);
|
|
13392
|
+
const sharedFolderMeta = new Map([
|
|
13393
|
+
...loaded.flatMap(({ folderMeta: sourceMeta }) => Object.entries(sourceMeta ?? {})),
|
|
13394
|
+
...folderMeta.shared
|
|
13395
|
+
]);
|
|
13263
13396
|
const {
|
|
13264
13397
|
diagnostics: contentDiagnostics,
|
|
13265
13398
|
droppedPages,
|
|
@@ -13282,7 +13415,7 @@ var scanProject = async (root, options = {}) => {
|
|
|
13282
13415
|
folderMeta: folderMeta.meta,
|
|
13283
13416
|
i18n: config.i18n,
|
|
13284
13417
|
navigation: config.navigation,
|
|
13285
|
-
sharedFolderMeta
|
|
13418
|
+
sharedFolderMeta
|
|
13286
13419
|
});
|
|
13287
13420
|
const manifest = buildManifest({ config, context, graph });
|
|
13288
13421
|
const i18nWarnings = config.i18n ? i18nDiagnostics(pages, config.i18n) : [];
|
|
@@ -13345,14 +13478,16 @@ var shouldFail = (result, gate) => {
|
|
|
13345
13478
|
const failing = failingSeverities(gate);
|
|
13346
13479
|
return result.diagnostics.some((d) => failing.has(d.severity));
|
|
13347
13480
|
};
|
|
13348
|
-
var launchAgentCode = async (
|
|
13481
|
+
var launchAgentCode = async (agent, prompt) => {
|
|
13482
|
+
const cli = AGENTS[agent];
|
|
13349
13483
|
try {
|
|
13350
|
-
return await launchAgent(bin, prompt);
|
|
13484
|
+
return await launchAgent(cli.bin, prompt);
|
|
13351
13485
|
} catch (error) {
|
|
13352
13486
|
if (error?.code !== "ENOENT") {
|
|
13353
13487
|
throw error;
|
|
13354
13488
|
}
|
|
13355
|
-
|
|
13489
|
+
logger.error(`${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`);
|
|
13490
|
+
return process.exit(1);
|
|
13356
13491
|
}
|
|
13357
13492
|
};
|
|
13358
13493
|
var auditCommand = defineCommand2({
|
|
@@ -13460,11 +13595,7 @@ var auditCommand = defineCommand2({
|
|
|
13460
13595
|
process.stderr.write(` Handing ${count} finding${count === 1 ? "" : "s"} to ${cli.name}…
|
|
13461
13596
|
|
|
13462
13597
|
`);
|
|
13463
|
-
const code = await launchAgentCode(
|
|
13464
|
-
if (code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
13465
|
-
logger.error(`${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`);
|
|
13466
|
-
process.exit(1);
|
|
13467
|
-
}
|
|
13598
|
+
const code = await launchAgentCode(agent, fixPrompt(report));
|
|
13468
13599
|
if (code !== 0) {
|
|
13469
13600
|
process.exit(code);
|
|
13470
13601
|
}
|
|
@@ -13510,7 +13641,7 @@ var buildRssFeeds = (project) => {
|
|
|
13510
13641
|
if (!(rss.enabled && site)) {
|
|
13511
13642
|
return [];
|
|
13512
13643
|
}
|
|
13513
|
-
const base = site
|
|
13644
|
+
const base = siteRoot(site);
|
|
13514
13645
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
13515
13646
|
const rootLink = `${base}${deployBase}`;
|
|
13516
13647
|
const feeds = [];
|
|
@@ -13590,7 +13721,7 @@ var askApiUrl = (endpoint, site, abs) => {
|
|
|
13590
13721
|
if (!endpoint) {
|
|
13591
13722
|
return abs("/api/ask");
|
|
13592
13723
|
}
|
|
13593
|
-
return site && endpoint.startsWith("/") ?
|
|
13724
|
+
return site && endpoint.startsWith("/") ? absoluteUrl(site, endpoint) : endpoint;
|
|
13594
13725
|
};
|
|
13595
13726
|
var wellKnownArtifacts = (config, abs) => {
|
|
13596
13727
|
const artifacts = {};
|
|
@@ -13614,7 +13745,7 @@ var buildAgentReadability = (project) => {
|
|
|
13614
13745
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
13615
13746
|
const abs = (path) => {
|
|
13616
13747
|
const based = withBasePath(deployBase, path);
|
|
13617
|
-
return site ?
|
|
13748
|
+
return site ? absoluteUrl(site, based) : based;
|
|
13618
13749
|
};
|
|
13619
13750
|
const negotiates = config.deployment.output === "server" && (config.deployment.adapter === "vercel" || config.deployment.adapter === "cloudflare");
|
|
13620
13751
|
const artifacts = {
|
|
@@ -14079,7 +14210,7 @@ var applyAgentVisibility = (markdown) => applyAudienceVisibility(markdown, "agen
|
|
|
14079
14210
|
// src/ai/llms.ts
|
|
14080
14211
|
var pageUrl = (route, site, base = "") => {
|
|
14081
14212
|
const path = withBasePath(base, route);
|
|
14082
|
-
return encodeURI(site ?
|
|
14213
|
+
return encodeURI(site ? absoluteUrl(site, path) : path);
|
|
14083
14214
|
};
|
|
14084
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"));
|
|
14085
14216
|
var indexedNavigations = (project) => {
|
|
@@ -14253,49 +14384,16 @@ var markdownRoutePaths = (project) => {
|
|
|
14253
14384
|
};
|
|
14254
14385
|
|
|
14255
14386
|
// src/ai/skills.ts
|
|
14256
|
-
import { createHash as
|
|
14387
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
14257
14388
|
import { readdir, readFile as readFile11, stat as stat2 } from "node:fs/promises";
|
|
14258
14389
|
import { join as join18 } from "pathe";
|
|
14259
14390
|
|
|
14260
14391
|
// src/ai/tar.ts
|
|
14261
14392
|
import { gzipSync } from "node:zlib";
|
|
14262
|
-
|
|
14393
|
+
import { createTar } from "nanotar";
|
|
14263
14394
|
var NAME_MAX = 100;
|
|
14264
14395
|
var encoder = new TextEncoder;
|
|
14265
|
-
var octal = (header, offset, length, value) => {
|
|
14266
|
-
const text = value.toString(8).padStart(length - 1, "0");
|
|
14267
|
-
header.set(encoder.encode(text), offset);
|
|
14268
|
-
header[offset + length - 1] = 0;
|
|
14269
|
-
};
|
|
14270
|
-
var text = (header, offset, value) => {
|
|
14271
|
-
header.set(encoder.encode(value), offset);
|
|
14272
|
-
};
|
|
14273
|
-
var fileHeader = (entry) => {
|
|
14274
|
-
const header = new Uint8Array(BLOCK);
|
|
14275
|
-
text(header, 0, entry.path);
|
|
14276
|
-
octal(header, 100, 8, entry.executable ? 493 : 420);
|
|
14277
|
-
octal(header, 108, 8, 0);
|
|
14278
|
-
octal(header, 116, 8, 0);
|
|
14279
|
-
octal(header, 124, 12, entry.content.byteLength);
|
|
14280
|
-
octal(header, 136, 12, 0);
|
|
14281
|
-
text(header, 156, "0");
|
|
14282
|
-
text(header, 257, "ustar");
|
|
14283
|
-
header[262] = 0;
|
|
14284
|
-
text(header, 263, "00");
|
|
14285
|
-
octal(header, 329, 8, 0);
|
|
14286
|
-
octal(header, 337, 8, 0);
|
|
14287
|
-
header.fill(32, 148, 156);
|
|
14288
|
-
let sum = 0;
|
|
14289
|
-
for (const byte of header) {
|
|
14290
|
-
sum += byte;
|
|
14291
|
-
}
|
|
14292
|
-
text(header, 148, sum.toString(8).padStart(6, "0"));
|
|
14293
|
-
header[154] = 0;
|
|
14294
|
-
header[155] = 32;
|
|
14295
|
-
return header;
|
|
14296
|
-
};
|
|
14297
14396
|
var buildTarGz = (entries) => {
|
|
14298
|
-
const blocks = [];
|
|
14299
14397
|
for (const entry of entries) {
|
|
14300
14398
|
if (encoder.encode(entry.path).byteLength > NAME_MAX) {
|
|
14301
14399
|
throw new Error(`tar path exceeds ${NAME_MAX} bytes: ${entry.path}`);
|
|
@@ -14303,20 +14401,19 @@ var buildTarGz = (entries) => {
|
|
|
14303
14401
|
if (entry.path.startsWith("/") || entry.path.split("/").includes("..")) {
|
|
14304
14402
|
throw new Error(`tar path must be archive-relative: ${entry.path}`);
|
|
14305
14403
|
}
|
|
14306
|
-
blocks.push(fileHeader(entry), entry.content);
|
|
14307
|
-
const overhang = entry.content.byteLength % BLOCK;
|
|
14308
|
-
if (overhang > 0) {
|
|
14309
|
-
blocks.push(new Uint8Array(BLOCK - overhang));
|
|
14310
|
-
}
|
|
14311
|
-
}
|
|
14312
|
-
blocks.push(new Uint8Array(BLOCK * 2));
|
|
14313
|
-
const total = blocks.reduce((sum, block) => sum + block.byteLength, 0);
|
|
14314
|
-
const tar = new Uint8Array(total);
|
|
14315
|
-
let offset = 0;
|
|
14316
|
-
for (const block of blocks) {
|
|
14317
|
-
tar.set(block, offset);
|
|
14318
|
-
offset += block.byteLength;
|
|
14319
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
|
+
})));
|
|
14320
14417
|
return new Uint8Array(gzipSync(tar, { level: 9 }));
|
|
14321
14418
|
};
|
|
14322
14419
|
|
|
@@ -14326,7 +14423,7 @@ var AGENT_SKILLS_SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema
|
|
|
14326
14423
|
var SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
14327
14424
|
var SKILL_NAME_MAX = 64;
|
|
14328
14425
|
var DESCRIPTION_MAX2 = 1024;
|
|
14329
|
-
var sha256 = (content) => `sha256:${
|
|
14426
|
+
var sha256 = (content) => `sha256:${createHash3("sha256").update(content).digest("hex")}`;
|
|
14330
14427
|
var collectEntries = async (dir, prefix = "") => {
|
|
14331
14428
|
const entries = [];
|
|
14332
14429
|
const items = await readdir(dir, { withFileTypes: true });
|
|
@@ -14587,6 +14684,20 @@ const redirectFor = (pathname) => {
|
|
|
14587
14684
|
return Object.hasOwn(REDIRECTS, path) ? REDIRECTS[path] : null;
|
|
14588
14685
|
};
|
|
14589
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
|
+
|
|
14590
14701
|
const parseAccept = (accept) =>
|
|
14591
14702
|
accept.split(",").map((part) => {
|
|
14592
14703
|
const segments = part.trim().split(";");
|
|
@@ -14666,7 +14777,7 @@ export default {
|
|
|
14666
14777
|
const redirect = redirectFor(url.pathname);
|
|
14667
14778
|
if (redirect !== null) {
|
|
14668
14779
|
return new Response(null, {
|
|
14669
|
-
headers: { location: redirect[0] },
|
|
14780
|
+
headers: { location: redirectLocation(redirect[0], url.search) },
|
|
14670
14781
|
status: redirect[1],
|
|
14671
14782
|
});
|
|
14672
14783
|
}
|
|
@@ -14839,7 +14950,7 @@ var buildRobots = (project) => {
|
|
|
14839
14950
|
const { site } = config.deployment;
|
|
14840
14951
|
if (site && config.seo.sitemap) {
|
|
14841
14952
|
const sitemapPath = withBasePath(normalizeBasePath(config.deployment.base), "/sitemap.xml");
|
|
14842
|
-
lines.push("", `Sitemap: ${site
|
|
14953
|
+
lines.push("", `Sitemap: ${absoluteUrl(site, sitemapPath)}`);
|
|
14843
14954
|
}
|
|
14844
14955
|
return `${lines.join(`
|
|
14845
14956
|
`)}
|
|
@@ -14934,12 +15045,19 @@ var lastmodTag = (value) => {
|
|
|
14934
15045
|
const date = new Date(value);
|
|
14935
15046
|
return Number.isNaN(date.getTime()) ? "" : `<lastmod>${date.toISOString().slice(0, 10)}</lastmod>`;
|
|
14936
15047
|
};
|
|
14937
|
-
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) => {
|
|
14938
15056
|
const { site } = project.config.deployment;
|
|
14939
15057
|
if (!(site && project.config.seo.sitemap)) {
|
|
14940
15058
|
return null;
|
|
14941
15059
|
}
|
|
14942
|
-
const base = site
|
|
15060
|
+
const base = siteRoot(site);
|
|
14943
15061
|
const deployBase = normalizeBasePath(project.config.deployment.base);
|
|
14944
15062
|
const seen = new Set;
|
|
14945
15063
|
const urls = [];
|
|
@@ -14966,12 +15084,27 @@ var buildSitemap = (project) => {
|
|
|
14966
15084
|
pushUrl(withBasePath(deployBase, route));
|
|
14967
15085
|
}
|
|
14968
15086
|
urls.sort();
|
|
14969
|
-
|
|
14970
|
-
|
|
14971
|
-
|
|
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(`
|
|
14972
15104
|
`)}
|
|
14973
|
-
</
|
|
15105
|
+
</sitemapindex>
|
|
14974
15106
|
`;
|
|
15107
|
+
return [{ name: "sitemap.xml", xml: index }, ...chunks];
|
|
14975
15108
|
};
|
|
14976
15109
|
|
|
14977
15110
|
// src/deploy/vercel-negotiation.ts
|
|
@@ -15081,6 +15214,11 @@ var buildSearchIndex = async (outDir) => {
|
|
|
15081
15214
|
return result.page_count;
|
|
15082
15215
|
};
|
|
15083
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
|
+
|
|
15084
15222
|
// src/search/facets.ts
|
|
15085
15223
|
var pageFacets = (page, config) => {
|
|
15086
15224
|
const declared = config.content.types[page.contentType]?.facets;
|
|
@@ -15098,25 +15236,60 @@ var pageFacets = (page, config) => {
|
|
|
15098
15236
|
};
|
|
15099
15237
|
|
|
15100
15238
|
// src/search/documents.ts
|
|
15101
|
-
var CODE_FENCE2 = /```[\s\S]*?```/gu;
|
|
15102
|
-
var INLINE_CODE2 = /`(?<code>[^`]+)`/gu;
|
|
15103
15239
|
var HTML_OR_JSX = /<\/?[a-zA-Z][^\n<>]*>|<\/?>/gu;
|
|
15104
|
-
var IMAGE = /!\[[^\]]*\]\([^)]*\)/gu;
|
|
15105
|
-
var LINK = /\[(?<text>[^\]]*)\]\([^)]*\)/gu;
|
|
15106
|
-
var HEADING_MARK = /^#{1,6}\s+/gmu;
|
|
15107
|
-
var MARKDOWN_PUNCT = /[*_~>]+/gu;
|
|
15108
15240
|
var WHITESPACE3 = /\s+/gu;
|
|
15109
|
-
var
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
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
|
+
}
|
|
15117
15283
|
}
|
|
15118
|
-
|
|
15119
|
-
|
|
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();
|
|
15120
15293
|
};
|
|
15121
15294
|
var buildCrumbIndex = (sidebar) => {
|
|
15122
15295
|
const index = new Map;
|
|
@@ -15436,7 +15609,7 @@ var refuseIfDevRunning = (root, action, options = {}) => {
|
|
|
15436
15609
|
};
|
|
15437
15610
|
|
|
15438
15611
|
// src/astro/generate.ts
|
|
15439
|
-
import { createHash as
|
|
15612
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
15440
15613
|
import { existsSync as existsSync16, readFileSync as readFileSync8, realpathSync } from "node:fs";
|
|
15441
15614
|
import {
|
|
15442
15615
|
lstat,
|
|
@@ -15449,6 +15622,8 @@ import {
|
|
|
15449
15622
|
} from "node:fs/promises";
|
|
15450
15623
|
import { createRequire as createRequire4 } from "node:module";
|
|
15451
15624
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
15625
|
+
import { imageSize as imageSize2 } from "image-size";
|
|
15626
|
+
import pMap6 from "p-map";
|
|
15452
15627
|
import { basename as basename3, dirname as dirname11, join as join28, normalize as normalize3, relative as relative14, resolve as resolve8 } from "pathe";
|
|
15453
15628
|
import { glob as glob7 } from "tinyglobby";
|
|
15454
15629
|
|
|
@@ -15557,7 +15732,7 @@ var MCP_TOOLS = [
|
|
|
15557
15732
|
// src/ai/mcp/discovery.ts
|
|
15558
15733
|
var serverUrl = (input) => {
|
|
15559
15734
|
const path = withBasePath(input.base, input.route);
|
|
15560
|
-
return input.site ?
|
|
15735
|
+
return input.site ? absoluteUrl(input.site, path) : path;
|
|
15561
15736
|
};
|
|
15562
15737
|
var buildMcpDiscovery = (input) => ({
|
|
15563
15738
|
servers: [
|
|
@@ -15570,7 +15745,10 @@ var buildMcpDiscovery = (input) => ({
|
|
|
15570
15745
|
});
|
|
15571
15746
|
var SERVER_CARD_SCHEMA = "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json";
|
|
15572
15747
|
var CARD_TEXT_MAX = 100;
|
|
15573
|
-
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, "-"), "-");
|
|
15574
15752
|
var reverseDnsName = (input) => {
|
|
15575
15753
|
let namespace = "localhost";
|
|
15576
15754
|
if (input.site) {
|
|
@@ -15578,7 +15756,7 @@ var reverseDnsName = (input) => {
|
|
|
15578
15756
|
namespace = new URL(input.site).hostname.split(".").toReversed().join(".");
|
|
15579
15757
|
} catch {}
|
|
15580
15758
|
}
|
|
15581
|
-
return `${namespace}/${
|
|
15759
|
+
return `${namespace}/${asciiSlugify(input.name) || "docs"}`;
|
|
15582
15760
|
};
|
|
15583
15761
|
var HTTP_URL2 = /^https?:\/\//u;
|
|
15584
15762
|
var buildMcpServerCard = (input) => {
|
|
@@ -15601,7 +15779,7 @@ var buildMcpServerCard = (input) => {
|
|
|
15601
15779
|
transports: [{ endpoint: url, type: "streamable-http" }],
|
|
15602
15780
|
url,
|
|
15603
15781
|
version: input.version,
|
|
15604
|
-
...input.site ? { websiteUrl:
|
|
15782
|
+
...input.site ? { websiteUrl: siteRoot(input.site) } : {}
|
|
15605
15783
|
};
|
|
15606
15784
|
};
|
|
15607
15785
|
|
|
@@ -15936,9 +16114,9 @@ var analyzeComponentOverrides = (source, filePath) => {
|
|
|
15936
16114
|
import { mkdir as mkdir6 } from "node:fs/promises";
|
|
15937
16115
|
import { dirname as dirname9 } from "pathe";
|
|
15938
16116
|
import writeFileAtomic from "write-file-atomic";
|
|
15939
|
-
var writeTextAtomic = async (path,
|
|
16117
|
+
var writeTextAtomic = async (path, text) => {
|
|
15940
16118
|
await mkdir6(dirname9(path), { recursive: true });
|
|
15941
|
-
await writeFileAtomic(path,
|
|
16119
|
+
await writeFileAtomic(path, text, { encoding: "utf-8", fsync: false });
|
|
15942
16120
|
};
|
|
15943
16121
|
|
|
15944
16122
|
// src/core/tsconfig-aliases.ts
|
|
@@ -16185,9 +16363,8 @@ ${dark}`;
|
|
|
16185
16363
|
|
|
16186
16364
|
// src/openapi/scalar.ts
|
|
16187
16365
|
var URL_SPEC2 = /^https?:\/\//u;
|
|
16188
|
-
var ROUTE_EDGES = /^\/+|\/+$/gu;
|
|
16189
16366
|
var referencePagePath = (route) => {
|
|
16190
|
-
const segments = route
|
|
16367
|
+
const segments = trimChar(route, "/");
|
|
16191
16368
|
return `${segments === "" ? "index" : segments}.astro`;
|
|
16192
16369
|
};
|
|
16193
16370
|
var darkModeConfig = (mode) => {
|
|
@@ -17229,14 +17406,17 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
|
|
|
17229
17406
|
|
|
17230
17407
|
// src/astro/examples.ts
|
|
17231
17408
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
17409
|
+
import pMap5 from "p-map";
|
|
17232
17410
|
import { join as join27, relative as relative13 } from "pathe";
|
|
17233
17411
|
import { glob as glob6 } from "tinyglobby";
|
|
17234
17412
|
|
|
17235
17413
|
// src/astro/islands.ts
|
|
17236
17414
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
17415
|
+
import pMap4 from "p-map";
|
|
17237
17416
|
import { basename as basename2, join as join26 } from "pathe";
|
|
17238
17417
|
import { glob as glob5 } from "tinyglobby";
|
|
17239
17418
|
var DEFAULT_CLIENT = "visible";
|
|
17419
|
+
var READ_CONCURRENCY2 = 16;
|
|
17240
17420
|
var VALID_MODES = new Set([
|
|
17241
17421
|
"idle",
|
|
17242
17422
|
"load",
|
|
@@ -17270,7 +17450,9 @@ var discoverIslands = async (root) => {
|
|
|
17270
17450
|
onlyFiles: true
|
|
17271
17451
|
});
|
|
17272
17452
|
const files = matches2.toSorted();
|
|
17273
|
-
const sources = await
|
|
17453
|
+
const sources = await pMap4(files, (file) => readFile14(file, "utf-8"), {
|
|
17454
|
+
concurrency: READ_CONCURRENCY2
|
|
17455
|
+
});
|
|
17274
17456
|
const islands = [];
|
|
17275
17457
|
const warnings = [];
|
|
17276
17458
|
const seen = new Map;
|
|
@@ -17315,6 +17497,7 @@ var FRAMEWORK_BY_EXT3 = {
|
|
|
17315
17497
|
};
|
|
17316
17498
|
var EXAMPLE_FILE = /\.(?<ext>astro|jsx|svelte|tsx|vue)$/u;
|
|
17317
17499
|
var DEFAULT_EXAMPLE_GLOB = "**/*.{astro,jsx,svelte,tsx,vue}";
|
|
17500
|
+
var READ_CONCURRENCY3 = 16;
|
|
17318
17501
|
var GLOB_MAGIC = /[!*?[\]{}]/u;
|
|
17319
17502
|
var splitGlobBase = (pattern) => {
|
|
17320
17503
|
const segments = pattern.split("/");
|
|
@@ -17333,7 +17516,9 @@ var discoverExamples = async (root, pattern = "examples") => {
|
|
|
17333
17516
|
onlyFiles: true
|
|
17334
17517
|
});
|
|
17335
17518
|
const files = matches2.toSorted();
|
|
17336
|
-
const sources = await
|
|
17519
|
+
const sources = await pMap5(files, (file) => readFile15(file, "utf-8"), {
|
|
17520
|
+
concurrency: READ_CONCURRENCY3
|
|
17521
|
+
});
|
|
17337
17522
|
const examples = [];
|
|
17338
17523
|
const warnings = [];
|
|
17339
17524
|
const seen = new Map;
|
|
@@ -17542,6 +17727,18 @@ var searchProviderWarnings = (provider, root, pkgDir = packageRoot()) => {
|
|
|
17542
17727
|
}
|
|
17543
17728
|
return warnings;
|
|
17544
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
|
+
};
|
|
17545
17742
|
var examplesCssFile = (root, config) => config.examples.css ? join28(root, config.examples.css) : null;
|
|
17546
17743
|
var writeExamplesPreview = async (options) => {
|
|
17547
17744
|
const { config, hasExamples, root, srcDir, write } = options;
|
|
@@ -17572,16 +17769,19 @@ var detectNeedsReact = async (root) => {
|
|
|
17572
17769
|
return matches2.length > 0;
|
|
17573
17770
|
};
|
|
17574
17771
|
var containsMath = (content) => content.includes("$$") || content.includes("<Math");
|
|
17772
|
+
var READ_CONCURRENCY4 = 16;
|
|
17575
17773
|
var detectUsesMath = async (root, staged = []) => {
|
|
17576
17774
|
const files = await glob7(["**/*.{md,mdx}"], {
|
|
17577
17775
|
cwd: root,
|
|
17578
17776
|
ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
|
|
17579
17777
|
onlyFiles: true
|
|
17580
17778
|
});
|
|
17581
|
-
const contents = await
|
|
17779
|
+
const contents = await pMap6(files, (file) => readOptional(join28(root, file)), {
|
|
17780
|
+
concurrency: READ_CONCURRENCY4
|
|
17781
|
+
});
|
|
17582
17782
|
return [...contents, ...staged].some(containsMath);
|
|
17583
17783
|
};
|
|
17584
|
-
var hashConfigSource = (source) =>
|
|
17784
|
+
var hashConfigSource = (source) => createHash4("sha256").update(source).digest("hex");
|
|
17585
17785
|
var loadIntegrationBridge = async (config, context) => {
|
|
17586
17786
|
if (config.integrations.length === 0 || !context.configFile) {
|
|
17587
17787
|
return;
|
|
@@ -17631,35 +17831,25 @@ var collectStaged = (project) => {
|
|
|
17631
17831
|
var writeStagedContent = async (out, staged) => {
|
|
17632
17832
|
const contentDir = stagedContentDir(out);
|
|
17633
17833
|
const written = new Set;
|
|
17634
|
-
await Promise.all([...staged].map(async ([entryId,
|
|
17834
|
+
await Promise.all([...staged].map(async ([entryId, text]) => {
|
|
17635
17835
|
const path = join28(contentDir, entryId);
|
|
17636
17836
|
written.add(normalize3(path));
|
|
17637
|
-
await writeIfChanged(path,
|
|
17837
|
+
await writeIfChanged(path, text);
|
|
17638
17838
|
}));
|
|
17639
17839
|
if (existsSync16(contentDir)) {
|
|
17640
17840
|
await pruneOrphans(contentDir, written);
|
|
17641
17841
|
}
|
|
17642
17842
|
};
|
|
17643
|
-
var SVG_ROOT = /<svg\b(?<attributes>[^>]*)>/u;
|
|
17644
|
-
var SVG_WIDTH = /\bwidth\s*=\s*["'](?<value>[^"']+)["']/u;
|
|
17645
|
-
var SVG_HEIGHT = /\bheight\s*=\s*["'](?<value>[^"']+)["']/u;
|
|
17646
|
-
var SVG_LENGTH = /^\s*(?<value>[\d.]+)(?:px)?\s*$/u;
|
|
17647
|
-
var SVG_VIEW_BOX = /\bviewBox\s*=\s*["'][\d.-]+[\s,]+[\d.-]+[\s,]+(?<width>[\d.]+)[\s,]+(?<height>[\d.]+)["']/u;
|
|
17648
|
-
var parseSvgLength = (value) => {
|
|
17649
|
-
const length = Number(value?.match(SVG_LENGTH)?.groups?.value);
|
|
17650
|
-
return length > 0 ? length : undefined;
|
|
17651
|
-
};
|
|
17652
17843
|
var svgDimensions = (svg) => {
|
|
17653
|
-
|
|
17654
|
-
|
|
17655
|
-
|
|
17656
|
-
|
|
17657
|
-
|
|
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;
|
|
17658
17852
|
}
|
|
17659
|
-
const viewBox = attributes?.match(SVG_VIEW_BOX);
|
|
17660
|
-
const viewBoxWidth = Number(viewBox?.groups?.width);
|
|
17661
|
-
const viewBoxHeight = Number(viewBox?.groups?.height);
|
|
17662
|
-
return viewBoxWidth > 0 && viewBoxHeight > 0 ? { height: viewBoxHeight, width: viewBoxWidth } : undefined;
|
|
17663
17853
|
};
|
|
17664
17854
|
var readLogoSvg = (project, source) => {
|
|
17665
17855
|
if (!source?.toLowerCase().endsWith(".svg")) {
|
|
@@ -17678,7 +17868,7 @@ var resolveLogo = (project) => {
|
|
|
17678
17868
|
return null;
|
|
17679
17869
|
}
|
|
17680
17870
|
const config = typeof logo === "string" ? { image: logo } : logo;
|
|
17681
|
-
const { href, image: source, text
|
|
17871
|
+
const { href, image: source, text } = config;
|
|
17682
17872
|
const image = typeof source === "string" ? { light: source } : source;
|
|
17683
17873
|
const light = image?.light ?? image?.dark;
|
|
17684
17874
|
const dark = image?.dark ?? image?.light;
|
|
@@ -17687,12 +17877,12 @@ var resolveLogo = (project) => {
|
|
|
17687
17877
|
const lightSvg = readLogoSvg(project, light);
|
|
17688
17878
|
const darkSvg = dark === light ? lightSvg : readLogoSvg(project, dark);
|
|
17689
17879
|
if (light && light === dark && lightSvg) {
|
|
17690
|
-
return { alt, href: brandHref, svg: lightSvg, text
|
|
17880
|
+
return { alt, href: brandHref, svg: lightSvg, text };
|
|
17691
17881
|
}
|
|
17692
17882
|
const lightDimensions = svgDimensions(lightSvg);
|
|
17693
17883
|
const darkDimensions = svgDimensions(darkSvg);
|
|
17694
17884
|
const dimensions = lightDimensions || darkDimensions ? { dark: darkDimensions, light: lightDimensions } : undefined;
|
|
17695
|
-
return { alt, dark, dimensions, href: brandHref, light, text
|
|
17885
|
+
return { alt, dark, dimensions, href: brandHref, light, text };
|
|
17696
17886
|
};
|
|
17697
17887
|
var FAVICON_CANDIDATES = [
|
|
17698
17888
|
"icon.svg",
|
|
@@ -17980,7 +18170,7 @@ var writeAskFiles = async (project, srcDir, write) => {
|
|
|
17980
18170
|
await write(join28(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
|
|
17981
18171
|
`);
|
|
17982
18172
|
}
|
|
17983
|
-
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));
|
|
17984
18174
|
};
|
|
17985
18175
|
var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
17986
18176
|
if (routeIsTaken(pages, contentPages, "/404")) {
|
|
@@ -18217,7 +18407,7 @@ var generateRuntime = async (project) => {
|
|
|
18217
18407
|
...islandDiscovery.islands.map((island) => island.name),
|
|
18218
18408
|
...overrideTags
|
|
18219
18409
|
]);
|
|
18220
|
-
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));
|
|
18221
18411
|
if (hasScalarReferences(config)) {
|
|
18222
18412
|
const references = await buildReferenceFiles({
|
|
18223
18413
|
config,
|
|
@@ -18241,37 +18431,24 @@ var generateRuntime = async (project) => {
|
|
|
18241
18431
|
};
|
|
18242
18432
|
|
|
18243
18433
|
// src/cli/env.ts
|
|
18244
|
-
import { existsSync as existsSync17
|
|
18245
|
-
import {
|
|
18434
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
18435
|
+
import { config } from "dotenv";
|
|
18246
18436
|
import { dirname as dirname12, join as join29, resolve as resolve9 } from "pathe";
|
|
18247
|
-
var applyEnv = (parsed) => {
|
|
18248
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
18249
|
-
if (!(key in process.env)) {
|
|
18250
|
-
process.env[key] = value;
|
|
18251
|
-
}
|
|
18252
|
-
}
|
|
18253
|
-
};
|
|
18254
|
-
var loadFile = (path) => {
|
|
18255
|
-
try {
|
|
18256
|
-
if (existsSync17(path)) {
|
|
18257
|
-
applyEnv(parse2(readFileSync9(path, "utf-8")));
|
|
18258
|
-
}
|
|
18259
|
-
} catch {}
|
|
18260
|
-
};
|
|
18261
18437
|
var loadEnvFiles = (startDir) => {
|
|
18438
|
+
const paths = [];
|
|
18262
18439
|
let dir = resolve9(startDir);
|
|
18263
18440
|
let done = false;
|
|
18264
18441
|
while (!done) {
|
|
18265
|
-
|
|
18266
|
-
loadFile(join29(dir, ".env"));
|
|
18442
|
+
paths.push(join29(dir, ".env.local"), join29(dir, ".env"));
|
|
18267
18443
|
const parent = dirname12(dir);
|
|
18268
18444
|
done = existsSync17(join29(dir, ".git")) || parent === dir;
|
|
18269
18445
|
dir = parent;
|
|
18270
18446
|
}
|
|
18447
|
+
config({ path: paths, quiet: true });
|
|
18271
18448
|
};
|
|
18272
18449
|
|
|
18273
18450
|
// src/cli/required-secrets.ts
|
|
18274
|
-
var checkRequiredSecrets = (
|
|
18451
|
+
var checkRequiredSecrets = (config2) => {
|
|
18275
18452
|
const diagnostics = [];
|
|
18276
18453
|
const requireSecret = (feature, env, note) => {
|
|
18277
18454
|
if (process.env[env]) {
|
|
@@ -18285,15 +18462,15 @@ var checkRequiredSecrets = (config) => {
|
|
|
18285
18462
|
suggestion: `Set ${env} in .env.local for local dev, or in your host's environment for production.`
|
|
18286
18463
|
});
|
|
18287
18464
|
};
|
|
18288
|
-
if (
|
|
18289
|
-
const backend = resolveAskBackend(
|
|
18465
|
+
if (config2.ai.ask?.enabled && !config2.ai.ask.endpoint) {
|
|
18466
|
+
const backend = resolveAskBackend(config2.ai.ask);
|
|
18290
18467
|
if (backend.kind === "gateway") {
|
|
18291
18468
|
requireSecret("Ask AI (AI Gateway)", "AI_GATEWAY_API_KEY", "on Vercel the gateway can also authenticate via OIDC");
|
|
18292
18469
|
} else {
|
|
18293
18470
|
requireSecret("Ask AI", backend.apiKeyEnv);
|
|
18294
18471
|
}
|
|
18295
18472
|
}
|
|
18296
|
-
if (
|
|
18473
|
+
if (config2.search.provider === "mixedbread") {
|
|
18297
18474
|
requireSecret("Mixedbread search", "MIXEDBREAD_API_KEY");
|
|
18298
18475
|
}
|
|
18299
18476
|
return diagnostics;
|
|
@@ -18365,9 +18542,9 @@ var validateBudgetFlags = (args) => {
|
|
|
18365
18542
|
}
|
|
18366
18543
|
}
|
|
18367
18544
|
};
|
|
18368
|
-
var emitRedirectFiles = async (
|
|
18369
|
-
const redirects = platformRedirects(
|
|
18370
|
-
if (redirects.length === 0 ||
|
|
18545
|
+
var emitRedirectFiles = async (config2, distDir) => {
|
|
18546
|
+
const redirects = platformRedirects(config2);
|
|
18547
|
+
if (redirects.length === 0 || config2.deployment.output !== "static") {
|
|
18371
18548
|
return;
|
|
18372
18549
|
}
|
|
18373
18550
|
await writeFile7(join30(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
|
|
@@ -18379,11 +18556,11 @@ var emitRedirectFiles = async (config, distDir) => {
|
|
|
18379
18556
|
logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
|
|
18380
18557
|
};
|
|
18381
18558
|
var emitHeaderFiles = async (project, distDir) => {
|
|
18382
|
-
const { config } = project;
|
|
18383
|
-
if (!readsHeaderFiles(
|
|
18559
|
+
const { config: config2 } = project;
|
|
18560
|
+
if (!readsHeaderFiles(config2.deployment) || existsSync18(join30(project.context.root, "public", "_headers"))) {
|
|
18384
18561
|
return;
|
|
18385
18562
|
}
|
|
18386
|
-
const ours = buildNetlifyHeaders(
|
|
18563
|
+
const ours = buildNetlifyHeaders(config2, buildHomeLinkHeader(config2, markdownRoutePaths(project)));
|
|
18387
18564
|
const target = join30(distDir, "_headers");
|
|
18388
18565
|
const existing = existsSync18(target) ? await readFile17(target, "utf-8") : "";
|
|
18389
18566
|
await writeFile7(target, existing ? `${existing.trimEnd()}
|
|
@@ -18420,15 +18597,15 @@ var emitAgentSkills = async (project, distDir) => {
|
|
|
18420
18597
|
await writeFile7(join30(outDir, "index.json"), buildSkillsIndex(skills, project.config), "utf-8");
|
|
18421
18598
|
logger.success(`Published ${skills.length} agent skill(s) (.well-known/agent-skills/index.json)`);
|
|
18422
18599
|
};
|
|
18423
|
-
var emitWellKnownFiles = async (
|
|
18600
|
+
var emitWellKnownFiles = async (config2, distDir) => {
|
|
18424
18601
|
const files = [
|
|
18425
18602
|
{
|
|
18426
|
-
content: buildSignaturesDirectory(
|
|
18603
|
+
content: buildSignaturesDirectory(config2),
|
|
18427
18604
|
label: "Web Bot Auth",
|
|
18428
18605
|
path: SIGNATURES_DIRECTORY_PATH
|
|
18429
18606
|
},
|
|
18430
18607
|
{
|
|
18431
|
-
content: buildApiCatalog(
|
|
18608
|
+
content: buildApiCatalog(config2),
|
|
18432
18609
|
label: "RFC 9727",
|
|
18433
18610
|
path: API_CATALOG_PATH
|
|
18434
18611
|
}
|
|
@@ -18444,18 +18621,18 @@ var emitWellKnownFiles = async (config, distDir) => {
|
|
|
18444
18621
|
}
|
|
18445
18622
|
};
|
|
18446
18623
|
var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
18447
|
-
const { config } = project;
|
|
18624
|
+
const { config: config2 } = project;
|
|
18448
18625
|
const configPath = join30(root, ".vercel", "output", "config.json");
|
|
18449
18626
|
if (!existsSync18(configPath)) {
|
|
18450
18627
|
return;
|
|
18451
18628
|
}
|
|
18452
18629
|
const overrides = {
|
|
18453
|
-
...hasApiCatalog(
|
|
18454
|
-
...
|
|
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 } : {}
|
|
18455
18632
|
};
|
|
18456
18633
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
18457
18634
|
const home = rawMarkdown["/"];
|
|
18458
|
-
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);
|
|
18459
18636
|
if (injected === null) {
|
|
18460
18637
|
logger.warn("Could not wire Accept: text/markdown negotiation into .vercel/output/config.json — raw Markdown stays available at the .md URLs.");
|
|
18461
18638
|
return;
|
|
@@ -18465,7 +18642,7 @@ var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
|
18465
18642
|
};
|
|
18466
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.");
|
|
18467
18644
|
var emitCloudflareNegotiation = async (project, routePaths) => {
|
|
18468
|
-
const { config, context } = project;
|
|
18645
|
+
const { config: config2, context } = project;
|
|
18469
18646
|
const serverDir = join30(context.distDir ?? join30(context.root, "dist"), "server");
|
|
18470
18647
|
const wranglerPath = join30(serverDir, "wrangler.json");
|
|
18471
18648
|
if (!existsSync18(wranglerPath)) {
|
|
@@ -18475,11 +18652,11 @@ var emitCloudflareNegotiation = async (project, routePaths) => {
|
|
|
18475
18652
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
18476
18653
|
const home = rawMarkdown["/"];
|
|
18477
18654
|
const injected = injectWorkerNegotiation(await readFile17(wranglerPath, "utf-8"), {
|
|
18478
|
-
base:
|
|
18655
|
+
base: config2.deployment.base,
|
|
18479
18656
|
contentRoutePaths: project.manifest.routes.map((route) => route.path),
|
|
18480
|
-
homeLinkHeader: buildHomeLinkHeader(
|
|
18657
|
+
homeLinkHeader: buildHomeLinkHeader(config2, routePaths),
|
|
18481
18658
|
homeTokens: home ? markdownTokenCount(agentMarkdown(home)) : undefined,
|
|
18482
|
-
redirects: platformRedirects(
|
|
18659
|
+
redirects: platformRedirects(config2),
|
|
18483
18660
|
routePaths
|
|
18484
18661
|
});
|
|
18485
18662
|
if (injected === null) {
|
|
@@ -18555,20 +18732,20 @@ var runClientAssetChecks = async (staticDir, args) => {
|
|
|
18555
18732
|
process.exit(1);
|
|
18556
18733
|
}
|
|
18557
18734
|
};
|
|
18558
|
-
var isolatedOutputDir = (
|
|
18559
|
-
const { adapter, output } =
|
|
18735
|
+
var isolatedOutputDir = (config2, context) => {
|
|
18736
|
+
const { adapter, output } = config2.deployment;
|
|
18560
18737
|
if (output === "server" && adapter === "vercel") {
|
|
18561
18738
|
return join30(context.outDir, ".vercel", "output");
|
|
18562
18739
|
}
|
|
18563
18740
|
return context.distDir ?? join30(context.outDir, "dist");
|
|
18564
18741
|
};
|
|
18565
|
-
var isolatedStaticDir = (
|
|
18566
|
-
const { adapter, output } =
|
|
18567
|
-
const outputDir = isolatedOutputDir(
|
|
18742
|
+
var isolatedStaticDir = (config2, context) => {
|
|
18743
|
+
const { adapter, output } = config2.deployment;
|
|
18744
|
+
const outputDir = isolatedOutputDir(config2, context);
|
|
18568
18745
|
if (output === "server" && adapter === "vercel") {
|
|
18569
18746
|
return join30(outputDir, "static");
|
|
18570
18747
|
}
|
|
18571
|
-
if (servesClientSubdir(
|
|
18748
|
+
if (servesClientSubdir(config2.deployment)) {
|
|
18572
18749
|
return join30(outputDir, "client");
|
|
18573
18750
|
}
|
|
18574
18751
|
return outputDir;
|
|
@@ -18609,10 +18786,10 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
18609
18786
|
if (project.config.ai.llmsTxt.enabled) {
|
|
18610
18787
|
await publishLlmsFiles(project, distDir);
|
|
18611
18788
|
}
|
|
18612
|
-
const
|
|
18613
|
-
if (
|
|
18614
|
-
await writeFile7(join30(distDir,
|
|
18615
|
-
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)`);
|
|
18616
18793
|
}
|
|
18617
18794
|
const robots = buildRobots(project);
|
|
18618
18795
|
if (robots && !existsSync18(join30(distDir, "robots.txt"))) {
|
|
@@ -18629,19 +18806,19 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
18629
18806
|
await emitAgentSkills(project, distDir);
|
|
18630
18807
|
await emitRedirectFiles(project.config, distDir);
|
|
18631
18808
|
await emitHeaderFiles(project, distDir);
|
|
18632
|
-
const { config } = project;
|
|
18633
|
-
const features = serverFeatures(
|
|
18634
|
-
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)";
|
|
18635
18812
|
logger.box([
|
|
18636
|
-
`Output ${
|
|
18637
|
-
`Adapter ${
|
|
18638
|
-
`Site ${
|
|
18639
|
-
`Search ${
|
|
18640
|
-
`Redirects ${
|
|
18641
|
-
`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}`,
|
|
18642
18819
|
`Robots ${robots ? "yes" : "no"}`,
|
|
18643
18820
|
`Agent JSON ${agentReadability ? "yes" : "no"}`,
|
|
18644
|
-
`LLM files ${
|
|
18821
|
+
`LLM files ${config2.ai.llmsTxt.enabled ? "yes" : "no"}`,
|
|
18645
18822
|
`Server features ${features.length > 0 ? features.join(", ") : "none"}`
|
|
18646
18823
|
].join(`
|
|
18647
18824
|
`));
|
|
@@ -18814,6 +18991,7 @@ var checkCommand = defineCommand4({
|
|
|
18814
18991
|
import { dev } from "astro";
|
|
18815
18992
|
import { watch } from "chokidar";
|
|
18816
18993
|
import { defineCommand as defineCommand5 } from "citty";
|
|
18994
|
+
import { debounce } from "perfect-debounce";
|
|
18817
18995
|
|
|
18818
18996
|
// src/astro/integration.ts
|
|
18819
18997
|
var overlayServer = null;
|
|
@@ -18868,29 +19046,6 @@ var parsePort = (value) => {
|
|
|
18868
19046
|
return port;
|
|
18869
19047
|
};
|
|
18870
19048
|
|
|
18871
|
-
// src/cli/coalesce.ts
|
|
18872
|
-
var coalescedRunner = (task) => {
|
|
18873
|
-
let inFlight = null;
|
|
18874
|
-
let pending = false;
|
|
18875
|
-
const cycle = async () => {
|
|
18876
|
-
try {
|
|
18877
|
-
do {
|
|
18878
|
-
pending = false;
|
|
18879
|
-
await task();
|
|
18880
|
-
} while (pending);
|
|
18881
|
-
} finally {
|
|
18882
|
-
inFlight = null;
|
|
18883
|
-
}
|
|
18884
|
-
};
|
|
18885
|
-
return () => {
|
|
18886
|
-
if (inFlight) {
|
|
18887
|
-
pending = true;
|
|
18888
|
-
return;
|
|
18889
|
-
}
|
|
18890
|
-
inFlight = cycle();
|
|
18891
|
-
};
|
|
18892
|
-
};
|
|
18893
|
-
|
|
18894
19049
|
// src/cli/commands/dev.ts
|
|
18895
19050
|
var normalizeHost = (host) => host === "" ? true : host ?? false;
|
|
18896
19051
|
var routeSignature = (routes) => routes.map((route) => `${route.path} ${route.entryId}`).toSorted().join(`
|
|
@@ -18958,7 +19113,7 @@ var devCommand = defineCommand5({
|
|
|
18958
19113
|
}
|
|
18959
19114
|
showBlumeErrorOverlay(project.diagnostics);
|
|
18960
19115
|
let lastSignature = routeSignature(project.manifest.routes);
|
|
18961
|
-
const
|
|
19116
|
+
const regenerate = debounce(async () => {
|
|
18962
19117
|
try {
|
|
18963
19118
|
const next = await scanProject(root, {
|
|
18964
19119
|
devServerUrl,
|
|
@@ -18981,16 +19136,9 @@ var devCommand = defineCommand5({
|
|
|
18981
19136
|
} catch (error) {
|
|
18982
19137
|
logger.error(`Regeneration failed: ${error.message}`);
|
|
18983
19138
|
}
|
|
18984
|
-
});
|
|
18985
|
-
let timer = null;
|
|
18986
|
-
const regenerate = () => {
|
|
18987
|
-
if (timer) {
|
|
18988
|
-
clearTimeout(timer);
|
|
18989
|
-
}
|
|
18990
|
-
timer = setTimeout(runRegenerate, 80);
|
|
18991
|
-
};
|
|
19139
|
+
}, 80);
|
|
18992
19140
|
if (boundPort !== port) {
|
|
18993
|
-
|
|
19141
|
+
regenerate();
|
|
18994
19142
|
}
|
|
18995
19143
|
const dirTargets = [project.context.pagesRoot].filter((target) => target !== null);
|
|
18996
19144
|
const fileTargets = [
|
|
@@ -19019,14 +19167,14 @@ var devCommand = defineCommand5({
|
|
|
19019
19167
|
});
|
|
19020
19168
|
|
|
19021
19169
|
// src/cli/commands/doctor.ts
|
|
19022
|
-
import { readFileSync as
|
|
19170
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
19023
19171
|
import { defineCommand as defineCommand6 } from "citty";
|
|
19024
19172
|
import { join as join32 } from "pathe";
|
|
19025
19173
|
import { satisfies } from "semver";
|
|
19026
19174
|
var FALLBACK_NODE_RANGE = ">=22.12.0";
|
|
19027
19175
|
var supportedNodeRange = () => {
|
|
19028
19176
|
try {
|
|
19029
|
-
const pkg = JSON.parse(
|
|
19177
|
+
const pkg = JSON.parse(readFileSync9(join32(packageRoot(), "package.json"), "utf-8"));
|
|
19030
19178
|
return pkg.engines?.node || FALLBACK_NODE_RANGE;
|
|
19031
19179
|
} catch {
|
|
19032
19180
|
return FALLBACK_NODE_RANGE;
|
|
@@ -19057,9 +19205,9 @@ var doctorCommand = defineCommand6({
|
|
|
19057
19205
|
try {
|
|
19058
19206
|
const project = await scanProject(root, { mode: "build" });
|
|
19059
19207
|
diagnostics.push(...project.diagnostics);
|
|
19060
|
-
const { config } = project;
|
|
19061
|
-
const features = serverFeatures(
|
|
19062
|
-
if (features.length > 0 &&
|
|
19208
|
+
const { config: config2 } = project;
|
|
19209
|
+
const features = serverFeatures(config2);
|
|
19210
|
+
if (features.length > 0 && config2.deployment.output === "static") {
|
|
19063
19211
|
diagnostics.push({
|
|
19064
19212
|
code: "BLUME_SERVER_FEATURE_REQUIRED",
|
|
19065
19213
|
message: `${features.join(", ")} require server output.`,
|
|
@@ -19067,7 +19215,7 @@ var doctorCommand = defineCommand6({
|
|
|
19067
19215
|
suggestion: 'Set deployment.output to "server".'
|
|
19068
19216
|
});
|
|
19069
19217
|
}
|
|
19070
|
-
if (
|
|
19218
|
+
if (config2.deployment.output === "server" && !config2.deployment.adapter) {
|
|
19071
19219
|
diagnostics.push({
|
|
19072
19220
|
code: "BLUME_ADAPTER_REQUIRED",
|
|
19073
19221
|
message: "Server output requires an adapter.",
|
|
@@ -19077,8 +19225,8 @@ var doctorCommand = defineCommand6({
|
|
|
19077
19225
|
}
|
|
19078
19226
|
if (!args.json) {
|
|
19079
19227
|
logger.info(`Pages: ${project.graph.pages.length}`);
|
|
19080
|
-
logger.info(`Output: ${
|
|
19081
|
-
logger.info(`Search: ${
|
|
19228
|
+
logger.info(`Output: ${config2.deployment.output}`);
|
|
19229
|
+
logger.info(`Search: ${config2.search.provider}`);
|
|
19082
19230
|
}
|
|
19083
19231
|
} catch (error) {
|
|
19084
19232
|
if (error instanceof BlumeError) {
|
|
@@ -19224,16 +19372,16 @@ var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
|
|
|
19224
19372
|
path: join33(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
|
|
19225
19373
|
}
|
|
19226
19374
|
] : [];
|
|
19227
|
-
var ejectIntegrationBridge = (
|
|
19375
|
+
var ejectIntegrationBridge = (config2, root, configFile) => config2.integrations.length > 0 && configFile ? { configFile: toPosix(relative15(root, configFile)) } : undefined;
|
|
19228
19376
|
var eject = async (root) => {
|
|
19229
19377
|
const project = await scanProject(root, { mode: "build" });
|
|
19230
|
-
const { context, config } = project;
|
|
19378
|
+
const { context, config: config2 } = project;
|
|
19231
19379
|
const srcDir = join33(root, "src");
|
|
19232
19380
|
const genDir = join33(srcDir, "generated");
|
|
19233
|
-
const askEnabled =
|
|
19234
|
-
const exportPdf =
|
|
19235
|
-
const exportEpub =
|
|
19236
|
-
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);
|
|
19237
19385
|
project.examples = exampleMarkdownLookup(examples.examples);
|
|
19238
19386
|
const [
|
|
19239
19387
|
pages,
|
|
@@ -19248,7 +19396,7 @@ var eject = async (root) => {
|
|
|
19248
19396
|
detectNeedsReact(root),
|
|
19249
19397
|
detectUsesMath(root),
|
|
19250
19398
|
context.themeFile ? readFile18(context.themeFile, "utf-8") : Promise.resolve(""),
|
|
19251
|
-
readExamplesCss(root,
|
|
19399
|
+
readExamplesCss(root, config2.examples.css),
|
|
19252
19400
|
buildRawMarkdown(project),
|
|
19253
19401
|
discoverIslands(root)
|
|
19254
19402
|
]);
|
|
@@ -19280,13 +19428,13 @@ var eject = async (root) => {
|
|
|
19280
19428
|
{
|
|
19281
19429
|
content: astroConfigTemplate({
|
|
19282
19430
|
askPath: "./src/generated/Ask.astro",
|
|
19283
|
-
config,
|
|
19431
|
+
config: config2,
|
|
19284
19432
|
contentRoutes: project.manifest.routes.map((route) => route.path),
|
|
19285
19433
|
context: relContext,
|
|
19286
19434
|
dataPath: "./src/generated/data.json",
|
|
19287
19435
|
examplesPath: "./src/generated/examples.ts",
|
|
19288
19436
|
examplesThemePath: "./src/generated/examples.css",
|
|
19289
|
-
integrationBridge: ejectIntegrationBridge(
|
|
19437
|
+
integrationBridge: ejectIntegrationBridge(config2, root, context.configFile),
|
|
19290
19438
|
needsReact,
|
|
19291
19439
|
needsSvelte,
|
|
19292
19440
|
needsVue,
|
|
@@ -19305,7 +19453,7 @@ var eject = async (root) => {
|
|
|
19305
19453
|
{ content: envTemplate(), path: join33(srcDir, "env.d.ts") },
|
|
19306
19454
|
{
|
|
19307
19455
|
content: contentConfigTemplate({
|
|
19308
|
-
config,
|
|
19456
|
+
config: config2,
|
|
19309
19457
|
context: relContext,
|
|
19310
19458
|
staged: hasStaged,
|
|
19311
19459
|
stagedBase: stagedDir
|
|
@@ -19330,12 +19478,12 @@ var eject = async (root) => {
|
|
|
19330
19478
|
path: join33(genDir, "islands.ts")
|
|
19331
19479
|
},
|
|
19332
19480
|
{
|
|
19333
|
-
content: exampleMapTemplate(examples.examples,
|
|
19481
|
+
content: exampleMapTemplate(examples.examples, config2.basePath),
|
|
19334
19482
|
path: join33(genDir, "examples.ts")
|
|
19335
19483
|
},
|
|
19336
19484
|
{
|
|
19337
19485
|
content: examplesEntryTemplate({
|
|
19338
|
-
configTokens: buildThemeCss(
|
|
19486
|
+
configTokens: buildThemeCss(config2.theme),
|
|
19339
19487
|
sources: ["../../**/*.{astro,jsx,svelte,ts,tsx,vue}"],
|
|
19340
19488
|
userCss: userExamplesCss
|
|
19341
19489
|
}),
|
|
@@ -19343,7 +19491,7 @@ var eject = async (root) => {
|
|
|
19343
19491
|
},
|
|
19344
19492
|
{
|
|
19345
19493
|
content: tailwindEntryTemplate({
|
|
19346
|
-
configTokens: buildThemeCss(
|
|
19494
|
+
configTokens: buildThemeCss(config2.theme),
|
|
19347
19495
|
sources: [
|
|
19348
19496
|
blumeSourceGlob(root, genDir),
|
|
19349
19497
|
"../../**/*.{astro,mdx,ts,tsx}"
|
|
@@ -19380,9 +19528,9 @@ var eject = async (root) => {
|
|
|
19380
19528
|
if (askEnabled) {
|
|
19381
19529
|
files.push(...await askFiles(project, srcDir, genDir));
|
|
19382
19530
|
}
|
|
19383
|
-
if (
|
|
19531
|
+
if (config2.seo.og.enabled) {
|
|
19384
19532
|
files.push({
|
|
19385
|
-
content: ogEndpointTemplate(customOgRoutes(pages,
|
|
19533
|
+
content: ogEndpointTemplate(customOgRoutes(pages, config2.title, config2.seo.og.titles)),
|
|
19386
19534
|
path: join33(srcDir, "pages", "og", "[...slug].png.ts")
|
|
19387
19535
|
});
|
|
19388
19536
|
}
|
|
@@ -19399,10 +19547,10 @@ var eject = async (root) => {
|
|
|
19399
19547
|
});
|
|
19400
19548
|
}
|
|
19401
19549
|
files.push({
|
|
19402
|
-
content: searchClientTemplate(
|
|
19550
|
+
content: searchClientTemplate(config2),
|
|
19403
19551
|
path: join33(genDir, "search-client.ts")
|
|
19404
19552
|
});
|
|
19405
|
-
if (servesStaticIndex(
|
|
19553
|
+
if (servesStaticIndex(config2.search.provider)) {
|
|
19406
19554
|
const documents = await buildSearchDocuments(project);
|
|
19407
19555
|
files.push({
|
|
19408
19556
|
content: `${JSON.stringify(documents)}
|
|
@@ -19413,9 +19561,9 @@ var eject = async (root) => {
|
|
|
19413
19561
|
path: join33(srcDir, "pages", "blume-search.json.ts")
|
|
19414
19562
|
});
|
|
19415
19563
|
}
|
|
19416
|
-
if (
|
|
19564
|
+
if (config2.search.provider === "mixedbread") {
|
|
19417
19565
|
files.push({
|
|
19418
|
-
content: mixedbreadSearchEndpointTemplate(
|
|
19566
|
+
content: mixedbreadSearchEndpointTemplate(config2.search.mixedbread?.storeId ?? ""),
|
|
19419
19567
|
path: join33(srcDir, "pages", "api", "search.ts")
|
|
19420
19568
|
});
|
|
19421
19569
|
}
|
|
@@ -19432,9 +19580,9 @@ var eject = async (root) => {
|
|
|
19432
19580
|
});
|
|
19433
19581
|
}
|
|
19434
19582
|
const warnings = [];
|
|
19435
|
-
if (hasScalarReferences(
|
|
19583
|
+
if (hasScalarReferences(config2)) {
|
|
19436
19584
|
const references = await buildReferenceFiles({
|
|
19437
|
-
config,
|
|
19585
|
+
config: config2,
|
|
19438
19586
|
contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
|
|
19439
19587
|
root
|
|
19440
19588
|
});
|
|
@@ -19452,7 +19600,7 @@ var eject = async (root) => {
|
|
|
19452
19600
|
})), ...examples.examples.map((example) => ({
|
|
19453
19601
|
content: exampleWrapperTemplate(example),
|
|
19454
19602
|
path: join33(genDir, "examples", `${exampleSlug(example.path)}.astro`)
|
|
19455
|
-
})), ...examplesPreviewFiles(srcDir,
|
|
19603
|
+
})), ...examplesPreviewFiles(srcDir, config2.basePath, examples.examples.length > 0));
|
|
19456
19604
|
for (const [entryId, content] of staged) {
|
|
19457
19605
|
files.push({ content, path: join33(root, stagedDir, entryId) });
|
|
19458
19606
|
}
|
|
@@ -19474,27 +19622,27 @@ var eject = async (root) => {
|
|
|
19474
19622
|
// src/cli/eject-scripts.ts
|
|
19475
19623
|
import { readFile as readFile19, writeFile as writeFile9 } from "node:fs/promises";
|
|
19476
19624
|
import { join as join34 } from "pathe";
|
|
19477
|
-
var droppedArtifactNotices = (
|
|
19625
|
+
var droppedArtifactNotices = (config2) => {
|
|
19478
19626
|
const notices = [];
|
|
19479
|
-
if (
|
|
19627
|
+
if (config2.search.provider === "pagefind") {
|
|
19480
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).');
|
|
19481
19629
|
}
|
|
19482
|
-
if (searchProviderMeta(
|
|
19483
|
-
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.`);
|
|
19484
19632
|
}
|
|
19485
|
-
if (
|
|
19633
|
+
if (config2.ai.llmsTxt.enabled) {
|
|
19486
19634
|
notices.push("llms.txt and llms-full.txt");
|
|
19487
19635
|
}
|
|
19488
|
-
if (
|
|
19636
|
+
if (config2.deployment.site && config2.seo.sitemap) {
|
|
19489
19637
|
notices.push("sitemap.xml — recreate it with the @astrojs/sitemap integration.");
|
|
19490
19638
|
}
|
|
19491
|
-
if (
|
|
19639
|
+
if (config2.seo.robots) {
|
|
19492
19640
|
notices.push("robots.txt — recreate it as a public/robots.txt file.");
|
|
19493
19641
|
}
|
|
19494
|
-
if (
|
|
19642
|
+
if (config2.seo.agentReadability) {
|
|
19495
19643
|
notices.push("agent-readability.json");
|
|
19496
19644
|
}
|
|
19497
|
-
if (
|
|
19645
|
+
if (config2.redirects.length > 0 && config2.deployment.output === "static") {
|
|
19498
19646
|
notices.push("the platform redirect files (_redirects, vercel.json) — your redirects still work as Astro-generated meta-refresh pages.");
|
|
19499
19647
|
}
|
|
19500
19648
|
return notices;
|
|
@@ -19521,6 +19669,7 @@ var updatePackageScripts = async (root) => {
|
|
|
19521
19669
|
// src/cli/init/scaffold.ts
|
|
19522
19670
|
import { existsSync as existsSync21 } from "node:fs";
|
|
19523
19671
|
import { mkdir as mkdir10, writeFile as writeFile10 } from "node:fs/promises";
|
|
19672
|
+
import { detect } from "package-manager-detector/detect";
|
|
19524
19673
|
import { basename as basename4, dirname as dirname14, isAbsolute as isAbsolute9, join as join35, relative as relative16 } from "pathe";
|
|
19525
19674
|
|
|
19526
19675
|
// src/core/package-json.ts
|
|
@@ -19649,6 +19798,11 @@ var detectPackageManager = (userAgent) => {
|
|
|
19649
19798
|
const name = userAgent?.split("/")[0];
|
|
19650
19799
|
return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : "npm";
|
|
19651
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
|
+
};
|
|
19652
19806
|
var validateContentDir = (root, dir) => isAbsolute9(dir) || relative16(root, join35(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
|
|
19653
19807
|
var titleize = (raw) => {
|
|
19654
19808
|
const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
|
|
@@ -19793,8 +19947,8 @@ var ejectCommand = defineCommand7({
|
|
|
19793
19947
|
refuseIfDevRunning(root, "ejecting");
|
|
19794
19948
|
let notices = [];
|
|
19795
19949
|
try {
|
|
19796
|
-
const { config } = await loadConfig(root);
|
|
19797
|
-
notices = droppedArtifactNotices(
|
|
19950
|
+
const { config: config2 } = await loadConfig(root);
|
|
19951
|
+
notices = droppedArtifactNotices(config2);
|
|
19798
19952
|
} catch {}
|
|
19799
19953
|
if (!args.yes) {
|
|
19800
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.");
|
|
@@ -19813,7 +19967,7 @@ var ejectCommand = defineCommand7({
|
|
|
19813
19967
|
`);
|
|
19814
19968
|
}
|
|
19815
19969
|
reportDroppedArtifacts(notices);
|
|
19816
|
-
const pm =
|
|
19970
|
+
const pm = await detectProjectPackageManager(root);
|
|
19817
19971
|
const { build: build2, dev: dev2 } = commandsFor(pm);
|
|
19818
19972
|
logger.box(`Your project is now a standalone Astro app.
|
|
19819
19973
|
|
|
@@ -19898,6 +20052,20 @@ import { mkdtemp as mkdtemp2, writeFile as writeFile11 } from "node:fs/promises"
|
|
|
19898
20052
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
19899
20053
|
import { colors as colors4 } from "consola/utils";
|
|
19900
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
|
|
19901
20069
|
var GLYPH2 = {
|
|
19902
20070
|
error: "!",
|
|
19903
20071
|
fail: "✖",
|
|
@@ -19911,16 +20079,6 @@ var STATUS_COLOR = {
|
|
|
19911
20079
|
skip: colors4.dim
|
|
19912
20080
|
};
|
|
19913
20081
|
var ID_PAD = 28;
|
|
19914
|
-
var seconds = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
19915
|
-
var money = (cost) => cost === undefined ? "" : `$${cost.toFixed(2)}`;
|
|
19916
|
-
var duration = (ms) => {
|
|
19917
|
-
if (ms < 60000) {
|
|
19918
|
-
return seconds(ms);
|
|
19919
|
-
}
|
|
19920
|
-
const minutes = Math.floor(ms / 60000);
|
|
19921
|
-
const rest = Math.round(ms % 60000 / 1000);
|
|
19922
|
-
return `${minutes}m ${rest}s`;
|
|
19923
|
-
};
|
|
19924
20082
|
var questionLine = (result) => {
|
|
19925
20083
|
const color = STATUS_COLOR[result.status];
|
|
19926
20084
|
const glyph = color(GLYPH2[result.status]);
|
|
@@ -20005,8 +20163,8 @@ import { tmpdir as tmpdir3 } from "node:os";
|
|
|
20005
20163
|
import { join as join38 } from "pathe";
|
|
20006
20164
|
|
|
20007
20165
|
// src/eval/agents.ts
|
|
20008
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
20009
20166
|
import { readFile as readFile20, writeFile as writeFile12 } from "node:fs/promises";
|
|
20167
|
+
import spawn2 from "cross-spawn";
|
|
20010
20168
|
import { join as join37 } from "pathe";
|
|
20011
20169
|
import { z as z3 } from "zod";
|
|
20012
20170
|
var KILL_GRACE_MS = 5000;
|
|
@@ -20030,19 +20188,17 @@ var DISALLOWED_TOOLS = [
|
|
|
20030
20188
|
"Task"
|
|
20031
20189
|
];
|
|
20032
20190
|
var runAgentHeadless = (bin, args, options) => new Promise((resolve11, reject) => {
|
|
20033
|
-
const platform = options.platform ?? process.platform;
|
|
20034
20191
|
const child = spawn2(bin, args, {
|
|
20035
20192
|
cwd: options.cwd,
|
|
20036
|
-
shell: platform === "win32",
|
|
20037
20193
|
stdio: ["pipe", "pipe", "pipe"]
|
|
20038
20194
|
});
|
|
20039
20195
|
let stdout = "";
|
|
20040
20196
|
let stderr = "";
|
|
20041
20197
|
let timedOut = false;
|
|
20042
|
-
child.stdout
|
|
20198
|
+
child.stdout?.on("data", (chunk) => {
|
|
20043
20199
|
stdout += chunk.toString("utf-8");
|
|
20044
20200
|
});
|
|
20045
|
-
child.stderr
|
|
20201
|
+
child.stderr?.on("data", (chunk) => {
|
|
20046
20202
|
stderr += chunk.toString("utf-8");
|
|
20047
20203
|
});
|
|
20048
20204
|
const deadline = setTimeout(() => {
|
|
@@ -20066,7 +20222,7 @@ var runAgentHeadless = (bin, args, options) => new Promise((resolve11, reject) =
|
|
|
20066
20222
|
resolve11({ code: code ?? 1, stderr, stdout, timedOut });
|
|
20067
20223
|
}
|
|
20068
20224
|
});
|
|
20069
|
-
child.stdin
|
|
20225
|
+
child.stdin?.end(options.prompt);
|
|
20070
20226
|
});
|
|
20071
20227
|
var writeMcpConfig = async (dir, snapshotPath, launcher) => {
|
|
20072
20228
|
const resolved = launcher ?? {
|
|
@@ -20074,12 +20230,12 @@ var writeMcpConfig = async (dir, snapshotPath, launcher) => {
|
|
|
20074
20230
|
command: process.execPath
|
|
20075
20231
|
};
|
|
20076
20232
|
const configPath = join37(dir, "mcp-config.json");
|
|
20077
|
-
const
|
|
20233
|
+
const config2 = {
|
|
20078
20234
|
mcpServers: {
|
|
20079
20235
|
[MCP_SERVER_NAME]: { args: resolved.args, command: resolved.command }
|
|
20080
20236
|
}
|
|
20081
20237
|
};
|
|
20082
|
-
await writeFile12(configPath, JSON.stringify(
|
|
20238
|
+
await writeFile12(configPath, JSON.stringify(config2, null, 2));
|
|
20083
20239
|
return {
|
|
20084
20240
|
configPath,
|
|
20085
20241
|
serverArgs: resolved.args,
|
|
@@ -20181,17 +20337,17 @@ var readAgentOutput = async (kind, result, lastMessagePath) => {
|
|
|
20181
20337
|
text: payload.data.result
|
|
20182
20338
|
};
|
|
20183
20339
|
}
|
|
20184
|
-
let
|
|
20340
|
+
let text;
|
|
20185
20341
|
try {
|
|
20186
20342
|
const raw = await readFile20(lastMessagePath, "utf-8");
|
|
20187
|
-
|
|
20343
|
+
text = raw.trim();
|
|
20188
20344
|
} catch {
|
|
20189
20345
|
return { detail: "no last message written", isError: true, text: "" };
|
|
20190
20346
|
}
|
|
20191
|
-
if (
|
|
20347
|
+
if (text === "") {
|
|
20192
20348
|
return { detail: "empty last message", isError: true, text: "" };
|
|
20193
20349
|
}
|
|
20194
|
-
return { isError: false, text
|
|
20350
|
+
return { isError: false, text };
|
|
20195
20351
|
};
|
|
20196
20352
|
var verdictSchema = z3.object({
|
|
20197
20353
|
missing: z3.array(z3.string()).default([]),
|
|
@@ -20199,15 +20355,15 @@ var verdictSchema = z3.object({
|
|
|
20199
20355
|
pass: z3.boolean(),
|
|
20200
20356
|
score: z3.number().min(0).max(1).optional()
|
|
20201
20357
|
});
|
|
20202
|
-
var parseVerdict = (
|
|
20203
|
-
const start =
|
|
20204
|
-
const end =
|
|
20358
|
+
var parseVerdict = (text) => {
|
|
20359
|
+
const start = text.indexOf("{");
|
|
20360
|
+
const end = text.lastIndexOf("}");
|
|
20205
20361
|
if (start === -1 || end <= start) {
|
|
20206
20362
|
return;
|
|
20207
20363
|
}
|
|
20208
20364
|
let parsed;
|
|
20209
20365
|
try {
|
|
20210
|
-
parsed = JSON.parse(
|
|
20366
|
+
parsed = JSON.parse(text.slice(start, end + 1));
|
|
20211
20367
|
} catch {
|
|
20212
20368
|
return;
|
|
20213
20369
|
}
|
|
@@ -20486,21 +20642,21 @@ var runEval = async (options) => {
|
|
|
20486
20642
|
var DEFAULT_FILE = "evals.yaml";
|
|
20487
20643
|
var DEFAULT_TIMEOUT_S = 180;
|
|
20488
20644
|
var isAgentKind = (value) => (value in AGENTS);
|
|
20489
|
-
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) => {
|
|
20490
20651
|
try {
|
|
20491
|
-
return await launchAgent(bin, prompt);
|
|
20652
|
+
return await launchAgent(AGENTS[agent].bin, prompt);
|
|
20492
20653
|
} catch (error) {
|
|
20493
20654
|
if (error?.code !== "ENOENT") {
|
|
20494
20655
|
throw error;
|
|
20495
20656
|
}
|
|
20496
|
-
return
|
|
20657
|
+
return notInstalled(agent);
|
|
20497
20658
|
}
|
|
20498
20659
|
};
|
|
20499
|
-
var notInstalled = (agent) => {
|
|
20500
|
-
const cli = AGENTS[agent];
|
|
20501
|
-
logger.error(`${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`);
|
|
20502
|
-
return process.exit(1);
|
|
20503
|
-
};
|
|
20504
20660
|
var passFraction = (result) => {
|
|
20505
20661
|
const ran = result.results.length - result.counts.skip;
|
|
20506
20662
|
return ran === 0 ? 1 : result.counts.pass / ran;
|
|
@@ -20540,10 +20696,7 @@ var runFixHandoff = async (agent, result, root, threshold) => {
|
|
|
20540
20696
|
process.stderr.write(` Handing ${count} failed question${count === 1 ? "" : "s"} to ${cli.name}…
|
|
20541
20697
|
|
|
20542
20698
|
`);
|
|
20543
|
-
const code = await launchAgentCode2(
|
|
20544
|
-
if (code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
20545
|
-
notInstalled(agent);
|
|
20546
|
-
}
|
|
20699
|
+
const code = await launchAgentCode2(agent, evalFixPrompt(report));
|
|
20547
20700
|
if (code !== 0) {
|
|
20548
20701
|
process.exit(code);
|
|
20549
20702
|
}
|
|
@@ -20554,10 +20707,7 @@ var runInit = async (agent, file) => {
|
|
|
20554
20707
|
logger.error(`${file} already exists — edit it directly, or pass --file to draft elsewhere.`);
|
|
20555
20708
|
process.exit(1);
|
|
20556
20709
|
}
|
|
20557
|
-
const code = await launchAgentCode2(
|
|
20558
|
-
if (code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
20559
|
-
notInstalled(agent);
|
|
20560
|
-
}
|
|
20710
|
+
const code = await launchAgentCode2(agent, initPrompt(file));
|
|
20561
20711
|
if (code !== 0) {
|
|
20562
20712
|
process.exit(code);
|
|
20563
20713
|
}
|
|
@@ -20915,6 +21065,7 @@ import {
|
|
|
20915
21065
|
CallToolRequestSchema,
|
|
20916
21066
|
ListToolsRequestSchema
|
|
20917
21067
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
21068
|
+
import { z as z5 } from "zod";
|
|
20918
21069
|
|
|
20919
21070
|
// src/search/orama-index.ts
|
|
20920
21071
|
import { create, insertMultiple, search } from "@orama/orama";
|
|
@@ -20932,6 +21083,7 @@ var BOOST = { description: 2, title: 3 };
|
|
|
20932
21083
|
var SEGMENTED_LANGUAGES = new Set(["ja", "ko", "th", "zh"]);
|
|
20933
21084
|
var BIGRAM_LANGUAGES = new Set(["ja", "zh"]);
|
|
20934
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;
|
|
20935
21087
|
var addBigrams = (run, tokens) => {
|
|
20936
21088
|
const characters = [...run];
|
|
20937
21089
|
if (characters.length === 1) {
|
|
@@ -20968,17 +21120,30 @@ var segmentingTokenizer = (locale) => {
|
|
|
20968
21120
|
run = "";
|
|
20969
21121
|
}
|
|
20970
21122
|
};
|
|
20971
|
-
|
|
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())) {
|
|
20972
21132
|
if (!segment.isWordLike) {
|
|
20973
21133
|
flush();
|
|
20974
21134
|
continue;
|
|
20975
21135
|
}
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
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();
|
|
20979
21146
|
}
|
|
20980
|
-
flush();
|
|
20981
|
-
tokens.add(segment.segment);
|
|
20982
21147
|
}
|
|
20983
21148
|
flush();
|
|
20984
21149
|
return [...tokens];
|
|
@@ -21019,78 +21184,54 @@ var queryOramaIndex = async (db, term, limit, filters) => {
|
|
|
21019
21184
|
var DEFAULT_SEARCH_LIMIT = 8;
|
|
21020
21185
|
var MAX_SEARCH_LIMIT = 20;
|
|
21021
21186
|
var EXCERPT_LENGTH = 200;
|
|
21022
|
-
var
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
|
|
21027
|
-
|
|
21028
|
-
|
|
21029
|
-
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.',
|
|
21030
|
-
type: "object"
|
|
21031
|
-
};
|
|
21032
|
-
var INPUT_SCHEMAS = {
|
|
21033
|
-
get_navigation: { properties: {}, type: "object" },
|
|
21034
|
-
get_page: {
|
|
21035
|
-
properties: {
|
|
21036
|
-
route: {
|
|
21037
|
-
description: "The page route, e.g. `/guides/install`.",
|
|
21038
|
-
type: "string"
|
|
21039
|
-
}
|
|
21040
|
-
},
|
|
21041
|
-
required: ["route"],
|
|
21042
|
-
type: "object"
|
|
21043
|
-
},
|
|
21044
|
-
list_pages: {
|
|
21045
|
-
properties: {
|
|
21046
|
-
contentTypes: CONTENT_TYPES_SCHEMA,
|
|
21047
|
-
filters: FILTERS_SCHEMA
|
|
21048
|
-
},
|
|
21049
|
-
type: "object"
|
|
21050
|
-
},
|
|
21051
|
-
search_docs: {
|
|
21052
|
-
properties: {
|
|
21053
|
-
contentTypes: CONTENT_TYPES_SCHEMA,
|
|
21054
|
-
filters: FILTERS_SCHEMA,
|
|
21055
|
-
limit: {
|
|
21056
|
-
description: `Maximum hits to return (default ${DEFAULT_SEARCH_LIMIT}).`,
|
|
21057
|
-
maximum: MAX_SEARCH_LIMIT,
|
|
21058
|
-
minimum: 1,
|
|
21059
|
-
type: "integer"
|
|
21060
|
-
},
|
|
21061
|
-
query: { description: "The search query.", type: "string" }
|
|
21062
|
-
},
|
|
21063
|
-
required: ["query"],
|
|
21064
|
-
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;
|
|
21065
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;
|
|
21066
21226
|
};
|
|
21067
21227
|
var TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
|
|
21068
21228
|
annotations: tool.annotations,
|
|
21069
21229
|
description: tool.description,
|
|
21070
|
-
inputSchema:
|
|
21230
|
+
inputSchema: inputSchemaFor(TOOL_INPUTS[tool.name]),
|
|
21071
21231
|
name: tool.name,
|
|
21072
21232
|
title: tool.title
|
|
21073
21233
|
}));
|
|
21074
|
-
var asString2 = (value) => typeof value === "string" ? value : "";
|
|
21075
|
-
var asContentTypes = (value) => {
|
|
21076
|
-
const list2 = Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [value].filter((entry) => typeof entry === "string");
|
|
21077
|
-
return list2.length > 0 ? list2 : undefined;
|
|
21078
|
-
};
|
|
21079
|
-
var asFacetFilters = (value) => {
|
|
21080
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
21081
|
-
return;
|
|
21082
|
-
}
|
|
21083
|
-
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
21084
|
-
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
21085
|
-
};
|
|
21086
21234
|
var matchesFacets = (facets, filters) => Object.entries(filters).every(([key, value]) => facets?.[key] === value);
|
|
21087
|
-
var asLimit = (value) => {
|
|
21088
|
-
const num = typeof value === "number" ? value : Number(value);
|
|
21089
|
-
if (!Number.isFinite(num)) {
|
|
21090
|
-
return DEFAULT_SEARCH_LIMIT;
|
|
21091
|
-
}
|
|
21092
|
-
return Math.min(Math.max(Math.trunc(num), 1), MAX_SEARCH_LIMIT);
|
|
21093
|
-
};
|
|
21094
21235
|
var normalizeRoute2 = (input, data) => {
|
|
21095
21236
|
let value = input.trim();
|
|
21096
21237
|
if (/^https?:\/\//iu.test(value)) {
|
|
@@ -21101,15 +21242,12 @@ var normalizeRoute2 = (input, data) => {
|
|
|
21101
21242
|
try {
|
|
21102
21243
|
value = decodeURI(value);
|
|
21103
21244
|
} catch {}
|
|
21104
|
-
const
|
|
21105
|
-
|
|
21106
|
-
const withSlash = noSuffix.startsWith("/") ? noSuffix : `/${noSuffix}`;
|
|
21107
|
-
const based = stripBasePath(data.base, withSlash);
|
|
21108
|
-
return based === "" ? "/" : based;
|
|
21245
|
+
const noSuffix = trimEnd(value, "/").replace(/\.mdx?$/u, "");
|
|
21246
|
+
return stripBasePath(data.base, normalizeRoute(noSuffix));
|
|
21109
21247
|
};
|
|
21110
21248
|
var urlFor = (route, data) => {
|
|
21111
21249
|
const path = withBasePath(data.base, route);
|
|
21112
|
-
return data.site ?
|
|
21250
|
+
return data.site ? absoluteUrl(data.site, path) : path;
|
|
21113
21251
|
};
|
|
21114
21252
|
var excerptFor = (doc) => {
|
|
21115
21253
|
if (doc.description) {
|
|
@@ -21118,7 +21256,7 @@ var excerptFor = (doc) => {
|
|
|
21118
21256
|
const head = doc.content.slice(0, EXCERPT_LENGTH).trim();
|
|
21119
21257
|
return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;
|
|
21120
21258
|
};
|
|
21121
|
-
var
|
|
21259
|
+
var text = (value, isError = false) => ({
|
|
21122
21260
|
content: [{ text: value, type: "text" }],
|
|
21123
21261
|
...isError ? { isError: true } : {}
|
|
21124
21262
|
});
|
|
@@ -21140,10 +21278,11 @@ var buildServer = (data, index) => {
|
|
|
21140
21278
|
server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
21141
21279
|
const { arguments: args = {}, name } = request2.params;
|
|
21142
21280
|
if (name === "search_docs") {
|
|
21281
|
+
const input = TOOL_INPUTS.search_docs.parse(args);
|
|
21143
21282
|
const db = await index();
|
|
21144
|
-
const hits = await queryOramaIndex(db,
|
|
21145
|
-
contentTypes:
|
|
21146
|
-
facets:
|
|
21283
|
+
const hits = await queryOramaIndex(db, input.query, input.limit ?? DEFAULT_SEARCH_LIMIT, {
|
|
21284
|
+
contentTypes: input.contentTypes,
|
|
21285
|
+
facets: input.filters
|
|
21147
21286
|
});
|
|
21148
21287
|
const results = hits.map((doc) => ({
|
|
21149
21288
|
contentType: doc.contentType,
|
|
@@ -21153,21 +21292,21 @@ var buildServer = (data, index) => {
|
|
|
21153
21292
|
title: doc.title,
|
|
21154
21293
|
url: urlFor(doc.route, data)
|
|
21155
21294
|
}));
|
|
21156
|
-
return
|
|
21295
|
+
return text(JSON.stringify(results, null, 2));
|
|
21157
21296
|
}
|
|
21158
21297
|
if (name === "get_page") {
|
|
21159
|
-
const
|
|
21298
|
+
const input = TOOL_INPUTS.get_page.parse(args);
|
|
21299
|
+
const key = normalizeRoute2(input.route, data);
|
|
21160
21300
|
const markdown = data.pages[key];
|
|
21161
21301
|
if (markdown === undefined) {
|
|
21162
|
-
return
|
|
21302
|
+
return text(`No page found at "${key}". Use list_pages or search_docs to find valid routes.`, true);
|
|
21163
21303
|
}
|
|
21164
|
-
return
|
|
21304
|
+
return text(markdown);
|
|
21165
21305
|
}
|
|
21166
21306
|
if (name === "list_pages") {
|
|
21167
|
-
const contentTypes =
|
|
21168
|
-
const filters = asFacetFilters(args.filters);
|
|
21307
|
+
const { contentTypes, filters } = TOOL_INPUTS.list_pages.parse(args);
|
|
21169
21308
|
const routes = data.routes.filter((route) => (!contentTypes || contentTypes.includes(route.contentType)) && (!filters || matchesFacets(route.facets, filters)));
|
|
21170
|
-
return
|
|
21309
|
+
return text(JSON.stringify(routes.map((route) => ({
|
|
21171
21310
|
contentType: route.contentType,
|
|
21172
21311
|
description: route.description,
|
|
21173
21312
|
facets: route.facets,
|
|
@@ -21178,9 +21317,9 @@ var buildServer = (data, index) => {
|
|
|
21178
21317
|
})), null, 2));
|
|
21179
21318
|
}
|
|
21180
21319
|
if (name === "get_navigation") {
|
|
21181
|
-
return
|
|
21320
|
+
return text(JSON.stringify(data.navigation, null, 2));
|
|
21182
21321
|
}
|
|
21183
|
-
return
|
|
21322
|
+
return text(`Unknown tool: ${name}`, true);
|
|
21184
21323
|
});
|
|
21185
21324
|
return server;
|
|
21186
21325
|
};
|
|
@@ -21239,8 +21378,8 @@ var previewCommand = defineCommand11({
|
|
|
21239
21378
|
},
|
|
21240
21379
|
async run({ args }) {
|
|
21241
21380
|
const root = process.cwd();
|
|
21242
|
-
const { config } = await loadConfig(root);
|
|
21243
|
-
const context = resolveProjectContext(root,
|
|
21381
|
+
const { config: config2 } = await loadConfig(root);
|
|
21382
|
+
const context = resolveProjectContext(root, config2);
|
|
21244
21383
|
if (!existsSync23(join40(context.outDir, "astro.config.mjs"))) {
|
|
21245
21384
|
logger.error("No build found. Run `blume build` first.");
|
|
21246
21385
|
process.exit(1);
|
|
@@ -21279,8 +21418,8 @@ var syncCommand = defineCommand12({
|
|
|
21279
21418
|
async run({ args }) {
|
|
21280
21419
|
const root = process.cwd();
|
|
21281
21420
|
if (args.force) {
|
|
21282
|
-
const { config } = await loadConfig(root);
|
|
21283
|
-
const context = resolveProjectContext(root,
|
|
21421
|
+
const { config: config2 } = await loadConfig(root);
|
|
21422
|
+
const context = resolveProjectContext(root, config2);
|
|
21284
21423
|
await rm4(join41(context.outDir, "cache"), { force: true, recursive: true });
|
|
21285
21424
|
logger.info("Cleared source cache.");
|
|
21286
21425
|
}
|
|
@@ -21327,20 +21466,20 @@ var codexArgs2 = (lastMessagePath) => [
|
|
|
21327
21466
|
var translateAgentArgs = (kind, lastMessagePath) => kind === "claude" ? claudeArgs2() : codexArgs2(lastMessagePath);
|
|
21328
21467
|
|
|
21329
21468
|
// src/translate/ledger.ts
|
|
21330
|
-
import { createHash as
|
|
21469
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
21331
21470
|
import { readFile as readFile23 } from "node:fs/promises";
|
|
21332
21471
|
import { join as join42 } from "pathe";
|
|
21333
|
-
import { z as
|
|
21472
|
+
import { z as z6 } from "zod";
|
|
21334
21473
|
var LEDGER_FILE = "blume.translations.json";
|
|
21335
|
-
var ledgerSchema =
|
|
21336
|
-
files:
|
|
21337
|
-
version:
|
|
21474
|
+
var ledgerSchema = z6.object({
|
|
21475
|
+
files: z6.record(z6.string(), z6.record(z6.string(), z6.string())),
|
|
21476
|
+
version: z6.literal(1)
|
|
21338
21477
|
});
|
|
21339
21478
|
var emptyLedger = () => ({
|
|
21340
21479
|
files: {},
|
|
21341
21480
|
version: 1
|
|
21342
21481
|
});
|
|
21343
|
-
var hashSource = (
|
|
21482
|
+
var hashSource = (text2) => createHash5("sha256").update(text2).digest("hex").slice(0, 16);
|
|
21344
21483
|
var readLedger = async (root) => {
|
|
21345
21484
|
let raw;
|
|
21346
21485
|
try {
|
|
@@ -21427,16 +21566,6 @@ var SPINNER_FRAMES = [
|
|
|
21427
21566
|
];
|
|
21428
21567
|
var SPINNER_INTERVAL_MS = 80;
|
|
21429
21568
|
var REWRITE = `\r${ESC}[K`;
|
|
21430
|
-
var seconds2 = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
21431
|
-
var money2 = (cost) => cost === undefined ? "" : `$${cost.toFixed(2)}`;
|
|
21432
|
-
var duration2 = (ms) => {
|
|
21433
|
-
if (ms < 60000) {
|
|
21434
|
-
return seconds2(ms);
|
|
21435
|
-
}
|
|
21436
|
-
const minutes = Math.floor(ms / 60000);
|
|
21437
|
-
const rest = Math.round(ms % 60000 / 1000);
|
|
21438
|
-
return `${minutes}m ${rest}s`;
|
|
21439
|
-
};
|
|
21440
21569
|
var itemLabel = (item) => item.kind === "page" ? `${item.sourceRel} → ${item.locale}` : `meta title${item.entries.length === 1 ? "" : "s"} (${item.entries.length}) → ${item.locale}`;
|
|
21441
21570
|
var spinnerLine = (active, done, total, frame) => {
|
|
21442
21571
|
const first = active[0];
|
|
@@ -21448,7 +21577,7 @@ var itemEndLine = (result) => {
|
|
|
21448
21577
|
const glyph = color(GLYPH3[result.status]);
|
|
21449
21578
|
const label = itemLabel(result.item);
|
|
21450
21579
|
if (result.status === "translated") {
|
|
21451
|
-
const cells = [
|
|
21580
|
+
const cells = [seconds(result.durationMs), money(result.costUsd)].filter((cell) => cell !== "").join(" ");
|
|
21452
21581
|
return ` ${glyph} ${label} ${colors5.dim(cells)}`;
|
|
21453
21582
|
}
|
|
21454
21583
|
const word = result.status === "partial" ? "partial" : "failed";
|
|
@@ -21463,8 +21592,8 @@ var translateSummaryLine = (result, workList) => {
|
|
|
21463
21592
|
counts.partial > 0 ? `${counts.partial} partial` : "",
|
|
21464
21593
|
workList.untracked.length > 0 ? `${workList.untracked.length} adopted` : "",
|
|
21465
21594
|
workList.upToDate > 0 ? `${workList.upToDate} already up to date` : "",
|
|
21466
|
-
|
|
21467
|
-
|
|
21595
|
+
duration(result.durationMs),
|
|
21596
|
+
money(result.costUsd)
|
|
21468
21597
|
].filter((part) => part !== "");
|
|
21469
21598
|
return parts.join(" · ");
|
|
21470
21599
|
};
|
|
@@ -21605,6 +21734,8 @@ var createProgressRenderer = (options) => {
|
|
|
21605
21734
|
import { existsSync as existsSync24 } from "node:fs";
|
|
21606
21735
|
import { mkdtemp as mkdtemp4, readFile as readFile25 } from "node:fs/promises";
|
|
21607
21736
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
21737
|
+
import pLimit2 from "p-limit";
|
|
21738
|
+
import pMap7 from "p-map";
|
|
21608
21739
|
import { join as join44 } from "pathe";
|
|
21609
21740
|
|
|
21610
21741
|
// src/translate/meta.ts
|
|
@@ -21725,12 +21856,12 @@ ${JSON.stringify(titles, null, 2)}`;
|
|
|
21725
21856
|
// src/translate/validate.ts
|
|
21726
21857
|
var FRONTMATTER_OPEN = /^---\r?\n/u;
|
|
21727
21858
|
var FENCE_LINE = /^\s*(?:```|~~~)/u;
|
|
21728
|
-
var stripOuterFence = (
|
|
21729
|
-
const trimmed =
|
|
21859
|
+
var stripOuterFence = (text2) => {
|
|
21860
|
+
const trimmed = text2.trim();
|
|
21730
21861
|
const match = trimmed.match(/^(?<fence>`{3,}|~{3,})[^\n]*\n(?<inner>[\s\S]*?)\n\k<fence>\s*$/u);
|
|
21731
21862
|
return match?.groups?.inner ?? trimmed;
|
|
21732
21863
|
};
|
|
21733
|
-
var countFenceLines = (
|
|
21864
|
+
var countFenceLines = (text2) => text2.split(`
|
|
21734
21865
|
`).filter((line) => FENCE_LINE.test(line)).length;
|
|
21735
21866
|
var getPath2 = (data, path) => {
|
|
21736
21867
|
let value = data;
|
|
@@ -21749,8 +21880,8 @@ var setPath = (data, path, value) => {
|
|
|
21749
21880
|
}
|
|
21750
21881
|
parent[path.at(-1)] = value;
|
|
21751
21882
|
};
|
|
21752
|
-
var ensureTrailingNewline = (
|
|
21753
|
-
`) ?
|
|
21883
|
+
var ensureTrailingNewline = (text2) => text2.endsWith(`
|
|
21884
|
+
`) ? text2 : `${text2}
|
|
21754
21885
|
`;
|
|
21755
21886
|
var validateTranslation = (sourceText, agentText) => {
|
|
21756
21887
|
const candidate = stripOuterFence(agentText);
|
|
@@ -21829,11 +21960,6 @@ var metaDirKey = (dir) => dir === "" ? "." : dir;
|
|
|
21829
21960
|
var invokeAgent = async (context, prompt, index) => {
|
|
21830
21961
|
const messagePath = join44(context.dir, `message-${index}.txt`);
|
|
21831
21962
|
const result = await context.run(context.bin, translateAgentArgs(context.kind, messagePath), { cwd: context.dir, prompt, timeoutMs: context.timeoutMs });
|
|
21832
|
-
if (!result.timedOut && result.code === WINDOWS_COMMAND_NOT_FOUND) {
|
|
21833
|
-
const missing = new Error(`${context.bin} was not found on PATH`);
|
|
21834
|
-
missing.code = "ENOENT";
|
|
21835
|
-
throw missing;
|
|
21836
|
-
}
|
|
21837
21963
|
return await readAgentOutput(context.kind, result, messagePath);
|
|
21838
21964
|
};
|
|
21839
21965
|
var runPageItem = async (item, index, context, ledger) => {
|
|
@@ -21922,37 +22048,26 @@ var runTranslate = async (options) => {
|
|
|
21922
22048
|
timeoutMs: options.timeoutMs ?? DEFAULT_TRANSLATE_TIMEOUT_MS
|
|
21923
22049
|
};
|
|
21924
22050
|
const { items } = options.workList;
|
|
21925
|
-
const results = Array.from({ length: items.length });
|
|
21926
22051
|
const concurrency = Math.max(1, Math.min(options.concurrency ?? 1, items.length || 1));
|
|
21927
|
-
|
|
21928
|
-
const persist = () =>
|
|
21929
|
-
|
|
21930
|
-
|
|
21931
|
-
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
21935
|
-
|
|
21936
|
-
|
|
21937
|
-
|
|
21938
|
-
|
|
21939
|
-
|
|
21940
|
-
|
|
21941
|
-
|
|
21942
|
-
|
|
21943
|
-
|
|
21944
|
-
|
|
21945
|
-
|
|
21946
|
-
await persist();
|
|
21947
|
-
options.onProgress?.({
|
|
21948
|
-
index,
|
|
21949
|
-
kind: "item-end",
|
|
21950
|
-
result,
|
|
21951
|
-
total: items.length
|
|
21952
|
-
});
|
|
21953
|
-
}
|
|
21954
|
-
};
|
|
21955
|
-
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 });
|
|
21956
22071
|
const diagnostics = [];
|
|
21957
22072
|
for (const result of results) {
|
|
21958
22073
|
if (result.status !== "translated") {
|
|
@@ -22106,7 +22221,7 @@ var computeWorkList = async (project, ledger, options = {}) => {
|
|
|
22106
22221
|
|
|
22107
22222
|
// src/cli/commands/translate.ts
|
|
22108
22223
|
var DEFAULT_TIMEOUT_S2 = DEFAULT_TRANSLATE_TIMEOUT_MS / 1000;
|
|
22109
|
-
var
|
|
22224
|
+
var DEFAULT_CONCURRENCY2 = 4;
|
|
22110
22225
|
var MAX_CONCURRENCY = 16;
|
|
22111
22226
|
var parseFlags2 = (args) => {
|
|
22112
22227
|
const agents = Object.keys(AGENTS).filter((kind) => args[kind]);
|
|
@@ -22127,7 +22242,7 @@ var parseFlags2 = (args) => {
|
|
|
22127
22242
|
logger.error(`Invalid --timeout "${args.timeout}" (whole seconds).`);
|
|
22128
22243
|
process.exit(1);
|
|
22129
22244
|
}
|
|
22130
|
-
const concurrency = args.concurrency === undefined ?
|
|
22245
|
+
const concurrency = args.concurrency === undefined ? DEFAULT_CONCURRENCY2 : Number(args.concurrency);
|
|
22131
22246
|
if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency > MAX_CONCURRENCY) {
|
|
22132
22247
|
logger.error(`Invalid --concurrency "${args.concurrency}" (use 1..${MAX_CONCURRENCY}).`);
|
|
22133
22248
|
process.exit(1);
|
|
@@ -22174,7 +22289,7 @@ var translateCommand = defineCommand13({
|
|
|
22174
22289
|
type: "boolean"
|
|
22175
22290
|
},
|
|
22176
22291
|
concurrency: {
|
|
22177
|
-
description: `Parallel agent sessions. Defaults to ${
|
|
22292
|
+
description: `Parallel agent sessions. Defaults to ${DEFAULT_CONCURRENCY2}, max ${MAX_CONCURRENCY}.`,
|
|
22178
22293
|
type: "string"
|
|
22179
22294
|
},
|
|
22180
22295
|
force: {
|
|
@@ -22593,5 +22708,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
22593
22708
|
});
|
|
22594
22709
|
runMain(main);
|
|
22595
22710
|
|
|
22596
|
-
//# debugId=
|
|
22711
|
+
//# debugId=07BF7082AA0A5EEC64756E2164756E21
|
|
22597
22712
|
//# sourceMappingURL=index.js.map
|