blume 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +618 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/cli/index.js +1487 -360
- package/dist/cli/index.js.map +91 -85
- package/dist/types/ai/component-markdown.d.ts +34 -0
- package/dist/types/components/content/youtube.d.ts +18 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +36 -2
- package/dist/types/core/config.d.ts +3 -2
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +476 -132
- package/dist/types/core/schema.d.ts +216 -145
- package/dist/types/index.d.ts +1 -0
- package/dist/types/openapi/references.d.ts +60 -0
- package/docs/01-quickstart.mdx +5 -2
- package/docs/02-deployment.mdx +8 -8
- package/docs/03-faq.mdx +46 -16
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/configuration/ai.mdx +49 -10
- package/docs/configuration/customization.mdx +11 -0
- package/docs/configuration/index.mdx +27 -3
- package/docs/configuration/seo.mdx +2 -2
- package/docs/content/components.mdx +1 -1
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/navigation.mdx +3 -3
- package/docs/content/sources.mdx +1 -1
- package/docs/content/syntax.mdx +4 -2
- package/docs/index.mdx +2 -2
- package/docs/reference/cli.mdx +8 -6
- package/package.json +14 -4
- package/skills/blume/SKILL.md +5 -3
- package/skills/blume-update-docs/SKILL.md +3 -2
- package/src/ai/agent-readability.ts +9 -8
- package/src/ai/ask-context.ts +7 -2
- package/src/ai/ask-data.ts +3 -0
- package/src/ai/component-markdown.ts +461 -0
- package/src/ai/llms.ts +135 -26
- package/src/ai/markdown.ts +35 -6
- package/src/ai/mcp/data.ts +25 -4
- package/src/ai/mcp/discovery.ts +10 -3
- package/src/ai/mcp/server.ts +21 -7
- package/src/ai/visibility.ts +74 -0
- package/src/astro/component-slots.ts +11 -1
- package/src/astro/generate.ts +76 -45
- package/src/astro/integration.ts +1 -1
- package/src/astro/markdown-negotiation.ts +1 -1
- package/src/astro/pages.ts +81 -19
- package/src/astro/templates.ts +99 -12
- package/src/blume-modules.d.ts +8 -0
- package/src/cli/commands/build.ts +99 -19
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/dev.ts +26 -5
- package/src/cli/commands/eject.ts +47 -19
- package/src/cli/commands/init.ts +120 -180
- package/src/cli/commands/preview.ts +4 -1
- package/src/cli/commands/validate.ts +43 -2
- package/src/cli/dev-lock.ts +8 -4
- package/src/cli/eject-scripts.ts +72 -0
- package/src/cli/env.ts +15 -5
- package/src/cli/init/questions.ts +158 -0
- package/src/cli/init/scaffold.ts +380 -0
- package/src/components/content/AccordionItem.astro +23 -4
- package/src/components/content/Badge.astro +3 -1
- package/src/components/content/Card.astro +4 -2
- package/src/components/content/Step.astro +10 -1
- package/src/components/content/Tabs.astro +15 -3
- package/src/components/content/Tile.astro +2 -1
- package/src/components/content/Tooltip.astro +3 -1
- package/src/components/content/Update.astro +9 -2
- package/src/components/content/auto-type-table.ts +7 -1
- package/src/components/content/base-href.ts +33 -0
- package/src/components/content/changelog-element.ts +9 -2
- package/src/components/content/mermaid-element.ts +7 -2
- package/src/components/islands/AskAI.astro +5 -2
- package/src/components/islands/ask-ai.tsx +56 -6
- package/src/components/islands/hooks.ts +28 -8
- package/src/components/layout/Banner.astro +10 -2
- package/src/components/layout/Header.astro +13 -4
- package/src/components/layout/Logo.astro +11 -3
- package/src/components/layout/NavTree.astro +17 -3
- package/src/components/layout/PageActions.astro +25 -10
- package/src/components/layout/PageLayout.astro +45 -8
- package/src/components/layout/ReferenceLayout.astro +8 -1
- package/src/components/layout/RootLayout.astro +67 -9
- package/src/components/layout/Search.astro +94 -22
- package/src/components/layout/search/algolia.ts +11 -2
- package/src/components/layout/search/endpoint.ts +11 -5
- package/src/components/layout/search/orama-cloud.ts +8 -2
- package/src/components/layout/search/types.ts +5 -1
- package/src/components/layout/search/typesense.ts +4 -1
- package/src/components/layout/toc-element.ts +1 -1
- package/src/components/openapi/ApiTagOperations.astro +2 -1
- package/src/components/openapi/Operation.astro +47 -40
- package/src/components/openapi/RequestPanel.astro +1 -1
- package/src/components/openapi/helpers.ts +71 -3
- package/src/components/openapi/panel.ts +1 -1
- package/src/core/base-path.ts +24 -0
- package/src/core/builtin-tags.ts +2 -0
- package/src/core/config-input.ts +37 -2
- package/src/core/config.ts +3 -2
- package/src/core/data.ts +2 -0
- package/src/core/graph.ts +15 -5
- package/src/core/i18n-ui.ts +45 -0
- package/src/core/last-modified.ts +13 -6
- package/src/core/links.ts +32 -8
- package/src/core/navigation.ts +29 -4
- package/src/core/package-json.ts +17 -2
- package/src/core/project-graph.ts +15 -6
- package/src/core/schema.ts +36 -2
- package/src/core/sources/assets.ts +6 -1
- package/src/core/sources/filesystem.ts +4 -0
- package/src/core/sources/mdx-remote.ts +23 -14
- package/src/core/sources/normalize.ts +152 -50
- package/src/core/sources/notion.ts +8 -8
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +2 -1
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/deploy/adapter-output.ts +18 -8
- package/src/deploy/redirects.ts +7 -2
- package/src/deploy/sitemap.ts +53 -11
- package/src/index.ts +5 -0
- package/src/markdown/base-links.ts +10 -8
- package/src/markdown/index.ts +15 -3
- package/src/markdown/inline-code.ts +7 -2
- package/src/markdown/package-commands.ts +10 -4
- package/src/openapi/model.ts +12 -4
- package/src/openapi/parse.ts +21 -0
- package/src/openapi/references.ts +38 -8
- package/src/openapi/source.ts +59 -10
- package/src/registry/eject.ts +184 -12
- package/src/registry/registry.ts +0 -3
- package/src/search/documents.ts +34 -2
- package/src/seo/jsonld.ts +13 -12
package/dist/cli/index.js
CHANGED
|
@@ -48,7 +48,6 @@ import { dirname as dirname3, join as join4 } from "pathe";
|
|
|
48
48
|
// src/registry/registry.ts
|
|
49
49
|
import { join as join3 } from "pathe";
|
|
50
50
|
var packageSrc = join3(packageRoot(), "src");
|
|
51
|
-
var itemsRoot = join3(packageRoot(), "src", "registry", "items");
|
|
52
51
|
var layoutComponent = (config) => {
|
|
53
52
|
const target = `components/blume/${config.file}`;
|
|
54
53
|
return {
|
|
@@ -663,14 +662,17 @@ var buildAgentReadability = (project) => {
|
|
|
663
662
|
}
|
|
664
663
|
const site = config.deployment.site ?? null;
|
|
665
664
|
const deployBase = normalizeBasePath(config.deployment.base);
|
|
666
|
-
const abs = (path) =>
|
|
665
|
+
const abs = (path) => {
|
|
666
|
+
const based = withBasePath(deployBase, path);
|
|
667
|
+
return site ? `${site.replace(/\/+$/u, "")}${based}` : based;
|
|
668
|
+
};
|
|
667
669
|
const artifacts = {
|
|
668
670
|
markdown: {
|
|
669
671
|
contentNegotiation: "text/markdown",
|
|
670
672
|
pattern: abs("/{route}.md")
|
|
671
673
|
}
|
|
672
674
|
};
|
|
673
|
-
if (config.ai.llmsTxt) {
|
|
675
|
+
if (config.ai.llmsTxt.enabled) {
|
|
674
676
|
artifacts.llmsFullTxt = abs("/llms-full.txt");
|
|
675
677
|
artifacts.llmsTxt = abs("/llms.txt");
|
|
676
678
|
}
|
|
@@ -744,37 +746,376 @@ var readEntryText = async (ctx, page) => {
|
|
|
744
746
|
return "";
|
|
745
747
|
};
|
|
746
748
|
|
|
749
|
+
// src/ai/component-markdown.ts
|
|
750
|
+
import { mdxToMdast } from "satteri";
|
|
751
|
+
|
|
752
|
+
// src/components/content/youtube.ts
|
|
753
|
+
var BARE_ID = /^[\w-]{11}$/u;
|
|
754
|
+
var URL_ID = /(?:youtu\.be\/|\/embed\/|\/shorts\/|\/live\/|[?&]v=)(?<id>[\w-]{11})/u;
|
|
755
|
+
var parseYouTubeId = (input) => {
|
|
756
|
+
const value = input.trim();
|
|
757
|
+
if (!value) {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
if (BARE_ID.test(value)) {
|
|
761
|
+
return value;
|
|
762
|
+
}
|
|
763
|
+
return URL_ID.exec(value)?.groups?.id ?? null;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
// src/ai/component-markdown.ts
|
|
767
|
+
var evaluateExpression = (raw) => {
|
|
768
|
+
try {
|
|
769
|
+
const value = new Function(`"use strict"; return (${raw});`)();
|
|
770
|
+
return { ok: true, value };
|
|
771
|
+
} catch {
|
|
772
|
+
return { ok: false, value: undefined };
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
var readProps = (node) => {
|
|
776
|
+
const props = {};
|
|
777
|
+
let lossy = false;
|
|
778
|
+
for (const attribute of node.attributes ?? []) {
|
|
779
|
+
if (attribute.type !== "mdxJsxAttribute" || !attribute.name) {
|
|
780
|
+
lossy = true;
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
if (attribute.value === null || attribute.value === undefined) {
|
|
784
|
+
props[attribute.name] = true;
|
|
785
|
+
} else if (typeof attribute.value === "string") {
|
|
786
|
+
props[attribute.name] = attribute.value;
|
|
787
|
+
} else {
|
|
788
|
+
const result = evaluateExpression(attribute.value.value);
|
|
789
|
+
if (result.ok) {
|
|
790
|
+
props[attribute.name] = result.value;
|
|
791
|
+
} else {
|
|
792
|
+
lossy = true;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return { lossy, props };
|
|
797
|
+
};
|
|
798
|
+
var hasOffsets = (node) => typeof node.position?.start?.offset === "number" && typeof node.position?.end?.offset === "number";
|
|
799
|
+
var applySplices = (text, splices) => {
|
|
800
|
+
let result = text;
|
|
801
|
+
for (const splice of [...splices].toSorted((a, b) => b.start - a.start)) {
|
|
802
|
+
const lineStart = result.lastIndexOf(`
|
|
803
|
+
`, splice.start - 1) + 1;
|
|
804
|
+
const prefix = result.slice(lineStart, splice.start);
|
|
805
|
+
const indent = /^[\t ]+$/u.test(prefix) ? prefix : "";
|
|
806
|
+
const replacement = indent ? splice.text.split(`
|
|
807
|
+
`).map((line, index) => index === 0 || line === "" ? line : `${indent}${line}`).join(`
|
|
808
|
+
`) : splice.text;
|
|
809
|
+
result = result.slice(0, splice.start) + replacement + result.slice(splice.end);
|
|
810
|
+
}
|
|
811
|
+
return result;
|
|
812
|
+
};
|
|
813
|
+
var dedent = (text) => {
|
|
814
|
+
const lines = text.split(`
|
|
815
|
+
`);
|
|
816
|
+
const rest = lines.slice(1).filter((line) => line.trim() !== "");
|
|
817
|
+
if (rest.length === 0) {
|
|
818
|
+
return text;
|
|
819
|
+
}
|
|
820
|
+
const indent = Math.min(...rest.map((line) => line.length - line.trimStart().length));
|
|
821
|
+
if (indent === 0) {
|
|
822
|
+
return text;
|
|
823
|
+
}
|
|
824
|
+
return [
|
|
825
|
+
lines[0],
|
|
826
|
+
...lines.slice(1).map((line) => line.trim() === "" ? "" : line.slice(indent))
|
|
827
|
+
].join(`
|
|
828
|
+
`);
|
|
829
|
+
};
|
|
830
|
+
var isJsxElement = (node) => node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement";
|
|
831
|
+
var cellText = (value) => String(value ?? "").replaceAll(/\s*\n\s*/gu, " ").replaceAll("|", "\\|").trim();
|
|
832
|
+
var cellCode = (value) => {
|
|
833
|
+
const text = cellText(value);
|
|
834
|
+
return text && !text.includes("`") ? `\`${text}\`` : text;
|
|
835
|
+
};
|
|
836
|
+
var typeTable = ({ children, props }) => {
|
|
837
|
+
const { type } = props;
|
|
838
|
+
if (type === null || typeof type !== "object") {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
const entries = Object.entries(type);
|
|
842
|
+
const rows = entries.map(([name, info]) => {
|
|
843
|
+
const prop = cellCode(`${name}${info.required ? "" : "?"}`);
|
|
844
|
+
const typeCell = info.typeDescriptionLink ? `[${cellCode(info.type)}](${cellText(info.typeDescriptionLink)})` : cellCode(info.type);
|
|
845
|
+
const defaultCell = info.default === undefined ? "-" : cellCode(info.default);
|
|
846
|
+
const description = cellText([info.description, info.typeDescription].filter((part) => typeof part === "string" && part !== "").join(" "));
|
|
847
|
+
return `| ${prop} | ${typeCell} | ${defaultCell} | ${description} |`;
|
|
848
|
+
});
|
|
849
|
+
const table = rows.length > 0 ? [
|
|
850
|
+
"| Prop | Type | Default | Description |",
|
|
851
|
+
"| --- | --- | --- | --- |",
|
|
852
|
+
...rows
|
|
853
|
+
].join(`
|
|
854
|
+
`) : "";
|
|
855
|
+
return [table, children].filter(Boolean).join(`
|
|
856
|
+
|
|
857
|
+
`);
|
|
858
|
+
};
|
|
859
|
+
var callout = ({ children, props }) => {
|
|
860
|
+
const type = typeof props.type === "string" ? props.type : "info";
|
|
861
|
+
const label = typeof props.title === "string" && props.title !== "" ? props.title : type.charAt(0).toUpperCase() + type.slice(1);
|
|
862
|
+
if (!children) {
|
|
863
|
+
return `> **${label}**`;
|
|
864
|
+
}
|
|
865
|
+
const body = children.split(`
|
|
866
|
+
`).map((line) => line.trim() === "" ? ">" : `> ${line}`).join(`
|
|
867
|
+
`);
|
|
868
|
+
return `> **${label}**
|
|
869
|
+
>
|
|
870
|
+
${body}`;
|
|
871
|
+
};
|
|
872
|
+
var listItem = (index, content) => {
|
|
873
|
+
const marker = `${index}. `;
|
|
874
|
+
return content.split(`
|
|
875
|
+
`).map((line, lineIndex) => {
|
|
876
|
+
if (lineIndex === 0) {
|
|
877
|
+
return `${marker}${line}`;
|
|
878
|
+
}
|
|
879
|
+
return line === "" ? "" : ` ${line}`;
|
|
880
|
+
}).join(`
|
|
881
|
+
`);
|
|
882
|
+
};
|
|
883
|
+
var steps = ({ childComponents, children }) => {
|
|
884
|
+
const items = childComponents("Step");
|
|
885
|
+
if (items.length === 0) {
|
|
886
|
+
return children;
|
|
887
|
+
}
|
|
888
|
+
return items.map((step, index) => {
|
|
889
|
+
const title = typeof step.props.title === "string" && step.props.title !== "" ? `**${step.props.title}**` : "";
|
|
890
|
+
const content = [title, step.children].filter(Boolean).join(`
|
|
891
|
+
|
|
892
|
+
`);
|
|
893
|
+
return listItem(index + 1, content);
|
|
894
|
+
}).join(`
|
|
895
|
+
|
|
896
|
+
`);
|
|
897
|
+
};
|
|
898
|
+
var tabs = ({ childComponents, children }) => {
|
|
899
|
+
const items = childComponents("Tab");
|
|
900
|
+
if (items.length === 0) {
|
|
901
|
+
return children;
|
|
902
|
+
}
|
|
903
|
+
return items.map((tab, index) => {
|
|
904
|
+
const title = typeof tab.props.title === "string" && tab.props.title !== "" ? tab.props.title : `Tab ${index + 1}`;
|
|
905
|
+
return tab.children ? `**${title}**
|
|
906
|
+
|
|
907
|
+
${tab.children}` : `**${title}**`;
|
|
908
|
+
}).join(`
|
|
909
|
+
|
|
910
|
+
`);
|
|
911
|
+
};
|
|
912
|
+
var youtube = ({ props }) => {
|
|
913
|
+
let input = "";
|
|
914
|
+
if (typeof props.id === "string") {
|
|
915
|
+
input = props.id;
|
|
916
|
+
} else if (typeof props.url === "string") {
|
|
917
|
+
input = props.url;
|
|
918
|
+
}
|
|
919
|
+
const videoId = parseYouTubeId(input);
|
|
920
|
+
if (!videoId) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
const start = typeof props.start === "number" && props.start > 0 ? `&t=${Math.floor(props.start)}s` : "";
|
|
924
|
+
const title = typeof props.title === "string" && props.title !== "" ? props.title : "Watch on YouTube";
|
|
925
|
+
return `[${title}](https://www.youtube.com/watch?v=${videoId}${start})`;
|
|
926
|
+
};
|
|
927
|
+
var SERIALIZERS = {
|
|
928
|
+
Callout: callout,
|
|
929
|
+
Steps: steps,
|
|
930
|
+
Tabs: tabs,
|
|
931
|
+
TypeTable: typeTable,
|
|
932
|
+
YouTube: youtube
|
|
933
|
+
};
|
|
934
|
+
var escapeRegExp2 = (value) => value.replaceAll(/[$()*+.?[\\\]^{|}]/gu, String.raw`\$&`);
|
|
935
|
+
var componentHint = (registry2) => new RegExp(`<(?:${Object.keys(registry2).map(escapeRegExp2).join("|")})[\\s/>]`, "u");
|
|
936
|
+
var BUILT_IN_HINT = componentHint(SERIALIZERS);
|
|
937
|
+
var renderChildren = (walk, node) => {
|
|
938
|
+
const children = (node.children ?? []).filter(hasOffsets);
|
|
939
|
+
const [first] = children;
|
|
940
|
+
if (!first) {
|
|
941
|
+
return "";
|
|
942
|
+
}
|
|
943
|
+
const start = first.position.start.offset;
|
|
944
|
+
const end = children.at(-1)?.position.end.offset ?? start;
|
|
945
|
+
const splices = [];
|
|
946
|
+
collectSplices(walk, children, splices);
|
|
947
|
+
const spliced = applySplices(walk.source.slice(start, end), splices.map((splice) => ({
|
|
948
|
+
...splice,
|
|
949
|
+
end: splice.end - start,
|
|
950
|
+
start: splice.start - start
|
|
951
|
+
})));
|
|
952
|
+
return dedent(spliced).trim();
|
|
953
|
+
};
|
|
954
|
+
var serializeElement = (serializer, walk, node) => serializer({
|
|
955
|
+
...readProps(node),
|
|
956
|
+
childComponents: (name) => (node.children ?? []).filter((child) => isJsxElement(child) && child.name === name).map((child) => ({
|
|
957
|
+
...readProps(child),
|
|
958
|
+
children: renderChildren(walk, child)
|
|
959
|
+
})),
|
|
960
|
+
children: renderChildren(walk, node)
|
|
961
|
+
});
|
|
962
|
+
var collectSplices = (walk, nodes, out) => {
|
|
963
|
+
for (const node of nodes) {
|
|
964
|
+
const serializer = node.type === "mdxJsxFlowElement" && node.name ? walk.registry[node.name] : undefined;
|
|
965
|
+
if (serializer && hasOffsets(node)) {
|
|
966
|
+
const text = serializeElement(serializer, walk, node);
|
|
967
|
+
if (text !== null) {
|
|
968
|
+
out.push({
|
|
969
|
+
end: node.position.end.offset,
|
|
970
|
+
start: node.position.start.offset,
|
|
971
|
+
text
|
|
972
|
+
});
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
collectSplices(walk, node.children ?? [], out);
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
var downlevelComponents = (source, components) => {
|
|
980
|
+
const custom = components && Object.keys(components).length > 0;
|
|
981
|
+
const registry2 = custom ? { ...SERIALIZERS, ...components } : SERIALIZERS;
|
|
982
|
+
const hint = custom ? componentHint(registry2) : BUILT_IN_HINT;
|
|
983
|
+
if (!hint.test(source)) {
|
|
984
|
+
return source;
|
|
985
|
+
}
|
|
986
|
+
let tree;
|
|
987
|
+
try {
|
|
988
|
+
tree = mdxToMdast(source);
|
|
989
|
+
} catch {
|
|
990
|
+
return source;
|
|
991
|
+
}
|
|
992
|
+
const splices = [];
|
|
993
|
+
collectSplices({ registry: registry2, source }, tree.children ?? [], splices);
|
|
994
|
+
return splices.length > 0 ? applySplices(source, splices) : source;
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
// src/ai/visibility.ts
|
|
998
|
+
var CODE_FENCE_BLOCK = /^(?<fence>`{3,}|~{3,})[^\n]*\n[\s\S]*?^\k<fence>[^\n]*(?=\n|$)/gmu;
|
|
999
|
+
var FENCE_TOKEN = /\u0000blume-fence-(?<index>\d+)\u0000/gu;
|
|
1000
|
+
var visibilityBlock = (audience) => new RegExp(`<Visibility\\s+for\\s*=\\s*(?:"${audience}"|'${audience}')\\s*>(?<inner>[\\s\\S]*?)</Visibility\\s*>`, "gu");
|
|
1001
|
+
var BLOCKS = {
|
|
1002
|
+
agents: visibilityBlock("agents"),
|
|
1003
|
+
web: visibilityBlock("web")
|
|
1004
|
+
};
|
|
1005
|
+
var applyAudienceVisibility = (markdown, audience) => {
|
|
1006
|
+
const fences = [];
|
|
1007
|
+
const masked = markdown.replace(CODE_FENCE_BLOCK, (block) => {
|
|
1008
|
+
fences.push(block);
|
|
1009
|
+
return `\x00blume-fence-${fences.length - 1}\x00`;
|
|
1010
|
+
});
|
|
1011
|
+
let touched = false;
|
|
1012
|
+
const filtered = masked.replaceAll(BLOCKS[audience === "agents" ? "web" : "agents"], () => {
|
|
1013
|
+
touched = true;
|
|
1014
|
+
return "";
|
|
1015
|
+
}).replaceAll(BLOCKS[audience], (_match, inner) => {
|
|
1016
|
+
touched = true;
|
|
1017
|
+
return inner;
|
|
1018
|
+
});
|
|
1019
|
+
const tidied = touched ? filtered.replaceAll(/\n{3,}/gu, `
|
|
1020
|
+
|
|
1021
|
+
`) : filtered;
|
|
1022
|
+
return tidied.replaceAll(FENCE_TOKEN, (token, index) => fences[Number(index)] ?? token);
|
|
1023
|
+
};
|
|
1024
|
+
var applyAgentVisibility = (markdown) => applyAudienceVisibility(markdown, "agents");
|
|
1025
|
+
|
|
747
1026
|
// src/ai/llms.ts
|
|
748
1027
|
var pageUrl = (route, site, base = "") => {
|
|
749
|
-
|
|
750
|
-
|
|
1028
|
+
const path = withBasePath(base, route);
|
|
1029
|
+
return encodeURI(site ? `${site.replace(/\/$/u, "")}${path}` : path);
|
|
1030
|
+
};
|
|
1031
|
+
var eligiblePages = (project) => project.graph.pages.filter((page) => !(page.meta.draft || page.meta.sidebar.hidden || page.meta.seo.noindex) && (project.config.ai.llmsTxt.openapi || page.source.name !== "openapi"));
|
|
1032
|
+
var indexedNavigations = (project) => {
|
|
1033
|
+
const { i18n } = project.config;
|
|
1034
|
+
if (i18n) {
|
|
1035
|
+
return i18n.locales.flatMap(({ code, label }) => {
|
|
1036
|
+
const nav = project.graph.navigationByLocale[code];
|
|
1037
|
+
if (!nav) {
|
|
1038
|
+
return [];
|
|
1039
|
+
}
|
|
1040
|
+
return [{ label: code === i18n.defaultLocale ? undefined : label, nav }];
|
|
1041
|
+
});
|
|
751
1042
|
}
|
|
752
|
-
return
|
|
1043
|
+
return [{ nav: project.graph.navigation }];
|
|
753
1044
|
};
|
|
754
|
-
var orderedPages = (project) => [...project.graph.pages].filter((page) => !page.meta.draft).sort((a, b) => a.route.localeCompare(b.route));
|
|
755
1045
|
var buildIndex = (project) => {
|
|
756
1046
|
const { config } = project;
|
|
757
1047
|
const { site } = config.deployment;
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
const url = pageUrl(page.route, site, normalizeBasePath(config.deployment.base));
|
|
1048
|
+
const base = normalizeBasePath(config.deployment.base);
|
|
1049
|
+
const eligible = eligiblePages(project);
|
|
1050
|
+
const byRoute = new Map(eligible.map((page) => [page.route, page]));
|
|
1051
|
+
const seen = new Set;
|
|
1052
|
+
const line = (page) => {
|
|
1053
|
+
seen.add(page.route);
|
|
765
1054
|
const summary = page.description ? `: ${page.description}` : "";
|
|
766
|
-
|
|
1055
|
+
return `- [${page.title}](${pageUrl(page.route, site, base)})${summary}`;
|
|
1056
|
+
};
|
|
1057
|
+
const renderLevel = (nodes, depth) => {
|
|
1058
|
+
const list = [];
|
|
1059
|
+
const groupBlocks = [];
|
|
1060
|
+
for (const node of nodes) {
|
|
1061
|
+
if (node.kind === "page") {
|
|
1062
|
+
const page = byRoute.get(node.route);
|
|
1063
|
+
if (page && !seen.has(page.route)) {
|
|
1064
|
+
list.push(line(page));
|
|
1065
|
+
}
|
|
1066
|
+
continue;
|
|
1067
|
+
}
|
|
1068
|
+
const rootPage = node.route ? byRoute.get(node.route) : undefined;
|
|
1069
|
+
const blocks2 = renderLevel(node.children, depth + 1);
|
|
1070
|
+
if (rootPage && !seen.has(rootPage.route)) {
|
|
1071
|
+
blocks2.unshift(line(rootPage));
|
|
1072
|
+
}
|
|
1073
|
+
if (blocks2.length > 0) {
|
|
1074
|
+
groupBlocks.push(`${"#".repeat(Math.min(depth, 6))} ${node.label}`, ...blocks2);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
return list.length > 0 ? [list.join(`
|
|
1078
|
+
`), ...groupBlocks] : groupBlocks;
|
|
1079
|
+
};
|
|
1080
|
+
const renderNav = (nav, depth) => {
|
|
1081
|
+
const loose = nav.sidebar.filter((node) => node.kind === "page");
|
|
1082
|
+
const groups = nav.sidebar.filter((node) => node.kind === "group");
|
|
1083
|
+
const looseBlocks = renderLevel(loose, depth + 1);
|
|
1084
|
+
return [
|
|
1085
|
+
...looseBlocks.length > 0 ? [`${"#".repeat(depth)} Docs`, ...looseBlocks] : [],
|
|
1086
|
+
...renderLevel(groups, depth)
|
|
1087
|
+
];
|
|
1088
|
+
};
|
|
1089
|
+
const blocks = [];
|
|
1090
|
+
for (const { label, nav } of indexedNavigations(project)) {
|
|
1091
|
+
if (label) {
|
|
1092
|
+
const localized = renderNav(nav, 3);
|
|
1093
|
+
if (localized.length > 0) {
|
|
1094
|
+
blocks.push(`## ${label}`, ...localized);
|
|
1095
|
+
}
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
blocks.push(...renderNav(nav, 2));
|
|
767
1099
|
}
|
|
768
|
-
|
|
1100
|
+
const leftover = eligible.filter((page) => !seen.has(page.route)).toSorted((a, b) => a.route.localeCompare(b.route));
|
|
1101
|
+
if (leftover.length > 0) {
|
|
1102
|
+
blocks.push(blocks.length > 0 ? "## Other" : "## Docs", leftover.map(line).join(`
|
|
1103
|
+
`));
|
|
1104
|
+
}
|
|
1105
|
+
const header = config.description ? `# ${config.title}
|
|
1106
|
+
|
|
1107
|
+
> ${config.description}` : `# ${config.title}`;
|
|
1108
|
+
return `${[header, ...blocks].join(`
|
|
1109
|
+
|
|
769
1110
|
`)}
|
|
770
1111
|
`;
|
|
771
1112
|
};
|
|
772
1113
|
var buildFull = async (project) => {
|
|
773
1114
|
const { config } = project;
|
|
774
|
-
const pages =
|
|
1115
|
+
const pages = eligiblePages(project).toSorted((a, b) => a.route.localeCompare(b.route));
|
|
775
1116
|
const sections = await Promise.all(pages.map(async (page) => {
|
|
776
1117
|
const raw = await readEntryText(project, page);
|
|
777
|
-
const body = frontmatter_default(raw).content.trim();
|
|
1118
|
+
const body = downlevelComponents(applyAgentVisibility(frontmatter_default(raw).content), config.ai.markdownComponents).trim();
|
|
778
1119
|
const url = pageUrl(page.route, config.deployment.site, normalizeBasePath(config.deployment.base));
|
|
779
1120
|
return [`# ${page.title}`, `Source: ${url}`, "", body].join(`
|
|
780
1121
|
`);
|
|
@@ -897,7 +1238,7 @@ import { existsSync as existsSync4 } from "node:fs";
|
|
|
897
1238
|
import { cp, mkdir as mkdir2, rm } from "node:fs/promises";
|
|
898
1239
|
import { dirname as dirname4, join as join6 } from "pathe";
|
|
899
1240
|
var ADAPTER_OUTPUT_PATHS = {
|
|
900
|
-
netlify: ".netlify",
|
|
1241
|
+
netlify: ".netlify/v1",
|
|
901
1242
|
vercel: ".vercel/output"
|
|
902
1243
|
};
|
|
903
1244
|
var deployStaticDir = (config, context) => {
|
|
@@ -905,7 +1246,11 @@ var deployStaticDir = (config, context) => {
|
|
|
905
1246
|
if (output === "server" && adapter === "vercel") {
|
|
906
1247
|
return join6(context.root, ".vercel", "output", "static");
|
|
907
1248
|
}
|
|
908
|
-
|
|
1249
|
+
const dist = context.distDir ?? join6(context.root, "dist");
|
|
1250
|
+
if (output === "server" && adapter === "node") {
|
|
1251
|
+
return join6(dist, "client");
|
|
1252
|
+
}
|
|
1253
|
+
return dist;
|
|
909
1254
|
};
|
|
910
1255
|
var surfaceAdapterOutput = async (config, context) => {
|
|
911
1256
|
const { adapter, output } = config.deployment;
|
|
@@ -940,8 +1285,8 @@ var buildNetlifyRedirects = (redirects) => `${redirects.map((redirect) => `${red
|
|
|
940
1285
|
var buildVercelConfig = (redirects) => `${JSON.stringify({
|
|
941
1286
|
redirects: redirects.map((redirect) => ({
|
|
942
1287
|
destination: redirect.to,
|
|
943
|
-
|
|
944
|
-
|
|
1288
|
+
source: redirect.from,
|
|
1289
|
+
statusCode: redirect.status
|
|
945
1290
|
}))
|
|
946
1291
|
}, null, 2)}
|
|
947
1292
|
`;
|
|
@@ -986,7 +1331,71 @@ var buildRobots = (project) => {
|
|
|
986
1331
|
`;
|
|
987
1332
|
};
|
|
988
1333
|
|
|
1334
|
+
// src/astro/pages.ts
|
|
1335
|
+
import { extname, relative as relative4 } from "pathe";
|
|
1336
|
+
import { glob, globSync } from "tinyglobby";
|
|
1337
|
+
var PAGE_GLOB = ["**/*.astro"];
|
|
1338
|
+
var toPageRoutes = (pagesRoot, files) => {
|
|
1339
|
+
files.sort();
|
|
1340
|
+
return files.map((file) => {
|
|
1341
|
+
const rel = relative4(pagesRoot, file);
|
|
1342
|
+
const withoutExt = rel.slice(0, rel.length - extname(rel).length);
|
|
1343
|
+
const parts = withoutExt.split("/");
|
|
1344
|
+
if (parts.at(-1) === "index") {
|
|
1345
|
+
parts.pop();
|
|
1346
|
+
}
|
|
1347
|
+
const pattern = parts.length === 0 ? "/" : `/${parts.join("/")}`;
|
|
1348
|
+
return { entrypoint: file, pattern };
|
|
1349
|
+
});
|
|
1350
|
+
};
|
|
1351
|
+
var discoverPages = async (pagesRoot) => toPageRoutes(pagesRoot, await glob(PAGE_GLOB, { absolute: true, cwd: pagesRoot, onlyFiles: true }));
|
|
1352
|
+
var discoverPagesSync = (pagesRoot) => toPageRoutes(pagesRoot, globSync(PAGE_GLOB, { absolute: true, cwd: pagesRoot, onlyFiles: true }));
|
|
1353
|
+
var routeIsTaken = (pages, contentPages, route) => pages.some((page) => page.pattern === route) || contentPages.some((page) => page.route === route);
|
|
1354
|
+
var PRIVATE_SEGMENT = /^[._]/u;
|
|
1355
|
+
var staticSegments = (pattern) => {
|
|
1356
|
+
const segments = pattern.split("/").filter(Boolean);
|
|
1357
|
+
return segments.some((part) => PRIVATE_SEGMENT.test(part) || part.includes("[")) ? null : segments;
|
|
1358
|
+
};
|
|
1359
|
+
var customStaticRoutes = (pages) => {
|
|
1360
|
+
const routes = new Set;
|
|
1361
|
+
for (const { pattern } of pages) {
|
|
1362
|
+
const segments = staticSegments(pattern);
|
|
1363
|
+
if (segments !== null) {
|
|
1364
|
+
routes.add(segments.length === 0 ? "/" : `/${segments.join("/")}`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
return [...routes];
|
|
1368
|
+
};
|
|
1369
|
+
var hasGeneratedChangelog = (project, userPages) => {
|
|
1370
|
+
const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
|
|
1371
|
+
const hasChangelogSource = (project.config.content.sources ?? []).some((source) => source.type === "github-releases");
|
|
1372
|
+
return (hasChangelog || hasChangelogSource) && !routeIsTaken(userPages, project.graph.pages, "/changelog");
|
|
1373
|
+
};
|
|
1374
|
+
var humanizeSegment = (segment) => segment.split(/[-_]/u).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1375
|
+
var customOgRoutes = (pages, siteTitle) => {
|
|
1376
|
+
const seen = new Set;
|
|
1377
|
+
const routes = [];
|
|
1378
|
+
const collectRoute = (pattern) => {
|
|
1379
|
+
const segments = staticSegments(pattern);
|
|
1380
|
+
if (segments === null) {
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
const slug = segments.length === 0 ? "index" : segments.join("/");
|
|
1384
|
+
if (seen.has(slug)) {
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
seen.add(slug);
|
|
1388
|
+
const last = segments.at(-1);
|
|
1389
|
+
routes.push({ slug, title: last ? humanizeSegment(last) : siteTitle });
|
|
1390
|
+
};
|
|
1391
|
+
for (const { pattern } of pages) {
|
|
1392
|
+
collectRoute(pattern);
|
|
1393
|
+
}
|
|
1394
|
+
return routes;
|
|
1395
|
+
};
|
|
1396
|
+
|
|
989
1397
|
// src/deploy/sitemap.ts
|
|
1398
|
+
var ERROR_ROUTES = new Set(["/404", "/500"]);
|
|
990
1399
|
var lastmodTag = (value) => {
|
|
991
1400
|
if (!value) {
|
|
992
1401
|
return "";
|
|
@@ -1001,13 +1410,29 @@ var buildSitemap = (project) => {
|
|
|
1001
1410
|
}
|
|
1002
1411
|
const base = site.replace(/\/$/u, "");
|
|
1003
1412
|
const deployBase = normalizeBasePath(project.config.deployment.base);
|
|
1413
|
+
const seen = new Set;
|
|
1004
1414
|
const urls = [];
|
|
1415
|
+
const pushUrl = (route, lastModified) => {
|
|
1416
|
+
const loc = escapeXml(encodeURI(`${base}${route}`));
|
|
1417
|
+
if (seen.has(loc)) {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
seen.add(loc);
|
|
1421
|
+
urls.push(` <url><loc>${loc}</loc>${lastmodTag(lastModified)}</url>`);
|
|
1422
|
+
};
|
|
1005
1423
|
for (const page of project.graph.pages) {
|
|
1006
|
-
if (page.meta.draft || page.meta.sidebar.hidden || page.meta.seo.noindex) {
|
|
1424
|
+
if (page.meta.draft || page.meta.sidebar.hidden || page.meta.seo.noindex || ERROR_ROUTES.has(page.route)) {
|
|
1007
1425
|
continue;
|
|
1008
1426
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1427
|
+
pushUrl(withBasePath(deployBase, page.route), page.lastModified);
|
|
1428
|
+
}
|
|
1429
|
+
const userPages = project.context.pagesRoot ? discoverPagesSync(project.context.pagesRoot) : [];
|
|
1430
|
+
const extraRoutes = customStaticRoutes(userPages).filter((route) => !ERROR_ROUTES.has(route));
|
|
1431
|
+
if (hasGeneratedChangelog(project, userPages)) {
|
|
1432
|
+
extraRoutes.push("/changelog");
|
|
1433
|
+
}
|
|
1434
|
+
for (const route of extraRoutes) {
|
|
1435
|
+
pushUrl(withBasePath(deployBase, route));
|
|
1011
1436
|
}
|
|
1012
1437
|
urls.sort();
|
|
1013
1438
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -1055,6 +1480,7 @@ var ar = {
|
|
|
1055
1480
|
send: "إرسال",
|
|
1056
1481
|
title: "اسأل الذكاء الاصطناعي"
|
|
1057
1482
|
},
|
|
1483
|
+
banner: { dismiss: "إغلاق الإعلان" },
|
|
1058
1484
|
feedback: {
|
|
1059
1485
|
no: "لا",
|
|
1060
1486
|
question: "هل كانت هذه الصفحة مفيدة؟",
|
|
@@ -1101,6 +1527,7 @@ var bg = {
|
|
|
1101
1527
|
send: "Изпрати",
|
|
1102
1528
|
title: "Попитай ИИ"
|
|
1103
1529
|
},
|
|
1530
|
+
banner: { dismiss: "Затваряне на съобщението" },
|
|
1104
1531
|
feedback: {
|
|
1105
1532
|
no: "Не",
|
|
1106
1533
|
question: "Беше ли полезна тази страница?",
|
|
@@ -1147,6 +1574,7 @@ var bn = {
|
|
|
1147
1574
|
send: "পাঠান",
|
|
1148
1575
|
title: "AI-কে জিজ্ঞাসা করুন"
|
|
1149
1576
|
},
|
|
1577
|
+
banner: { dismiss: "ঘোষণা বন্ধ করুন" },
|
|
1150
1578
|
feedback: {
|
|
1151
1579
|
no: "না",
|
|
1152
1580
|
question: "এই পৃষ্ঠাটি কি সহায়ক ছিল?",
|
|
@@ -1193,6 +1621,7 @@ var ca = {
|
|
|
1193
1621
|
send: "Envia",
|
|
1194
1622
|
title: "Pregunta a la IA"
|
|
1195
1623
|
},
|
|
1624
|
+
banner: { dismiss: "Tanca l'anunci" },
|
|
1196
1625
|
feedback: {
|
|
1197
1626
|
no: "No",
|
|
1198
1627
|
question: "Aquesta pàgina t'ha estat útil?",
|
|
@@ -1239,6 +1668,7 @@ var cs = {
|
|
|
1239
1668
|
send: "Odeslat",
|
|
1240
1669
|
title: "Zeptat se AI"
|
|
1241
1670
|
},
|
|
1671
|
+
banner: { dismiss: "Zavřít oznámení" },
|
|
1242
1672
|
feedback: {
|
|
1243
1673
|
no: "Ne",
|
|
1244
1674
|
question: "Byla tato stránka užitečná?",
|
|
@@ -1285,6 +1715,7 @@ var da = {
|
|
|
1285
1715
|
send: "Send",
|
|
1286
1716
|
title: "Spørg AI"
|
|
1287
1717
|
},
|
|
1718
|
+
banner: { dismiss: "Luk meddelelsen" },
|
|
1288
1719
|
feedback: {
|
|
1289
1720
|
no: "Nej",
|
|
1290
1721
|
question: "Var denne side nyttig?",
|
|
@@ -1331,6 +1762,7 @@ var de = {
|
|
|
1331
1762
|
send: "Senden",
|
|
1332
1763
|
title: "KI fragen"
|
|
1333
1764
|
},
|
|
1765
|
+
banner: { dismiss: "Ankündigung schließen" },
|
|
1334
1766
|
feedback: {
|
|
1335
1767
|
no: "Nein",
|
|
1336
1768
|
question: "War diese Seite hilfreich?",
|
|
@@ -1377,6 +1809,7 @@ var el = {
|
|
|
1377
1809
|
send: "Αποστολή",
|
|
1378
1810
|
title: "Ρωτήστε την AI"
|
|
1379
1811
|
},
|
|
1812
|
+
banner: { dismiss: "Κλείσιμο ανακοίνωσης" },
|
|
1380
1813
|
feedback: {
|
|
1381
1814
|
no: "Όχι",
|
|
1382
1815
|
question: "Σας φάνηκε χρήσιμη αυτή η σελίδα;",
|
|
@@ -1423,6 +1856,7 @@ var es = {
|
|
|
1423
1856
|
send: "Enviar",
|
|
1424
1857
|
title: "Preguntar a la IA"
|
|
1425
1858
|
},
|
|
1859
|
+
banner: { dismiss: "Cerrar el anuncio" },
|
|
1426
1860
|
feedback: {
|
|
1427
1861
|
no: "No",
|
|
1428
1862
|
question: "¿Te ha resultado útil esta página?",
|
|
@@ -1469,6 +1903,7 @@ var fa = {
|
|
|
1469
1903
|
send: "ارسال",
|
|
1470
1904
|
title: "از هوش مصنوعی بپرسید"
|
|
1471
1905
|
},
|
|
1906
|
+
banner: { dismiss: "بستن اطلاعیه" },
|
|
1472
1907
|
feedback: {
|
|
1473
1908
|
no: "خیر",
|
|
1474
1909
|
question: "آیا این صفحه مفید بود؟",
|
|
@@ -1515,6 +1950,7 @@ var fi = {
|
|
|
1515
1950
|
send: "Lähetä",
|
|
1516
1951
|
title: "Kysy tekoälyltä"
|
|
1517
1952
|
},
|
|
1953
|
+
banner: { dismiss: "Sulje ilmoitus" },
|
|
1518
1954
|
feedback: {
|
|
1519
1955
|
no: "Ei",
|
|
1520
1956
|
question: "Oliko tästä sivusta apua?",
|
|
@@ -1543,7 +1979,7 @@ var fr = {
|
|
|
1543
1979
|
actions: {
|
|
1544
1980
|
addToCursor: "Ajouter à Cursor",
|
|
1545
1981
|
addToVscode: "Ajouter à VS Code",
|
|
1546
|
-
askAI: "Demander à l'IA",
|
|
1982
|
+
askAI: "Demander à l'IA à propos de cette page",
|
|
1547
1983
|
connectMcp: "Se connecter à MCP",
|
|
1548
1984
|
copied: "Copié !",
|
|
1549
1985
|
copyClaudeCode: "Copier la commande Claude Code",
|
|
@@ -1561,6 +1997,7 @@ var fr = {
|
|
|
1561
1997
|
send: "Envoyer",
|
|
1562
1998
|
title: "Demander à l'IA"
|
|
1563
1999
|
},
|
|
2000
|
+
banner: { dismiss: "Fermer l'annonce" },
|
|
1564
2001
|
feedback: {
|
|
1565
2002
|
no: "Non",
|
|
1566
2003
|
question: "Cette page vous a-t-elle été utile ?",
|
|
@@ -1607,6 +2044,7 @@ var he = {
|
|
|
1607
2044
|
send: "שלח",
|
|
1608
2045
|
title: "שאל את ה-AI"
|
|
1609
2046
|
},
|
|
2047
|
+
banner: { dismiss: "סגירת ההודעה" },
|
|
1610
2048
|
feedback: {
|
|
1611
2049
|
no: "לא",
|
|
1612
2050
|
question: "האם העמוד הזה היה מועיל?",
|
|
@@ -1653,6 +2091,7 @@ var hi = {
|
|
|
1653
2091
|
send: "भेजें",
|
|
1654
2092
|
title: "AI से पूछें"
|
|
1655
2093
|
},
|
|
2094
|
+
banner: { dismiss: "घोषणा बंद करें" },
|
|
1656
2095
|
feedback: {
|
|
1657
2096
|
no: "नहीं",
|
|
1658
2097
|
question: "क्या यह पेज सहायक था?",
|
|
@@ -1699,6 +2138,7 @@ var hr = {
|
|
|
1699
2138
|
send: "Pošalji",
|
|
1700
2139
|
title: "Pitaj AI"
|
|
1701
2140
|
},
|
|
2141
|
+
banner: { dismiss: "Zatvori obavijest" },
|
|
1702
2142
|
feedback: {
|
|
1703
2143
|
no: "Ne",
|
|
1704
2144
|
question: "Je li vam ova stranica bila korisna?",
|
|
@@ -1745,6 +2185,7 @@ var hu = {
|
|
|
1745
2185
|
send: "Küldés",
|
|
1746
2186
|
title: "Kérdezd az AI-t"
|
|
1747
2187
|
},
|
|
2188
|
+
banner: { dismiss: "Közlemény bezárása" },
|
|
1748
2189
|
feedback: {
|
|
1749
2190
|
no: "Nem",
|
|
1750
2191
|
question: "Hasznos volt ez az oldal?",
|
|
@@ -1791,6 +2232,7 @@ var id = {
|
|
|
1791
2232
|
send: "Kirim",
|
|
1792
2233
|
title: "Tanya AI"
|
|
1793
2234
|
},
|
|
2235
|
+
banner: { dismiss: "Tutup pengumuman" },
|
|
1794
2236
|
feedback: {
|
|
1795
2237
|
no: "Tidak",
|
|
1796
2238
|
question: "Apakah halaman ini membantu?",
|
|
@@ -1837,6 +2279,7 @@ var it = {
|
|
|
1837
2279
|
send: "Invia",
|
|
1838
2280
|
title: "Chiedi all'IA"
|
|
1839
2281
|
},
|
|
2282
|
+
banner: { dismiss: "Chiudi l'annuncio" },
|
|
1840
2283
|
feedback: {
|
|
1841
2284
|
no: "No",
|
|
1842
2285
|
question: "Questa pagina ti è stata utile?",
|
|
@@ -1883,6 +2326,7 @@ var ja = {
|
|
|
1883
2326
|
send: "送信",
|
|
1884
2327
|
title: "AI に質問"
|
|
1885
2328
|
},
|
|
2329
|
+
banner: { dismiss: "お知らせを閉じる" },
|
|
1886
2330
|
feedback: {
|
|
1887
2331
|
no: "いいえ",
|
|
1888
2332
|
question: "このページは役に立ちましたか?",
|
|
@@ -1929,6 +2373,7 @@ var ko = {
|
|
|
1929
2373
|
send: "보내기",
|
|
1930
2374
|
title: "AI에게 질문"
|
|
1931
2375
|
},
|
|
2376
|
+
banner: { dismiss: "공지 닫기" },
|
|
1932
2377
|
feedback: {
|
|
1933
2378
|
no: "아니요",
|
|
1934
2379
|
question: "이 페이지가 도움이 되었나요?",
|
|
@@ -1975,6 +2420,7 @@ var nl = {
|
|
|
1975
2420
|
send: "Verzenden",
|
|
1976
2421
|
title: "AI vragen"
|
|
1977
2422
|
},
|
|
2423
|
+
banner: { dismiss: "Aankondiging sluiten" },
|
|
1978
2424
|
feedback: {
|
|
1979
2425
|
no: "Nee",
|
|
1980
2426
|
question: "Was deze pagina nuttig?",
|
|
@@ -2021,6 +2467,7 @@ var no = {
|
|
|
2021
2467
|
send: "Send",
|
|
2022
2468
|
title: "Spør AI"
|
|
2023
2469
|
},
|
|
2470
|
+
banner: { dismiss: "Lukk kunngjøringen" },
|
|
2024
2471
|
feedback: {
|
|
2025
2472
|
no: "Nei",
|
|
2026
2473
|
question: "Var denne siden nyttig?",
|
|
@@ -2067,6 +2514,7 @@ var pl = {
|
|
|
2067
2514
|
send: "Wyślij",
|
|
2068
2515
|
title: "Zapytaj AI"
|
|
2069
2516
|
},
|
|
2517
|
+
banner: { dismiss: "Zamknij ogłoszenie" },
|
|
2070
2518
|
feedback: {
|
|
2071
2519
|
no: "Nie",
|
|
2072
2520
|
question: "Czy ta strona była pomocna?",
|
|
@@ -2113,6 +2561,7 @@ var ptBR = {
|
|
|
2113
2561
|
send: "Enviar",
|
|
2114
2562
|
title: "Perguntar à IA"
|
|
2115
2563
|
},
|
|
2564
|
+
banner: { dismiss: "Fechar o anúncio" },
|
|
2116
2565
|
feedback: {
|
|
2117
2566
|
no: "Não",
|
|
2118
2567
|
question: "Esta página foi útil?",
|
|
@@ -2159,6 +2608,7 @@ var pt = {
|
|
|
2159
2608
|
send: "Enviar",
|
|
2160
2609
|
title: "Perguntar à IA"
|
|
2161
2610
|
},
|
|
2611
|
+
banner: { dismiss: "Fechar o anúncio" },
|
|
2162
2612
|
feedback: {
|
|
2163
2613
|
no: "Não",
|
|
2164
2614
|
question: "Esta página foi útil?",
|
|
@@ -2205,6 +2655,7 @@ var ro = {
|
|
|
2205
2655
|
send: "Trimite",
|
|
2206
2656
|
title: "Întreabă AI"
|
|
2207
2657
|
},
|
|
2658
|
+
banner: { dismiss: "Închide anunțul" },
|
|
2208
2659
|
feedback: {
|
|
2209
2660
|
no: "Nu",
|
|
2210
2661
|
question: "Ți-a fost utilă această pagină?",
|
|
@@ -2251,6 +2702,7 @@ var ru = {
|
|
|
2251
2702
|
send: "Отправить",
|
|
2252
2703
|
title: "Спросить ИИ"
|
|
2253
2704
|
},
|
|
2705
|
+
banner: { dismiss: "Закрыть объявление" },
|
|
2254
2706
|
feedback: {
|
|
2255
2707
|
no: "Нет",
|
|
2256
2708
|
question: "Эта страница была полезной?",
|
|
@@ -2297,6 +2749,7 @@ var sk = {
|
|
|
2297
2749
|
send: "Odoslať",
|
|
2298
2750
|
title: "Opýtať sa AI"
|
|
2299
2751
|
},
|
|
2752
|
+
banner: { dismiss: "Zavrieť oznámenie" },
|
|
2300
2753
|
feedback: {
|
|
2301
2754
|
no: "Nie",
|
|
2302
2755
|
question: "Bola táto stránka užitočná?",
|
|
@@ -2343,6 +2796,7 @@ var sr = {
|
|
|
2343
2796
|
send: "Пошаљи",
|
|
2344
2797
|
title: "Питајте AI"
|
|
2345
2798
|
},
|
|
2799
|
+
banner: { dismiss: "Затвори обавештење" },
|
|
2346
2800
|
feedback: {
|
|
2347
2801
|
no: "Не",
|
|
2348
2802
|
question: "Да ли вам је ова страница помогла?",
|
|
@@ -2389,6 +2843,7 @@ var sv = {
|
|
|
2389
2843
|
send: "Skicka",
|
|
2390
2844
|
title: "Fråga AI"
|
|
2391
2845
|
},
|
|
2846
|
+
banner: { dismiss: "Stäng meddelandet" },
|
|
2392
2847
|
feedback: {
|
|
2393
2848
|
no: "Nej",
|
|
2394
2849
|
question: "Var den här sidan till hjälp?",
|
|
@@ -2435,6 +2890,7 @@ var th = {
|
|
|
2435
2890
|
send: "ส่ง",
|
|
2436
2891
|
title: "ถาม AI"
|
|
2437
2892
|
},
|
|
2893
|
+
banner: { dismiss: "ปิดประกาศ" },
|
|
2438
2894
|
feedback: {
|
|
2439
2895
|
no: "ไม่",
|
|
2440
2896
|
question: "หน้านี้มีประโยชน์หรือไม่?",
|
|
@@ -2481,6 +2937,7 @@ var tr = {
|
|
|
2481
2937
|
send: "Gönder",
|
|
2482
2938
|
title: "Yapay zekâya sor"
|
|
2483
2939
|
},
|
|
2940
|
+
banner: { dismiss: "Duyuruyu kapat" },
|
|
2484
2941
|
feedback: {
|
|
2485
2942
|
no: "Hayır",
|
|
2486
2943
|
question: "Bu sayfa yardımcı oldu mu?",
|
|
@@ -2527,6 +2984,7 @@ var uk = {
|
|
|
2527
2984
|
send: "Надіслати",
|
|
2528
2985
|
title: "Запитати ШІ"
|
|
2529
2986
|
},
|
|
2987
|
+
banner: { dismiss: "Закрити оголошення" },
|
|
2530
2988
|
feedback: {
|
|
2531
2989
|
no: "Ні",
|
|
2532
2990
|
question: "Чи була ця сторінка корисною?",
|
|
@@ -2573,6 +3031,7 @@ var vi = {
|
|
|
2573
3031
|
send: "Gửi",
|
|
2574
3032
|
title: "Hỏi AI"
|
|
2575
3033
|
},
|
|
3034
|
+
banner: { dismiss: "Đóng thông báo" },
|
|
2576
3035
|
feedback: {
|
|
2577
3036
|
no: "Không",
|
|
2578
3037
|
question: "Trang này có hữu ích không?",
|
|
@@ -2619,6 +3078,7 @@ var zhTW = {
|
|
|
2619
3078
|
send: "傳送",
|
|
2620
3079
|
title: "向 AI 提問"
|
|
2621
3080
|
},
|
|
3081
|
+
banner: { dismiss: "關閉公告" },
|
|
2622
3082
|
feedback: {
|
|
2623
3083
|
no: "沒有幫助",
|
|
2624
3084
|
question: "這個頁面有幫助嗎?",
|
|
@@ -2665,6 +3125,7 @@ var zh = {
|
|
|
2665
3125
|
send: "发送",
|
|
2666
3126
|
title: "向 AI 提问"
|
|
2667
3127
|
},
|
|
3128
|
+
banner: { dismiss: "关闭公告" },
|
|
2668
3129
|
feedback: {
|
|
2669
3130
|
no: "没有帮助",
|
|
2670
3131
|
question: "这个页面有帮助吗?",
|
|
@@ -2903,9 +3364,16 @@ var toPlainText = (markdown) => {
|
|
|
2903
3364
|
};
|
|
2904
3365
|
var buildCrumbIndex = (sidebar) => {
|
|
2905
3366
|
const index = new Map;
|
|
3367
|
+
const groupRoutes = new Map;
|
|
2906
3368
|
const walk = (nodes, trail) => {
|
|
2907
3369
|
for (const node of nodes) {
|
|
2908
3370
|
if (node.kind === "group") {
|
|
3371
|
+
if (node.route && !groupRoutes.has(node.route)) {
|
|
3372
|
+
groupRoutes.set(node.route, {
|
|
3373
|
+
breadcrumb: [...trail, node.label],
|
|
3374
|
+
section: node.label
|
|
3375
|
+
});
|
|
3376
|
+
}
|
|
2909
3377
|
walk(node.children, [...trail, node.label]);
|
|
2910
3378
|
} else if (node.route) {
|
|
2911
3379
|
index.set(node.route, {
|
|
@@ -2916,6 +3384,11 @@ var buildCrumbIndex = (sidebar) => {
|
|
|
2916
3384
|
}
|
|
2917
3385
|
};
|
|
2918
3386
|
walk(sidebar, []);
|
|
3387
|
+
for (const [route, crumbs] of groupRoutes) {
|
|
3388
|
+
if (!index.has(route)) {
|
|
3389
|
+
index.set(route, crumbs);
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
2919
3392
|
return index;
|
|
2920
3393
|
};
|
|
2921
3394
|
var buildSearchDocuments = async (project, options) => {
|
|
@@ -2939,7 +3412,8 @@ var buildSearchDocuments = async (project, options) => {
|
|
|
2939
3412
|
const page = pageById.get(route.id);
|
|
2940
3413
|
const raw = page ? await readEntryText(project, page) : "";
|
|
2941
3414
|
const source = raw ? frontmatter_default(raw).content : "";
|
|
2942
|
-
const
|
|
3415
|
+
const visible = applyAudienceVisibility(source, options?.audience ?? "web");
|
|
3416
|
+
const body = options?.content === "markdown" ? visible.trim() : toPlainText(visible);
|
|
2943
3417
|
const tags = page?.meta?.search?.tags;
|
|
2944
3418
|
const crumb = crumbs.get(route.path);
|
|
2945
3419
|
return {
|
|
@@ -3235,10 +3709,11 @@ var updateDevLockPort = (outDir, port) => {
|
|
|
3235
3709
|
}
|
|
3236
3710
|
};
|
|
3237
3711
|
var describeDevLock = (lock) => lock.port === undefined ? "" : ` at http://localhost:${lock.port}`;
|
|
3238
|
-
var refuseIfDevRunning = (root, action,
|
|
3239
|
-
const lock = readDevLock(resolveRuntimeDir(root, runtimeDir));
|
|
3712
|
+
var refuseIfDevRunning = (root, action, options = {}) => {
|
|
3713
|
+
const lock = readDevLock(resolveRuntimeDir(root, options.runtimeDir));
|
|
3240
3714
|
if (lock) {
|
|
3241
|
-
|
|
3715
|
+
const remedies = options.isolatedHint ? "Reuse that server, stop it first, or re-run with --isolated to build/verify against .blume-verify without touching it." : "Reuse that server or stop it first.";
|
|
3716
|
+
logger.error(`A \`blume dev\` server is running${describeDevLock(lock)}; ${action} would corrupt its .blume runtime. ${remedies}`);
|
|
3242
3717
|
process.exit(1);
|
|
3243
3718
|
}
|
|
3244
3719
|
};
|
|
@@ -3262,6 +3737,7 @@ import { glob as glob5 } from "tinyglobby";
|
|
|
3262
3737
|
// src/ai/ask-data.ts
|
|
3263
3738
|
var buildAskData = async (project) => {
|
|
3264
3739
|
const documents = await buildSearchDocuments(project, {
|
|
3740
|
+
audience: "agents",
|
|
3265
3741
|
content: "markdown",
|
|
3266
3742
|
includeWhenDisabled: true
|
|
3267
3743
|
});
|
|
@@ -3332,6 +3808,7 @@ var askBackendRuntimeDep = (ask) => {
|
|
|
3332
3808
|
|
|
3333
3809
|
// src/ai/markdown.ts
|
|
3334
3810
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
3811
|
+
var agentMarkdown = (entry) => entry.md ?? entry.mdx;
|
|
3335
3812
|
var buildRawMarkdown = async (project) => {
|
|
3336
3813
|
const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
|
|
3337
3814
|
const readRoute = async (route) => {
|
|
@@ -3341,17 +3818,29 @@ var buildRawMarkdown = async (project) => {
|
|
|
3341
3818
|
}
|
|
3342
3819
|
return route.sourcePath ? await readFile4(route.sourcePath, "utf-8") : "";
|
|
3343
3820
|
};
|
|
3344
|
-
const entries = await Promise.all(project.manifest.routes.map(async (route) =>
|
|
3821
|
+
const entries = await Promise.all(project.manifest.routes.map(async (route) => {
|
|
3822
|
+
const source = applyAgentVisibility(await readRoute(route));
|
|
3823
|
+
const md = downlevelComponents(source, project.config.ai.markdownComponents);
|
|
3824
|
+
const entry = md === source ? { mdx: source } : { md, mdx: source };
|
|
3825
|
+
return [route.path, entry];
|
|
3826
|
+
}));
|
|
3345
3827
|
return Object.fromEntries(entries);
|
|
3346
3828
|
};
|
|
3347
3829
|
|
|
3348
3830
|
// src/ai/mcp/data.ts
|
|
3349
3831
|
var buildMcpData = async (project) => {
|
|
3350
3832
|
const { config, graph, manifest } = project;
|
|
3351
|
-
const [documents,
|
|
3352
|
-
buildSearchDocuments(project, {
|
|
3833
|
+
const [documents, rawMarkdown] = await Promise.all([
|
|
3834
|
+
buildSearchDocuments(project, {
|
|
3835
|
+
audience: "agents",
|
|
3836
|
+
includeWhenDisabled: true
|
|
3837
|
+
}),
|
|
3353
3838
|
buildRawMarkdown(project)
|
|
3354
3839
|
]);
|
|
3840
|
+
const pages = Object.fromEntries(Object.entries(rawMarkdown).map(([route, entry]) => [
|
|
3841
|
+
route,
|
|
3842
|
+
agentMarkdown(entry)
|
|
3843
|
+
]));
|
|
3355
3844
|
const descriptionById = new Map(graph.pages.map((page) => [page.id, page.description]));
|
|
3356
3845
|
const routes = [];
|
|
3357
3846
|
for (const route of manifest.routes) {
|
|
@@ -3368,6 +3857,7 @@ var buildMcpData = async (project) => {
|
|
|
3368
3857
|
});
|
|
3369
3858
|
}
|
|
3370
3859
|
return {
|
|
3860
|
+
base: normalizeBasePath(config.deployment.base),
|
|
3371
3861
|
documents: documents.map((doc) => ({
|
|
3372
3862
|
content: doc.content,
|
|
3373
3863
|
description: doc.description,
|
|
@@ -3414,7 +3904,10 @@ var MCP_TOOLS = [
|
|
|
3414
3904
|
];
|
|
3415
3905
|
|
|
3416
3906
|
// src/ai/mcp/discovery.ts
|
|
3417
|
-
var serverUrl = (input) =>
|
|
3907
|
+
var serverUrl = (input) => {
|
|
3908
|
+
const path = withBasePath(input.base, input.route);
|
|
3909
|
+
return input.site ? `${input.site.replace(/\/+$/u, "")}${path}` : path;
|
|
3910
|
+
};
|
|
3418
3911
|
var buildMcpDiscovery = (input) => ({
|
|
3419
3912
|
servers: [
|
|
3420
3913
|
{
|
|
@@ -3504,7 +3997,7 @@ var validateUsedComponents = (pages, extraTags, registryNames) => {
|
|
|
3504
3997
|
|
|
3505
3998
|
// src/core/component-overrides.ts
|
|
3506
3999
|
import { existsSync as existsSync7 } from "node:fs";
|
|
3507
|
-
import { dirname as dirname5, extname, isAbsolute as isAbsolute2, resolve as resolve3 } from "pathe";
|
|
4000
|
+
import { dirname as dirname5, extname as extname2, isAbsolute as isAbsolute2, resolve as resolve3 } from "pathe";
|
|
3508
4001
|
import ts from "typescript";
|
|
3509
4002
|
var GROUPS = ["mdx", "layout", "islands"];
|
|
3510
4003
|
var GROUP_SET = new Set(GROUPS);
|
|
@@ -3603,17 +4096,17 @@ var probeExtension = (base) => {
|
|
|
3603
4096
|
return null;
|
|
3604
4097
|
};
|
|
3605
4098
|
var toImport = (specifier, imported, dir) => {
|
|
3606
|
-
const
|
|
4099
|
+
const relative5 = specifier.startsWith(".") || isAbsolute2(specifier);
|
|
3607
4100
|
let path = specifier;
|
|
3608
|
-
let extension =
|
|
3609
|
-
if (
|
|
4101
|
+
let extension = extname2(specifier).slice(1).toLowerCase();
|
|
4102
|
+
if (relative5) {
|
|
3610
4103
|
const absolute = isAbsolute2(specifier) ? specifier : resolve3(dir, specifier);
|
|
3611
4104
|
if (extension) {
|
|
3612
4105
|
path = absolute;
|
|
3613
4106
|
} else {
|
|
3614
4107
|
const probed = probeExtension(absolute);
|
|
3615
4108
|
path = probed ?? absolute;
|
|
3616
|
-
extension = probed ?
|
|
4109
|
+
extension = probed ? extname2(probed).slice(1).toLowerCase() : "";
|
|
3617
4110
|
}
|
|
3618
4111
|
}
|
|
3619
4112
|
return {
|
|
@@ -3779,14 +4272,20 @@ var uiStringsObject = z.object({
|
|
|
3779
4272
|
connectMcp: z.string().default("Connect to MCP"),
|
|
3780
4273
|
copied: z.string().default("Copied!"),
|
|
3781
4274
|
copyClaudeCode: z.string().default("Copy Claude Code command"),
|
|
4275
|
+
copyCode: z.string().default("Copy code"),
|
|
3782
4276
|
copyCodex: z.string().default("Copy Codex command"),
|
|
3783
4277
|
copyMarkdown: z.string().default("Copy as Markdown"),
|
|
3784
4278
|
copyServerUrl: z.string().default("Copy server URL"),
|
|
3785
4279
|
edit: z.string().default("Edit on GitHub"),
|
|
4280
|
+
export: z.string().default("Export"),
|
|
4281
|
+
exportEpub: z.string().default("Export to EPUB"),
|
|
4282
|
+
exportPdf: z.string().default("Export to PDF"),
|
|
4283
|
+
generating: z.string().default("Generating…"),
|
|
3786
4284
|
openInChat: z.string().default("Open in chat"),
|
|
3787
4285
|
scrollToTop: z.string().default("Scroll to top")
|
|
3788
4286
|
}).default({}),
|
|
3789
4287
|
ask: z.object({
|
|
4288
|
+
ai: z.string().default("AI"),
|
|
3790
4289
|
clear: z.string().default("Clear conversation"),
|
|
3791
4290
|
close: z.string().default("Close"),
|
|
3792
4291
|
copy: z.string().default("Copy conversation"),
|
|
@@ -3796,7 +4295,17 @@ var uiStringsObject = z.object({
|
|
|
3796
4295
|
placeholder: z.string().default("Ask a question…"),
|
|
3797
4296
|
send: z.string().default("Send"),
|
|
3798
4297
|
tip: z.string().default("Tip: You can open and close chat with"),
|
|
3799
|
-
title: z.string().default("Ask AI")
|
|
4298
|
+
title: z.string().default("Ask AI"),
|
|
4299
|
+
you: z.string().default("You")
|
|
4300
|
+
}).default({}),
|
|
4301
|
+
banner: z.object({
|
|
4302
|
+
dismiss: z.string().default("Dismiss announcement")
|
|
4303
|
+
}).default({}),
|
|
4304
|
+
changelog: z.object({
|
|
4305
|
+
showReleases: z.string().default("Show {version} releases")
|
|
4306
|
+
}).default({}),
|
|
4307
|
+
content: z.object({
|
|
4308
|
+
diagramError: z.string().default("Could not render this diagram.")
|
|
3800
4309
|
}).default({}),
|
|
3801
4310
|
feedback: z.object({
|
|
3802
4311
|
no: z.string().default("No"),
|
|
@@ -3808,6 +4317,18 @@ var uiStringsObject = z.object({
|
|
|
3808
4317
|
label: z.string().default("Language"),
|
|
3809
4318
|
untranslated: z.string().default("Not translated")
|
|
3810
4319
|
}).default({}),
|
|
4320
|
+
nav: z.object({
|
|
4321
|
+
back: z.string().default("Back"),
|
|
4322
|
+
closeNavigation: z.string().default("Close navigation"),
|
|
4323
|
+
deprecated: z.string().default("deprecated"),
|
|
4324
|
+
featured: z.string().default("Featured"),
|
|
4325
|
+
githubRepository: z.string().default("GitHub repository"),
|
|
4326
|
+
navigation: z.string().default("Navigation"),
|
|
4327
|
+
primary: z.string().default("Primary"),
|
|
4328
|
+
sections: z.string().default("Sections"),
|
|
4329
|
+
toggleNavigation: z.string().default("Toggle navigation"),
|
|
4330
|
+
toggleTheme: z.string().default("Toggle color theme")
|
|
4331
|
+
}).default({}),
|
|
3811
4332
|
notFound: z.object({
|
|
3812
4333
|
description: z.string().default("We couldn't find the page you're looking for."),
|
|
3813
4334
|
home: z.string().default("Back to home"),
|
|
@@ -3821,11 +4342,19 @@ var uiStringsObject = z.object({
|
|
|
3821
4342
|
}).default({}),
|
|
3822
4343
|
search: z.object({
|
|
3823
4344
|
allLanguages: z.string().default("All languages"),
|
|
4345
|
+
askAi: z.string().default("Ask AI"),
|
|
4346
|
+
askAiHint: z.string().default("Get an instant answer from AI"),
|
|
3824
4347
|
button: z.string().default("Search"),
|
|
3825
4348
|
devOnly: z.string().default("Search is available in the production build."),
|
|
4349
|
+
error: z.string().default("Something went wrong. Please try again."),
|
|
3826
4350
|
label: z.string().default("Search docs"),
|
|
4351
|
+
navigate: z.string().default("navigate"),
|
|
3827
4352
|
noResults: z.string().default("No results found."),
|
|
3828
|
-
|
|
4353
|
+
open: z.string().default("open"),
|
|
4354
|
+
placeholder: z.string().default("Search documentation…"),
|
|
4355
|
+
popular: z.string().default("Popular"),
|
|
4356
|
+
preview: z.string().default("preview"),
|
|
4357
|
+
results: z.string().default("Results")
|
|
3829
4358
|
}).default({}),
|
|
3830
4359
|
toc: z.object({
|
|
3831
4360
|
title: z.string().default("On this page")
|
|
@@ -4060,7 +4589,7 @@ var sourcesOf = (block) => {
|
|
|
4060
4589
|
}
|
|
4061
4590
|
return sources;
|
|
4062
4591
|
};
|
|
4063
|
-
var referencesFor = (kind, block, defaultLabel, renderer, display) => {
|
|
4592
|
+
var referencesFor = (kind, block, defaultLabel, renderer, display, basePath) => {
|
|
4064
4593
|
if (!block.enabled) {
|
|
4065
4594
|
return [];
|
|
4066
4595
|
}
|
|
@@ -4078,6 +4607,7 @@ var referencesFor = (kind, block, defaultLabel, renderer, display) => {
|
|
|
4078
4607
|
route = normalizeRoute(`${base}/${suffix || index + 1}`);
|
|
4079
4608
|
}
|
|
4080
4609
|
return {
|
|
4610
|
+
basePath,
|
|
4081
4611
|
display,
|
|
4082
4612
|
kind,
|
|
4083
4613
|
label,
|
|
@@ -4094,8 +4624,8 @@ var resolveReferences = (config) => [
|
|
|
4094
4624
|
...referencesFor("openapi", config.openapi, "API Reference", config.openapi.renderer, {
|
|
4095
4625
|
codeSamples: config.openapi.codeSamples,
|
|
4096
4626
|
expandSchemas: config.openapi.expandSchemas
|
|
4097
|
-
}),
|
|
4098
|
-
...referencesFor("asyncapi", config.asyncapi, "Events", "scalar", NO_DISPLAY)
|
|
4627
|
+
}, config.basePath),
|
|
4628
|
+
...referencesFor("asyncapi", config.asyncapi, "Events", "scalar", NO_DISPLAY, config.basePath)
|
|
4099
4629
|
];
|
|
4100
4630
|
var referenceTabs = (config) => resolveReferences(config).map((ref) => ({
|
|
4101
4631
|
label: ref.label,
|
|
@@ -4105,10 +4635,11 @@ var blumeReferenceOf = (ref, seen, usedSlugs) => {
|
|
|
4105
4635
|
if (ref.kind !== "openapi" || ref.renderer !== "blume") {
|
|
4106
4636
|
return null;
|
|
4107
4637
|
}
|
|
4108
|
-
|
|
4638
|
+
const kept = seen.get(ref.route);
|
|
4639
|
+
if (kept) {
|
|
4640
|
+
(kept.collisions ??= []).push(`Two API reference sources resolve to ${ref.route}; keeping the first.`);
|
|
4109
4641
|
return null;
|
|
4110
4642
|
}
|
|
4111
|
-
seen.add(ref.route);
|
|
4112
4643
|
let { slug } = ref;
|
|
4113
4644
|
let n = 2;
|
|
4114
4645
|
while (usedSlugs.has(slug)) {
|
|
@@ -4116,10 +4647,12 @@ var blumeReferenceOf = (ref, seen, usedSlugs) => {
|
|
|
4116
4647
|
n += 1;
|
|
4117
4648
|
}
|
|
4118
4649
|
usedSlugs.add(slug);
|
|
4119
|
-
|
|
4650
|
+
const accepted = slug === ref.slug ? ref : { ...ref, slug };
|
|
4651
|
+
seen.set(ref.route, accepted);
|
|
4652
|
+
return accepted;
|
|
4120
4653
|
};
|
|
4121
4654
|
var blumeReferences = (config) => {
|
|
4122
|
-
const seen = new
|
|
4655
|
+
const seen = new Map;
|
|
4123
4656
|
const usedSlugs = new Set;
|
|
4124
4657
|
const result = [];
|
|
4125
4658
|
for (const ref of resolveReferences(config)) {
|
|
@@ -4243,9 +4776,14 @@ var extractOperations = (document, baseRoute) => {
|
|
|
4243
4776
|
const tagsSeen = new Set;
|
|
4244
4777
|
const tagMeta = new Map((document.tags ?? []).map((tag) => [tag.name, tag.description ?? ""]));
|
|
4245
4778
|
const seen = new Set;
|
|
4779
|
+
const warnings = [];
|
|
4246
4780
|
for (const [path, rawItem] of Object.entries(document.paths ?? {})) {
|
|
4247
4781
|
const item = rawItem;
|
|
4248
|
-
if (!item
|
|
4782
|
+
if (!item) {
|
|
4783
|
+
continue;
|
|
4784
|
+
}
|
|
4785
|
+
if ("$ref" in item) {
|
|
4786
|
+
warnings.push(`Path "${path}" is a $ref to a shared path item; referenced path items are not resolved, so its operations are missing from the reference. Inline the path item under "paths" to render it.`);
|
|
4249
4787
|
continue;
|
|
4250
4788
|
}
|
|
4251
4789
|
for (const method of HTTP_METHODS) {
|
|
@@ -4283,7 +4821,7 @@ var extractOperations = (document, baseRoute) => {
|
|
|
4283
4821
|
name,
|
|
4284
4822
|
slug: slugify2(name) || "operations"
|
|
4285
4823
|
}));
|
|
4286
|
-
return { operations, tags };
|
|
4824
|
+
return { operations, tags, warnings };
|
|
4287
4825
|
};
|
|
4288
4826
|
|
|
4289
4827
|
// src/openapi/parse.ts
|
|
@@ -4307,6 +4845,13 @@ var PROXY_ENV_VARS = [
|
|
|
4307
4845
|
"ALL_PROXY",
|
|
4308
4846
|
"all_proxy"
|
|
4309
4847
|
];
|
|
4848
|
+
|
|
4849
|
+
class InvalidSpecError extends Error {
|
|
4850
|
+
constructor(message) {
|
|
4851
|
+
super(message);
|
|
4852
|
+
this.name = "InvalidSpecError";
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4310
4855
|
var proxyInstalled = false;
|
|
4311
4856
|
var ensureProxyDispatcher = async () => {
|
|
4312
4857
|
if (proxyInstalled || !PROXY_ENV_VARS.some((name) => process.env[name])) {
|
|
@@ -4417,6 +4962,9 @@ var parseSpec = async (spec, root, options = {}) => {
|
|
|
4417
4962
|
const { text, warnings } = await readSpecText(spec, root, options);
|
|
4418
4963
|
const normalized = normalize2(text);
|
|
4419
4964
|
const { specification } = upgrade(normalized);
|
|
4965
|
+
if (specification === null || typeof specification !== "object") {
|
|
4966
|
+
throw new InvalidSpecError(`${spec} is not a valid OpenAPI document (expected a YAML or JSON object).`);
|
|
4967
|
+
}
|
|
4420
4968
|
return { document: specification, warnings };
|
|
4421
4969
|
};
|
|
4422
4970
|
|
|
@@ -4533,7 +5081,11 @@ var openApiSource = (references, ctx) => {
|
|
|
4533
5081
|
const loadReference = async (reference) => {
|
|
4534
5082
|
try {
|
|
4535
5083
|
const { document, warnings } = await parseSpec(reference.spec, ctx.projectRoot, { cacheDir: ctx.cacheDir, refresh: ctx.refresh });
|
|
4536
|
-
const {
|
|
5084
|
+
const {
|
|
5085
|
+
operations,
|
|
5086
|
+
tags,
|
|
5087
|
+
warnings: extractWarnings
|
|
5088
|
+
} = extractOperations(document, reference.route);
|
|
4537
5089
|
const info = document.info ?? { title: reference.label, version: "" };
|
|
4538
5090
|
const spec = {
|
|
4539
5091
|
codeSamples: reference.display.codeSamples,
|
|
@@ -4541,7 +5093,13 @@ var openApiSource = (references, ctx) => {
|
|
|
4541
5093
|
document,
|
|
4542
5094
|
expandSchemas: reference.display.expandSchemas,
|
|
4543
5095
|
label: reference.label,
|
|
4544
|
-
operations: Object.fromEntries(operations.map((operation) => [
|
|
5096
|
+
operations: Object.fromEntries(operations.map((operation) => [
|
|
5097
|
+
operation.key,
|
|
5098
|
+
{
|
|
5099
|
+
...operation,
|
|
5100
|
+
route: withBasePath(reference.basePath, operation.route)
|
|
5101
|
+
}
|
|
5102
|
+
])),
|
|
4545
5103
|
route: reference.route,
|
|
4546
5104
|
slug: reference.slug,
|
|
4547
5105
|
tags,
|
|
@@ -4549,11 +5107,26 @@ var openApiSource = (references, ctx) => {
|
|
|
4549
5107
|
version: info.version ?? ""
|
|
4550
5108
|
};
|
|
4551
5109
|
return {
|
|
4552
|
-
diagnostics:
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
5110
|
+
diagnostics: [
|
|
5111
|
+
...warnings.map((message) => ({
|
|
5112
|
+
code: "BLUME_OPENAPI_STALE",
|
|
5113
|
+
message,
|
|
5114
|
+
severity: "warning"
|
|
5115
|
+
})),
|
|
5116
|
+
...extractWarnings.map((message) => ({
|
|
5117
|
+
code: "BLUME_OPENAPI_REF_PATH_ITEM",
|
|
5118
|
+
message: `In OpenAPI spec "${reference.spec}": ${message}`,
|
|
5119
|
+
severity: "warning"
|
|
5120
|
+
})),
|
|
5121
|
+
...operations.length === 0 ? [
|
|
5122
|
+
{
|
|
5123
|
+
code: "BLUME_OPENAPI_EMPTY",
|
|
5124
|
+
message: `OpenAPI spec "${reference.spec}" for ${reference.route} declares no operations; its API reference is empty.`,
|
|
5125
|
+
severity: "warning",
|
|
5126
|
+
suggestion: "Check the spec points at an OpenAPI document with operations under `paths`."
|
|
5127
|
+
}
|
|
5128
|
+
] : []
|
|
5129
|
+
],
|
|
4557
5130
|
entries: specEntries(spec, operations),
|
|
4558
5131
|
slug: reference.slug,
|
|
4559
5132
|
spec
|
|
@@ -4563,14 +5136,18 @@ var openApiSource = (references, ctx) => {
|
|
|
4563
5136
|
code: "BLUME_OPENAPI_UNAVAILABLE",
|
|
4564
5137
|
message: `Could not load OpenAPI spec "${reference.spec}" for ${reference.route} (${error.message}); its reference pages were skipped.`,
|
|
4565
5138
|
severity: ctx.mode === "build" ? "error" : "warning",
|
|
4566
|
-
suggestion: "Check the spec URL/path is reachable from the build environment; behind a proxy, set HTTP(S)_PROXY."
|
|
5139
|
+
suggestion: error instanceof InvalidSpecError ? "Point the spec at an OpenAPI document (a YAML or JSON file with an object at the top level)." : "Check the spec URL/path is reachable from the build environment; behind a proxy, set HTTP(S)_PROXY."
|
|
4567
5140
|
};
|
|
4568
5141
|
}
|
|
4569
5142
|
};
|
|
4570
5143
|
const load2 = async () => {
|
|
4571
5144
|
const results = await Promise.all(references.map(loadReference));
|
|
4572
5145
|
const entries = [];
|
|
4573
|
-
const diagnostics = []
|
|
5146
|
+
const diagnostics = references.flatMap((reference) => (reference.collisions ?? []).map((message) => ({
|
|
5147
|
+
code: "BLUME_OPENAPI_ROUTE_COLLISION",
|
|
5148
|
+
message,
|
|
5149
|
+
severity: "warning"
|
|
5150
|
+
})));
|
|
4574
5151
|
const data = {};
|
|
4575
5152
|
for (const result of results) {
|
|
4576
5153
|
if ("severity" in result) {
|
|
@@ -4596,8 +5173,8 @@ var openApiSource = (references, ctx) => {
|
|
|
4596
5173
|
// src/core/sources/filesystem.ts
|
|
4597
5174
|
import { existsSync as existsSync8, watch as fsWatch } from "node:fs";
|
|
4598
5175
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
4599
|
-
import { extname as
|
|
4600
|
-
import { glob } from "tinyglobby";
|
|
5176
|
+
import { extname as extname3, isAbsolute as isAbsolute4, join as join12, relative as relative5, resolve as resolve4 } from "pathe";
|
|
5177
|
+
import { glob as glob2 } from "tinyglobby";
|
|
4601
5178
|
|
|
4602
5179
|
// src/core/sources/watch.ts
|
|
4603
5180
|
var BLUME_IGNORE_DIRS = [
|
|
@@ -4627,7 +5204,7 @@ var ignoringWatchListener = (onChange, ignoreDirs = BLUME_WATCH_IGNORE_DIRS) =>
|
|
|
4627
5204
|
var filesystemSource = (options) => {
|
|
4628
5205
|
const contentRoot = isAbsolute4(options.root) ? options.root : join12(resolve4(options.projectRoot), options.root);
|
|
4629
5206
|
const load2 = async () => {
|
|
4630
|
-
const files = await
|
|
5207
|
+
const files = await glob2(options.include, {
|
|
4631
5208
|
absolute: true,
|
|
4632
5209
|
cwd: contentRoot,
|
|
4633
5210
|
ignore: [...options.exclude, ...baselineScanIgnore()],
|
|
@@ -4636,13 +5213,14 @@ var filesystemSource = (options) => {
|
|
|
4636
5213
|
files.sort();
|
|
4637
5214
|
const entries = await Promise.all(files.map(async (file) => {
|
|
4638
5215
|
const source = await readFile7(file, "utf-8");
|
|
4639
|
-
const ext =
|
|
5216
|
+
const ext = extname3(file).toLowerCase();
|
|
4640
5217
|
const format = ext === ".mdx" ? "mdx" : "md";
|
|
4641
5218
|
const parsed = frontmatter_default(source);
|
|
4642
5219
|
return {
|
|
4643
5220
|
body: { format, text: parsed.content },
|
|
4644
5221
|
data: parsed.data,
|
|
4645
|
-
|
|
5222
|
+
raw: source,
|
|
5223
|
+
ref: relative5(contentRoot, file),
|
|
4646
5224
|
sourcePath: file
|
|
4647
5225
|
};
|
|
4648
5226
|
}));
|
|
@@ -4878,14 +5456,9 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
4878
5456
|
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
4879
5457
|
const cache = snapshotCache(ctx.cacheDir);
|
|
4880
5458
|
let snapshot = new Map;
|
|
4881
|
-
const
|
|
4882
|
-
if (options.github) {
|
|
4883
|
-
return
|
|
4884
|
-
}
|
|
4885
|
-
if (options.files && options.url) {
|
|
4886
|
-
const base = options.url.replace(/\/$/u, "");
|
|
4887
|
-
const refs = options.files.flatMap((ref) => matchesInclude(ref, options.include) ? [{ editUrl: `${base}/${ref}`, fetchUrl: `${base}/${ref}`, ref }] : []);
|
|
4888
|
-
return { refs, truncated: false };
|
|
5459
|
+
const assertConfigured = () => {
|
|
5460
|
+
if (options.github || options.files && options.url) {
|
|
5461
|
+
return;
|
|
4889
5462
|
}
|
|
4890
5463
|
throw new BlumeError({
|
|
4891
5464
|
code: "BLUME_SOURCE_MISCONFIGURED",
|
|
@@ -4893,6 +5466,14 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
4893
5466
|
severity: "error"
|
|
4894
5467
|
});
|
|
4895
5468
|
};
|
|
5469
|
+
const enumerate = async () => {
|
|
5470
|
+
if (options.github) {
|
|
5471
|
+
return await enumerateGithub(options.github, options.include, doFetch);
|
|
5472
|
+
}
|
|
5473
|
+
const base = (options.url ?? "").replace(/\/$/u, "");
|
|
5474
|
+
const refs = (options.files ?? []).flatMap((ref) => matchesInclude(ref, options.include) ? [{ editUrl: `${base}/${ref}`, fetchUrl: `${base}/${ref}`, ref }] : []);
|
|
5475
|
+
return { refs, truncated: false };
|
|
5476
|
+
};
|
|
4896
5477
|
const fetchEntry = async (item) => {
|
|
4897
5478
|
const res = await doFetch(item.fetchUrl, {
|
|
4898
5479
|
headers: githubHeaders2(item.fetchUrl)
|
|
@@ -4913,6 +5494,7 @@ var mdxRemoteSource = (options, ctx) => {
|
|
|
4913
5494
|
};
|
|
4914
5495
|
};
|
|
4915
5496
|
const load2 = async (refresh = ctx.refresh ?? true) => {
|
|
5497
|
+
assertConfigured();
|
|
4916
5498
|
const skipped = [];
|
|
4917
5499
|
const result = await loadWithCache(options.name, cache, async () => {
|
|
4918
5500
|
const { refs, truncated } = await enumerate();
|
|
@@ -4973,22 +5555,22 @@ import { join as join14 } from "pathe";
|
|
|
4973
5555
|
|
|
4974
5556
|
// src/core/sources/assets.ts
|
|
4975
5557
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
4976
|
-
import { extname as
|
|
5558
|
+
import { extname as extname4, join as join13 } from "pathe";
|
|
4977
5559
|
var MD_IMAGE = /!\[(?<alt>[^\]]*)\]\((?<url>[^)\s]+)\)/gu;
|
|
4978
5560
|
var REMOTE = /^https?:\/\//u;
|
|
4979
5561
|
var SAFE_EXT = /^\.[a-z0-9]+$/iu;
|
|
4980
|
-
var
|
|
4981
|
-
var
|
|
5562
|
+
var CODE_FENCE_BLOCK2 = /^(?<fence>`{3,}|~{3,})[^\n]*\n[\s\S]*?^\k<fence>[^\n]*(?=\n|$)/gmu;
|
|
5563
|
+
var FENCE_TOKEN2 = /\u0000blume-fence-(?<index>\d+)\u0000/gu;
|
|
4982
5564
|
var extFor = (url) => {
|
|
4983
5565
|
const clean = url.split("?")[0] ?? url;
|
|
4984
|
-
const ext =
|
|
5566
|
+
const ext = extname4(clean);
|
|
4985
5567
|
return SAFE_EXT.test(ext) ? ext.toLowerCase() : ".png";
|
|
4986
5568
|
};
|
|
4987
5569
|
var materializeAssets = async (markdown, ctx) => {
|
|
4988
5570
|
const doFetch = ctx.fetchImpl ?? globalThis.fetch;
|
|
4989
5571
|
const diagnostics = [];
|
|
4990
5572
|
const fences = [];
|
|
4991
|
-
const masked = markdown.replace(
|
|
5573
|
+
const masked = markdown.replace(CODE_FENCE_BLOCK2, (block) => {
|
|
4992
5574
|
fences.push(block);
|
|
4993
5575
|
return `\x00blume-fence-${fences.length - 1}\x00`;
|
|
4994
5576
|
});
|
|
@@ -5007,7 +5589,7 @@ var materializeAssets = async (markdown, ctx) => {
|
|
|
5007
5589
|
throw new Error(`${res.status}`);
|
|
5008
5590
|
}
|
|
5009
5591
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
5010
|
-
const file = `${hashText(url)}${extFor(url)}`;
|
|
5592
|
+
const file = `${hashText(url.split("?")[0] ?? url)}${extFor(url)}`;
|
|
5011
5593
|
await mkdir5(ctx.assetsDir, { recursive: true });
|
|
5012
5594
|
await writeFile5(join13(ctx.assetsDir, file), bytes);
|
|
5013
5595
|
rewrites.set(url, `${ctx.assetsBaseUrl}/${file}`);
|
|
@@ -5022,14 +5604,14 @@ var materializeAssets = async (markdown, ctx) => {
|
|
|
5022
5604
|
const rewritten = masked.replaceAll(MD_IMAGE, (match, alt, url) => {
|
|
5023
5605
|
const local = rewrites.get(url);
|
|
5024
5606
|
return local ? `` : match;
|
|
5025
|
-
}).replaceAll(
|
|
5607
|
+
}).replaceAll(FENCE_TOKEN2, (token, index) => fences[Number(index)] ?? token);
|
|
5026
5608
|
return { diagnostics, markdown: rewritten };
|
|
5027
5609
|
};
|
|
5028
5610
|
|
|
5029
5611
|
// src/core/sources/normalize.ts
|
|
5030
5612
|
import { existsSync as existsSync9, readFileSync as readFileSync3 } from "node:fs";
|
|
5031
5613
|
import GithubSlugger from "github-slugger";
|
|
5032
|
-
import { extname as
|
|
5614
|
+
import { extname as extname5 } from "pathe";
|
|
5033
5615
|
|
|
5034
5616
|
// src/core/schema.ts
|
|
5035
5617
|
import { z as z2 } from "zod";
|
|
@@ -5503,7 +6085,16 @@ var aiConfigSchema = z2.strictObject({
|
|
|
5503
6085
|
});
|
|
5504
6086
|
}
|
|
5505
6087
|
}).optional(),
|
|
5506
|
-
llmsTxt: z2.
|
|
6088
|
+
llmsTxt: z2.union([
|
|
6089
|
+
z2.boolean(),
|
|
6090
|
+
z2.strictObject({
|
|
6091
|
+
enabled: z2.boolean().default(true),
|
|
6092
|
+
openapi: z2.boolean().default(true)
|
|
6093
|
+
})
|
|
6094
|
+
]).default(true).transform((value) => typeof value === "boolean" ? { enabled: value, openapi: true } : value),
|
|
6095
|
+
markdownComponents: z2.record(z2.custom((value) => typeof value === "function", {
|
|
6096
|
+
message: "Expected a serializer function."
|
|
6097
|
+
})).default({})
|
|
5507
6098
|
});
|
|
5508
6099
|
var featuredLinkSchema = z2.strictObject({
|
|
5509
6100
|
href: z2.string(),
|
|
@@ -5534,7 +6125,7 @@ var mcpConfigSchema = z2.strictObject({
|
|
|
5534
6125
|
enabled: z2.boolean().default(false),
|
|
5535
6126
|
instructions: z2.string().optional(),
|
|
5536
6127
|
name: z2.string().optional(),
|
|
5537
|
-
route: z2.string().default("/mcp")
|
|
6128
|
+
route: z2.string().default("/mcp").transform(normalizeRoute)
|
|
5538
6129
|
});
|
|
5539
6130
|
var localeSchema = z2.strictObject({
|
|
5540
6131
|
code: z2.string().min(1),
|
|
@@ -5751,7 +6342,7 @@ var addRouteSegment = (part, segments, groups) => {
|
|
|
5751
6342
|
segments.push(clean);
|
|
5752
6343
|
};
|
|
5753
6344
|
var mapRoute = (relativePath) => {
|
|
5754
|
-
const withoutExt = relativePath.slice(0, relativePath.length -
|
|
6345
|
+
const withoutExt = relativePath.slice(0, relativePath.length - extname5(relativePath).length);
|
|
5755
6346
|
const rawParts = withoutExt.split("/");
|
|
5756
6347
|
const segments = [];
|
|
5757
6348
|
const groups = [];
|
|
@@ -5761,41 +6352,78 @@ var mapRoute = (relativePath) => {
|
|
|
5761
6352
|
const route = segments.length === 0 ? "/" : `/${segments.join("/")}`;
|
|
5762
6353
|
return { groups, route, segments };
|
|
5763
6354
|
};
|
|
5764
|
-
var CODE_FENCE2 =
|
|
5765
|
-
var
|
|
5766
|
-
|
|
5767
|
-
if (
|
|
5768
|
-
return
|
|
6355
|
+
var CODE_FENCE2 = /^(?<delimiter>```|~~~)/u;
|
|
6356
|
+
var nextFenceState = (line, fence) => {
|
|
6357
|
+
const delimiter = line.trimStart().match(CODE_FENCE2)?.groups?.delimiter;
|
|
6358
|
+
if (delimiter === undefined) {
|
|
6359
|
+
return fence;
|
|
6360
|
+
}
|
|
6361
|
+
if (fence === null) {
|
|
6362
|
+
return delimiter;
|
|
6363
|
+
}
|
|
6364
|
+
return fence === delimiter ? null : fence;
|
|
6365
|
+
};
|
|
6366
|
+
var ATX_HEADING = /^ {0,3}(?<hashes>#{1,6})\s+(?<text>.+?)(?:\s+#+)?\s*$/u;
|
|
6367
|
+
var SETEXT_UNDERLINE = /^ {0,3}(?<marker>=+|-+)\s*$/u;
|
|
6368
|
+
var PARAGRAPH_INTERRUPT = /^ {0,3}(?:[-+*][ \t]|\d{1,9}[.)][ \t]|>)/u;
|
|
6369
|
+
var THEMATIC_BREAK = /^ {0,3}(?:(?:-[ \t]*){3,}|(?:\*[ \t]*){3,}|(?:_[ \t]*){3,})$/u;
|
|
6370
|
+
var FRONT_MATTER_CLOSE = /^(?:-{3}|\.{3})\s*$/u;
|
|
6371
|
+
var linesWithoutFrontMatter = (body) => {
|
|
6372
|
+
const lines = body.split(`
|
|
6373
|
+
`);
|
|
6374
|
+
if (!/^-{3}\s*$/u.test(lines[0] ?? "")) {
|
|
6375
|
+
return lines;
|
|
5769
6376
|
}
|
|
5770
|
-
|
|
5771
|
-
|
|
6377
|
+
const close = lines.findIndex((line, index) => index > 0 && FRONT_MATTER_CLOSE.test(line));
|
|
6378
|
+
return close === -1 ? lines : lines.slice(close + 1);
|
|
6379
|
+
};
|
|
6380
|
+
var scanHeadingLine = (line, state, slugger, headings) => {
|
|
6381
|
+
const next = nextFenceState(line, state.fence);
|
|
6382
|
+
if (state.fence !== null || next !== null) {
|
|
6383
|
+
state.fence = next;
|
|
6384
|
+
state.paragraph = [];
|
|
6385
|
+
return;
|
|
5772
6386
|
}
|
|
5773
|
-
const
|
|
5774
|
-
if (
|
|
5775
|
-
const depth =
|
|
5776
|
-
const text = (
|
|
6387
|
+
const atx = line.match(ATX_HEADING);
|
|
6388
|
+
if (atx?.groups) {
|
|
6389
|
+
const depth = atx.groups.hashes?.length ?? 1;
|
|
6390
|
+
const text = (atx.groups.text ?? "").trim();
|
|
5777
6391
|
headings.push({ depth, slug: slugger.slug(text), text });
|
|
6392
|
+
state.paragraph = [];
|
|
6393
|
+
return;
|
|
6394
|
+
}
|
|
6395
|
+
const setext = line.match(SETEXT_UNDERLINE);
|
|
6396
|
+
if (setext?.groups && state.paragraph.length > 0) {
|
|
6397
|
+
const text = state.paragraph.join(" ").trim();
|
|
6398
|
+
headings.push({
|
|
6399
|
+
depth: setext.groups.marker?.startsWith("=") ? 1 : 2,
|
|
6400
|
+
slug: slugger.slug(text),
|
|
6401
|
+
text
|
|
6402
|
+
});
|
|
6403
|
+
state.paragraph = [];
|
|
6404
|
+
return;
|
|
5778
6405
|
}
|
|
5779
|
-
|
|
6406
|
+
if (line.trim() === "" || THEMATIC_BREAK.test(line) || PARAGRAPH_INTERRUPT.test(line)) {
|
|
6407
|
+
state.paragraph = [];
|
|
6408
|
+
return;
|
|
6409
|
+
}
|
|
6410
|
+
state.paragraph.push(line.trim());
|
|
5780
6411
|
};
|
|
5781
6412
|
var extractHeadings = (body) => {
|
|
5782
6413
|
const headings = [];
|
|
5783
6414
|
const slugger = new GithubSlugger;
|
|
5784
|
-
|
|
5785
|
-
for (const line of body
|
|
5786
|
-
|
|
5787
|
-
inFence = scanHeadingLine(line, inFence, slugger, headings);
|
|
6415
|
+
const state = { fence: null, paragraph: [] };
|
|
6416
|
+
for (const line of linesWithoutFrontMatter(body)) {
|
|
6417
|
+
scanHeadingLine(line, state, slugger, headings);
|
|
5788
6418
|
}
|
|
5789
6419
|
return headings;
|
|
5790
6420
|
};
|
|
5791
6421
|
var MD_LINK = /\[[^\]]*\]\((?<target>[^)\s]+)(?:\s+"[^"]*")?\)/gu;
|
|
5792
6422
|
var INLINE_CODE2 = /`[^`]*`/gu;
|
|
5793
|
-
var scanLinkLine = (line, lineNumber,
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
if (inFence) {
|
|
5798
|
-
return inFence;
|
|
6423
|
+
var scanLinkLine = (line, lineNumber, fence, links) => {
|
|
6424
|
+
const next = nextFenceState(line, fence);
|
|
6425
|
+
if (fence !== null || next !== null) {
|
|
6426
|
+
return next;
|
|
5799
6427
|
}
|
|
5800
6428
|
const masked = line.replaceAll(INLINE_CODE2, (span) => " ".repeat(span.length));
|
|
5801
6429
|
for (const match of masked.matchAll(MD_LINK)) {
|
|
@@ -5810,27 +6438,25 @@ var scanLinkLine = (line, lineNumber, inFence, links) => {
|
|
|
5810
6438
|
target
|
|
5811
6439
|
});
|
|
5812
6440
|
}
|
|
5813
|
-
return
|
|
6441
|
+
return next;
|
|
5814
6442
|
};
|
|
5815
|
-
var extractLinks = (body) => {
|
|
6443
|
+
var extractLinks = (body, lineOffset = 0) => {
|
|
5816
6444
|
const links = [];
|
|
5817
|
-
let
|
|
5818
|
-
let lineNumber =
|
|
6445
|
+
let fence = null;
|
|
6446
|
+
let lineNumber = lineOffset;
|
|
5819
6447
|
for (const line of body.split(`
|
|
5820
6448
|
`)) {
|
|
5821
6449
|
lineNumber += 1;
|
|
5822
|
-
|
|
6450
|
+
fence = scanLinkLine(line, lineNumber, fence, links);
|
|
5823
6451
|
}
|
|
5824
6452
|
return links;
|
|
5825
6453
|
};
|
|
5826
6454
|
var DOUBLE_QUOTED = /"[^"]*"/gu;
|
|
5827
6455
|
var JSX_OPEN = /<(?<tag>[A-Z][A-Za-z0-9]*)/gu;
|
|
5828
|
-
var scanTagLine = (line,
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
if (inFence) {
|
|
5833
|
-
return inFence;
|
|
6456
|
+
var scanTagLine = (line, fence, tags) => {
|
|
6457
|
+
const next = nextFenceState(line, fence);
|
|
6458
|
+
if (fence !== null || next !== null) {
|
|
6459
|
+
return next;
|
|
5834
6460
|
}
|
|
5835
6461
|
const clean = line.replaceAll(INLINE_CODE2, "").replaceAll(DOUBLE_QUOTED, "");
|
|
5836
6462
|
for (const match of clean.matchAll(JSX_OPEN)) {
|
|
@@ -5839,17 +6465,20 @@ var scanTagLine = (line, inFence, tags) => {
|
|
|
5839
6465
|
tags.add(tag);
|
|
5840
6466
|
}
|
|
5841
6467
|
}
|
|
5842
|
-
return
|
|
6468
|
+
return next;
|
|
5843
6469
|
};
|
|
5844
6470
|
var extractComponentTags = (body) => {
|
|
5845
6471
|
const tags = new Set;
|
|
5846
|
-
let
|
|
6472
|
+
let fence = null;
|
|
5847
6473
|
for (const line of body.split(`
|
|
5848
6474
|
`)) {
|
|
5849
|
-
|
|
6475
|
+
fence = scanTagLine(line, fence, tags);
|
|
5850
6476
|
}
|
|
5851
6477
|
return [...tags];
|
|
5852
6478
|
};
|
|
6479
|
+
var strippedLineOffset = (raw, body) => raw ? Math.max(0, raw.split(`
|
|
6480
|
+
`).length - body.split(`
|
|
6481
|
+
`).length) : 0;
|
|
5853
6482
|
var deriveTitle = (meta, headings, id2) => {
|
|
5854
6483
|
if (meta.title) {
|
|
5855
6484
|
return meta.title;
|
|
@@ -5859,7 +6488,7 @@ var deriveTitle = (meta, headings, id2) => {
|
|
|
5859
6488
|
return firstHeading.text;
|
|
5860
6489
|
}
|
|
5861
6490
|
const base = id2.split("/").pop() ?? id2;
|
|
5862
|
-
return titleCase(stripNumericPrefix(base.replace(
|
|
6491
|
+
return titleCase(stripNumericPrefix(base.replace(extname5(base), "")));
|
|
5863
6492
|
};
|
|
5864
6493
|
var trimSlashes = (value) => value.replaceAll(/^\/+|\/+$/gu, "");
|
|
5865
6494
|
var withPrefix = (prefix, path) => {
|
|
@@ -5910,7 +6539,7 @@ var normalizeEntry2 = (entry, ctx) => {
|
|
|
5910
6539
|
headings,
|
|
5911
6540
|
id: `${ctx.source.name}:${entry.ref}`,
|
|
5912
6541
|
lastModified: meta.lastModified ?? entry.lastModified,
|
|
5913
|
-
links: extractLinks(entry.body.text),
|
|
6542
|
+
links: extractLinks(entry.body.text, strippedLineOffset(entry.raw, entry.body.text)),
|
|
5914
6543
|
meta,
|
|
5915
6544
|
navPath,
|
|
5916
6545
|
segments,
|
|
@@ -6129,12 +6758,9 @@ ${nested}`;
|
|
|
6129
6758
|
return Object.values(page.properties).find((p) => p.type === "title");
|
|
6130
6759
|
};
|
|
6131
6760
|
const isDraft = (page) => {
|
|
6132
|
-
if (!options.publishedValue) {
|
|
6133
|
-
return false;
|
|
6134
|
-
}
|
|
6135
6761
|
const prop = page.properties[props.status ?? "Status"];
|
|
6136
6762
|
const status = prop?.status?.name ?? prop?.select?.name;
|
|
6137
|
-
return Boolean(status && status !== options.publishedValue);
|
|
6763
|
+
return Boolean(status && status !== (options.publishedValue ?? "Published"));
|
|
6138
6764
|
};
|
|
6139
6765
|
const orderOf = (page) => {
|
|
6140
6766
|
const order = page.properties[props.order ?? "Order"]?.number;
|
|
@@ -6267,7 +6893,7 @@ var renderSpan = (span, defs) => {
|
|
|
6267
6893
|
}
|
|
6268
6894
|
return link?.href ? `[${text}](${link.href})` : text;
|
|
6269
6895
|
};
|
|
6270
|
-
var
|
|
6896
|
+
var renderChildren2 = (block) => {
|
|
6271
6897
|
const defs = new Map((block.markDefs ?? []).map((def) => [def._key, def]));
|
|
6272
6898
|
return (block.children ?? []).map((span) => renderSpan(span, defs)).join("");
|
|
6273
6899
|
};
|
|
@@ -6284,7 +6910,7 @@ var renderBlock = (block, options) => {
|
|
|
6284
6910
|
if (block._type !== "block") {
|
|
6285
6911
|
return `<!-- unsupported Portable Text block: ${block._type} -->`;
|
|
6286
6912
|
}
|
|
6287
|
-
const inline =
|
|
6913
|
+
const inline = renderChildren2(block);
|
|
6288
6914
|
if (block.listItem) {
|
|
6289
6915
|
const indent = " ".repeat(Math.max(0, (block.level ?? 1) - 1));
|
|
6290
6916
|
const marker = block.listItem === "number" ? "1." : "-";
|
|
@@ -6708,7 +7334,7 @@ import { isAbsolute as isAbsolute8, join as join19 } from "pathe";
|
|
|
6708
7334
|
// src/astro/templates.ts
|
|
6709
7335
|
import { existsSync as existsSync11, readFileSync as readFileSync5 } from "node:fs";
|
|
6710
7336
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
6711
|
-
import { dirname as dirname7, isAbsolute as isAbsolute7, join as join18, relative as
|
|
7337
|
+
import { dirname as dirname7, isAbsolute as isAbsolute7, join as join18, relative as relative6 } from "pathe";
|
|
6712
7338
|
var WORKSPACE_MARKERS = [
|
|
6713
7339
|
".git",
|
|
6714
7340
|
"bun.lock",
|
|
@@ -6852,11 +7478,12 @@ var astroConfigTemplate = (options) => {
|
|
|
6852
7478
|
const twoslashImport = `import { transformerTwoslash } from "@shikijs/twoslash";
|
|
6853
7479
|
`;
|
|
6854
7480
|
const twoslashTransformer = "transformerTwoslash({ explicitTrigger: true }), ";
|
|
6855
|
-
const
|
|
7481
|
+
const deployBase = normalizeBasePath(deployment.base);
|
|
6856
7482
|
const integrations = [
|
|
6857
7483
|
`mdx({ processor: blumeMdxProcessor(${JSON.stringify({
|
|
6858
|
-
basePath:
|
|
7484
|
+
basePath: config.basePath,
|
|
6859
7485
|
codeThemes: config.markdown.codeBlocks.theme,
|
|
7486
|
+
deployBase,
|
|
6860
7487
|
headingAnchors: config.markdown.headingAnchors
|
|
6861
7488
|
})}) })`
|
|
6862
7489
|
];
|
|
@@ -6885,8 +7512,9 @@ export default defineConfig({
|
|
|
6885
7512
|
integrations: [${integrations.join(", ")}],
|
|
6886
7513
|
markdown: {
|
|
6887
7514
|
processor: blumeMarkdownProcessor(${JSON.stringify({
|
|
6888
|
-
basePath:
|
|
7515
|
+
basePath: config.basePath,
|
|
6889
7516
|
codeThemes: config.markdown.codeBlocks.theme,
|
|
7517
|
+
deployBase,
|
|
6890
7518
|
headingAnchors: config.markdown.headingAnchors
|
|
6891
7519
|
})}),
|
|
6892
7520
|
shikiConfig: {
|
|
@@ -6953,7 +7581,7 @@ var contentConfigTemplate = (options) => {
|
|
|
6953
7581
|
const collectionBase = options.collection?.base ?? context.contentRoot;
|
|
6954
7582
|
const includeGlobs = options.collection?.include ?? config.content.include;
|
|
6955
7583
|
const excludeGlobs = options.collection?.exclude ?? config.content.exclude;
|
|
6956
|
-
const outDirRel =
|
|
7584
|
+
const outDirRel = relative6(collectionBase, context.outDir);
|
|
6957
7585
|
const outDirIgnore = outDirRel && !outDirRel.startsWith("..") && !isAbsolute7(outDirRel) ? [`!${outDirRel}/**`] : [];
|
|
6958
7586
|
const filesystem = options.filesystem ?? true;
|
|
6959
7587
|
const docsPattern = filesystem ? [
|
|
@@ -7041,6 +7669,21 @@ const ground = createAskContext(askData);
|
|
|
7041
7669
|
content: m.content,
|
|
7042
7670
|
role: m.role,
|
|
7043
7671
|
}));`;
|
|
7672
|
+
const keyCheck = backend.kind === "gateway" ? ` // The AI Gateway authenticates with an API key or Vercel's OIDC token.
|
|
7673
|
+
if (!(process.env.AI_GATEWAY_API_KEY || process.env.VERCEL_OIDC_TOKEN)) {
|
|
7674
|
+
return new Response(
|
|
7675
|
+
"Ask AI is not configured: set AI_GATEWAY_API_KEY (or deploy on Vercel with OIDC).",
|
|
7676
|
+
{ status: 500 }
|
|
7677
|
+
);
|
|
7678
|
+
}` : ` if (!process.env[${JSON.stringify(backend.apiKeyEnv)}]) {
|
|
7679
|
+
return new Response(
|
|
7680
|
+
${JSON.stringify(`Ask AI is not configured: set ${backend.apiKeyEnv}.`)},
|
|
7681
|
+
{ status: 500 }
|
|
7682
|
+
);
|
|
7683
|
+
}`;
|
|
7684
|
+
const onError = ` onError({ error }) {
|
|
7685
|
+
console.error("Ask AI provider error:", error);
|
|
7686
|
+
},`;
|
|
7044
7687
|
const stream = grounded ? ` const system =
|
|
7045
7688
|
(await ground(messages, body.page)) ??
|
|
7046
7689
|
"You are a helpful documentation assistant. Answer using the project's documentation.";
|
|
@@ -7048,14 +7691,17 @@ const ground = createAskContext(askData);
|
|
|
7048
7691
|
model: ${modelExpr},
|
|
7049
7692
|
system,
|
|
7050
7693
|
messages,
|
|
7694
|
+
${onError}
|
|
7051
7695
|
});` : ` const result = streamText({
|
|
7052
7696
|
model: ${modelExpr},
|
|
7053
7697
|
system:
|
|
7054
7698
|
"You are a helpful documentation assistant. Answer using the project's documentation.",
|
|
7055
7699
|
messages,
|
|
7700
|
+
${onError}
|
|
7056
7701
|
});`;
|
|
7057
7702
|
const handler = `export const POST: APIRoute = async ({ request }) => {
|
|
7058
7703
|
${validate}
|
|
7704
|
+
${keyCheck}
|
|
7059
7705
|
try {
|
|
7060
7706
|
${stream}
|
|
7061
7707
|
return result.toTextStreamResponse();
|
|
@@ -7182,7 +7828,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
7182
7828
|
});
|
|
7183
7829
|
};
|
|
7184
7830
|
`;
|
|
7185
|
-
var rawMarkdownEndpointTemplate = () => `// Generated by Blume. Do not edit.
|
|
7831
|
+
var rawMarkdownEndpointTemplate = (kind) => `// Generated by Blume. Do not edit.
|
|
7186
7832
|
import raw from "../generated/raw-markdown.json";
|
|
7187
7833
|
|
|
7188
7834
|
export const prerender = true;
|
|
@@ -7195,7 +7841,8 @@ export function getStaticPaths() {
|
|
|
7195
7841
|
}
|
|
7196
7842
|
|
|
7197
7843
|
export function GET({ props }) {
|
|
7198
|
-
|
|
7844
|
+
const entry = raw[props.route];
|
|
7845
|
+
return new Response(entry ? ${kind === "md" ? "(entry.md ?? entry.mdx)" : "entry.mdx"} : "", {
|
|
7199
7846
|
headers: { "Content-Type": "text/markdown; charset=utf-8" },
|
|
7200
7847
|
});
|
|
7201
7848
|
}
|
|
@@ -7335,6 +7982,7 @@ const configuration = ${JSON.stringify(options.configuration, null, 2)};
|
|
|
7335
7982
|
searchEnabled={data.config.search.enabled}
|
|
7336
7983
|
site={{ title: data.config.title, description: data.config.description }}
|
|
7337
7984
|
themeMode={data.config.theme.mode}
|
|
7985
|
+
ui={data.ui}
|
|
7338
7986
|
>
|
|
7339
7987
|
<ScalarComponent configuration={configuration} renderMode="client" />
|
|
7340
7988
|
</ReferenceLayout>
|
|
@@ -7615,6 +8263,7 @@ var changelogIndexTemplate = (options) => {
|
|
|
7615
8263
|
import { getCollection, render } from "astro:content";
|
|
7616
8264
|
import RootLayout from "blume/components/layout/RootLayout.astro";
|
|
7617
8265
|
import Update from "blume/components/content/Update.astro";
|
|
8266
|
+
import { withBase } from "blume/components/islands/base-path.ts";
|
|
7618
8267
|
import { resolveSlot } from "blume/components/layout/overrides.ts";
|
|
7619
8268
|
import { layoutOverrides } from "../generated/components.ts";
|
|
7620
8269
|
${askImport}import data from "../generated/data.json";
|
|
@@ -7695,6 +8344,20 @@ const items = await Promise.all(
|
|
|
7695
8344
|
})
|
|
7696
8345
|
);
|
|
7697
8346
|
|
|
8347
|
+
// Repeated labels slug to the same id (e.g. two entries with neither a title
|
|
8348
|
+
// nor a version both falling back to "update"); suffix the later ones -2, -3,
|
|
8349
|
+
// ... so every heading deep-links to its own entry. The first keeps the plain
|
|
8350
|
+
// slug, and the rendered ids stay in lockstep with the \`headings\` list below.
|
|
8351
|
+
const seenIds = new Set();
|
|
8352
|
+
for (const item of items) {
|
|
8353
|
+
let uniqueId = item.id;
|
|
8354
|
+
for (let n = 2; seenIds.has(uniqueId); n += 1) {
|
|
8355
|
+
uniqueId = item.id + "-" + n;
|
|
8356
|
+
}
|
|
8357
|
+
seenIds.add(uniqueId);
|
|
8358
|
+
item.id = uniqueId;
|
|
8359
|
+
}
|
|
8360
|
+
|
|
7698
8361
|
// A changelog is semver-paginated only when every visible release parses as
|
|
7699
8362
|
// semver and they span more than one major line. Older majors then collapse
|
|
7700
8363
|
// into groups the reader reveals one at a time; otherwise the timeline is flat.
|
|
@@ -7715,7 +8378,20 @@ const headings = items.map((item) => ({
|
|
|
7715
8378
|
}));
|
|
7716
8379
|
|
|
7717
8380
|
const base = data.config.site ? data.config.site.replace(/\\/$/, "") : null;
|
|
7718
|
-
|
|
8381
|
+
// The canonical URL carries the deployment base (the page is served under it),
|
|
8382
|
+
// matching how the catch-all canonicalizes via \`withBase(route)\`.
|
|
8383
|
+
const basedRoute = withBase("/changelog");
|
|
8384
|
+
const canonical = base ? base + basedRoute : null;
|
|
8385
|
+
|
|
8386
|
+
// The changelog is an unlocalized route, so its chrome renders in the default
|
|
8387
|
+
// locale's dictionary and direction (\`data.ui\` is the default locale's resolved
|
|
8388
|
+
// dictionary), mirroring the catch-all's locale wiring.
|
|
8389
|
+
const i18n = data.config.i18n;
|
|
8390
|
+
const localeMeta = i18n
|
|
8391
|
+
? i18n.locales.find((l) => l.code === i18n.defaultLocale)
|
|
8392
|
+
: null;
|
|
8393
|
+
const dir = localeMeta?.dir ?? "ltr";
|
|
8394
|
+
const htmlLang = i18n ? i18n.defaultLocale : "en";
|
|
7719
8395
|
|
|
7720
8396
|
const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
7721
8397
|
---
|
|
@@ -7732,7 +8408,10 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
7732
8408
|
imageZoom={data.config.imageZoom}
|
|
7733
8409
|
codeWrap={data.config.codeWrap}
|
|
7734
8410
|
navigation={data.navigation}
|
|
7735
|
-
|
|
8411
|
+
locale={htmlLang}
|
|
8412
|
+
dir={dir}
|
|
8413
|
+
ui={data.ui}
|
|
8414
|
+
page={{
|
|
7736
8415
|
title: data.config.title + " changelog",
|
|
7737
8416
|
description: "Product updates and release notes.",
|
|
7738
8417
|
route: "/changelog",
|
|
@@ -7759,7 +8438,10 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
7759
8438
|
items.length === 0 ? (
|
|
7760
8439
|
<p>No changelog entries yet.</p>
|
|
7761
8440
|
) : paginate ? (
|
|
7762
|
-
<blume-changelog
|
|
8441
|
+
<blume-changelog
|
|
8442
|
+
class="not-prose mt-8 block"
|
|
8443
|
+
data-i18n-more={data.ui.changelog?.showReleases}
|
|
8444
|
+
>
|
|
7763
8445
|
{majorGroups[0].items.map(({ Content, href, id, label, date, tags }) => (
|
|
7764
8446
|
<Update description={date} href={href} id={id} label={label} tags={tags}>
|
|
7765
8447
|
<Content />
|
|
@@ -7808,11 +8490,22 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
7808
8490
|
var notFoundPageTemplate = () => `---
|
|
7809
8491
|
// Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
|
|
7810
8492
|
import PageLayout from "blume/components/layout/PageLayout.astro";
|
|
8493
|
+
import { withBase } from "blume/components/islands/base-path.ts";
|
|
7811
8494
|
import data from "../generated/data.json";
|
|
7812
8495
|
|
|
7813
8496
|
export const prerender = true;
|
|
7814
8497
|
|
|
7815
8498
|
const nf = data.ui.notFound;
|
|
8499
|
+
|
|
8500
|
+
// The 404 page is an unlocalized route, so its chrome renders in the default
|
|
8501
|
+
// locale's dictionary and direction (\`data.ui\` is the default locale's resolved
|
|
8502
|
+
// dictionary), mirroring the catch-all's locale wiring.
|
|
8503
|
+
const i18n = data.config.i18n;
|
|
8504
|
+
const localeMeta = i18n
|
|
8505
|
+
? i18n.locales.find((l) => l.code === i18n.defaultLocale)
|
|
8506
|
+
: null;
|
|
8507
|
+
const dir = localeMeta?.dir ?? "ltr";
|
|
8508
|
+
const htmlLang = i18n ? i18n.defaultLocale : "en";
|
|
7816
8509
|
---
|
|
7817
8510
|
|
|
7818
8511
|
<PageLayout
|
|
@@ -7827,6 +8520,8 @@ const nf = data.ui.notFound;
|
|
|
7827
8520
|
themeMode={data.config.theme.mode}
|
|
7828
8521
|
fontCssVars={data.fontCssVars}
|
|
7829
8522
|
searchEnabled={data.config.search.enabled}
|
|
8523
|
+
locale={htmlLang}
|
|
8524
|
+
dir={dir}
|
|
7830
8525
|
ui={data.ui}
|
|
7831
8526
|
noindex={true}
|
|
7832
8527
|
>
|
|
@@ -7838,7 +8533,7 @@ const nf = data.ui.notFound;
|
|
|
7838
8533
|
<p class="text-muted-foreground">{nf.description}</p>
|
|
7839
8534
|
<a
|
|
7840
8535
|
class="mt-2 rounded-md bg-accent px-4 py-2 text-sm font-medium text-accent-foreground"
|
|
7841
|
-
href="/">{nf.home}</a
|
|
8536
|
+
href={withBase("/")}>{nf.home}</a
|
|
7842
8537
|
>
|
|
7843
8538
|
</div>
|
|
7844
8539
|
</PageLayout>
|
|
@@ -8948,7 +9643,7 @@ ${clause}
|
|
|
8948
9643
|
<Component ${directiveFor(override)} {...Astro.props}><slot /></Component>
|
|
8949
9644
|
`;
|
|
8950
9645
|
};
|
|
8951
|
-
var sanitize = (value) => value.replaceAll(/[^A-Za-z0-9]/gu,
|
|
9646
|
+
var sanitize = (value) => value.replaceAll(/[^A-Za-z0-9]/gu, (char) => `_${(char.codePointAt(0) ?? 0).toString(16)}_`);
|
|
8952
9647
|
var planComponentSlots = (componentsFile, analysis) => {
|
|
8953
9648
|
const frameworks = new Set;
|
|
8954
9649
|
if (!componentsFile) {
|
|
@@ -9011,13 +9706,13 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
|
|
|
9011
9706
|
|
|
9012
9707
|
// src/astro/examples.ts
|
|
9013
9708
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
9014
|
-
import { join as join21, relative as
|
|
9015
|
-
import { glob as
|
|
9709
|
+
import { join as join21, relative as relative7 } from "pathe";
|
|
9710
|
+
import { glob as glob4 } from "tinyglobby";
|
|
9016
9711
|
|
|
9017
9712
|
// src/astro/islands.ts
|
|
9018
9713
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
9019
9714
|
import { basename, join as join20 } from "pathe";
|
|
9020
|
-
import { glob as
|
|
9715
|
+
import { glob as glob3 } from "tinyglobby";
|
|
9021
9716
|
var DEFAULT_CLIENT = "visible";
|
|
9022
9717
|
var VALID_MODES = new Set([
|
|
9023
9718
|
"idle",
|
|
@@ -9046,7 +9741,7 @@ var readClientMode = (source, file, warnings) => {
|
|
|
9046
9741
|
};
|
|
9047
9742
|
var discoverIslands = async (root) => {
|
|
9048
9743
|
const dir = join20(root, "islands");
|
|
9049
|
-
const matches = await
|
|
9744
|
+
const matches = await glob3(["**/*.{jsx,svelte,tsx,vue}"], {
|
|
9050
9745
|
absolute: true,
|
|
9051
9746
|
cwd: dir,
|
|
9052
9747
|
onlyFiles: true
|
|
@@ -9109,7 +9804,7 @@ var splitGlobBase = (pattern) => {
|
|
|
9109
9804
|
var discoverExamples = async (root, pattern = "examples") => {
|
|
9110
9805
|
const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
|
|
9111
9806
|
const dir = join21(root, base);
|
|
9112
|
-
const matches = await
|
|
9807
|
+
const matches = await glob4([rest], {
|
|
9113
9808
|
absolute: true,
|
|
9114
9809
|
cwd: dir,
|
|
9115
9810
|
onlyFiles: true
|
|
@@ -9125,7 +9820,7 @@ var discoverExamples = async (root, pattern = "examples") => {
|
|
|
9125
9820
|
if (!(ext && framework)) {
|
|
9126
9821
|
return;
|
|
9127
9822
|
}
|
|
9128
|
-
const path =
|
|
9823
|
+
const path = relative7(dir, file).slice(0, -(ext.length + 1));
|
|
9129
9824
|
const existing = seen.get(path);
|
|
9130
9825
|
if (existing) {
|
|
9131
9826
|
warnings.push(`Two examples both resolve to "${path}" ("${existing}" and "${file}"); ignoring the second. Give them distinct paths.`);
|
|
@@ -9147,52 +9842,6 @@ var discoverExamples = async (root, pattern = "examples") => {
|
|
|
9147
9842
|
return { examples, warnings };
|
|
9148
9843
|
};
|
|
9149
9844
|
|
|
9150
|
-
// src/astro/pages.ts
|
|
9151
|
-
import { extname as extname5, relative as relative7 } from "pathe";
|
|
9152
|
-
import { glob as glob4 } from "tinyglobby";
|
|
9153
|
-
var discoverPages = async (pagesRoot) => {
|
|
9154
|
-
const files = await glob4(["**/*.astro"], {
|
|
9155
|
-
absolute: true,
|
|
9156
|
-
cwd: pagesRoot,
|
|
9157
|
-
onlyFiles: true
|
|
9158
|
-
});
|
|
9159
|
-
files.sort();
|
|
9160
|
-
return files.map((file) => {
|
|
9161
|
-
const rel = relative7(pagesRoot, file);
|
|
9162
|
-
const withoutExt = rel.slice(0, rel.length - extname5(rel).length);
|
|
9163
|
-
const parts = withoutExt.split("/");
|
|
9164
|
-
if (parts.at(-1) === "index") {
|
|
9165
|
-
parts.pop();
|
|
9166
|
-
}
|
|
9167
|
-
const pattern = parts.length === 0 ? "/" : `/${parts.join("/")}`;
|
|
9168
|
-
return { entrypoint: file, pattern };
|
|
9169
|
-
});
|
|
9170
|
-
};
|
|
9171
|
-
var routeIsTaken = (pages, contentPages, route) => pages.some((page) => page.pattern === route) || contentPages.some((page) => page.route === route);
|
|
9172
|
-
var PRIVATE_SEGMENT = /^[._]/u;
|
|
9173
|
-
var humanizeSegment = (segment) => segment.split(/[-_]/u).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
9174
|
-
var customOgRoutes = (pages, siteTitle) => {
|
|
9175
|
-
const seen = new Set;
|
|
9176
|
-
const routes = [];
|
|
9177
|
-
const collectRoute = (pattern) => {
|
|
9178
|
-
const segments = pattern.split("/").filter(Boolean);
|
|
9179
|
-
if (segments.some((part) => PRIVATE_SEGMENT.test(part) || part.includes("["))) {
|
|
9180
|
-
return;
|
|
9181
|
-
}
|
|
9182
|
-
const slug = segments.length === 0 ? "index" : segments.join("/");
|
|
9183
|
-
if (seen.has(slug)) {
|
|
9184
|
-
return;
|
|
9185
|
-
}
|
|
9186
|
-
seen.add(slug);
|
|
9187
|
-
const last = segments.at(-1);
|
|
9188
|
-
routes.push({ slug, title: last ? humanizeSegment(last) : siteTitle });
|
|
9189
|
-
};
|
|
9190
|
-
for (const { pattern } of pages) {
|
|
9191
|
-
collectRoute(pattern);
|
|
9192
|
-
}
|
|
9193
|
-
return routes;
|
|
9194
|
-
};
|
|
9195
|
-
|
|
9196
9845
|
// src/astro/generate.ts
|
|
9197
9846
|
var BLUME_SRC = join22(packageRoot(), "src");
|
|
9198
9847
|
var canResolveFrom = (fromDir, spec) => {
|
|
@@ -9281,6 +9930,10 @@ var ISLAND_FRAMEWORK_DEPS = {
|
|
|
9281
9930
|
svelte: "@astrojs/svelte",
|
|
9282
9931
|
vue: "@astrojs/vue"
|
|
9283
9932
|
};
|
|
9933
|
+
var DEPLOYMENT_ADAPTER_DEPS = {
|
|
9934
|
+
cloudflare: "@astrojs/cloudflare",
|
|
9935
|
+
netlify: "@astrojs/netlify"
|
|
9936
|
+
};
|
|
9284
9937
|
var islandFrameworkWarnings = (frameworks, root) => {
|
|
9285
9938
|
const warnings = [];
|
|
9286
9939
|
for (const framework of frameworks) {
|
|
@@ -9291,6 +9944,15 @@ var islandFrameworkWarnings = (frameworks, root) => {
|
|
|
9291
9944
|
}
|
|
9292
9945
|
return warnings;
|
|
9293
9946
|
};
|
|
9947
|
+
var deploymentAdapterWarnings = (deployment, root) => {
|
|
9948
|
+
const dep = deployment.output === "server" && deployment.adapter ? DEPLOYMENT_ADAPTER_DEPS[deployment.adapter] : undefined;
|
|
9949
|
+
if (dep && !(canResolveFrom(root, dep) || canResolveFrom(packageRoot(), dep))) {
|
|
9950
|
+
return [
|
|
9951
|
+
`Deployment adapter "${deployment.adapter}" needs "${dep}", which isn't installed. Run \`npm install ${dep}\` (or your package manager's equivalent).`
|
|
9952
|
+
];
|
|
9953
|
+
}
|
|
9954
|
+
return [];
|
|
9955
|
+
};
|
|
9294
9956
|
var examplesCssFile = (root, config) => config.examples.css ? join22(root, config.examples.css) : null;
|
|
9295
9957
|
var writeExamplesPreview = async (options) => {
|
|
9296
9958
|
const { config, hasExamples, root, srcDir, write } = options;
|
|
@@ -9320,14 +9982,15 @@ var detectNeedsReact = async (root) => {
|
|
|
9320
9982
|
});
|
|
9321
9983
|
return matches.length > 0;
|
|
9322
9984
|
};
|
|
9323
|
-
var
|
|
9324
|
-
|
|
9985
|
+
var containsMath = (content) => content.includes("$$") || content.includes("<Math");
|
|
9986
|
+
var detectUsesMath = async (root, staged = []) => {
|
|
9987
|
+
const files = await glob5(["**/*.{md,mdx}"], {
|
|
9325
9988
|
cwd: root,
|
|
9326
9989
|
ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
|
|
9327
9990
|
onlyFiles: true
|
|
9328
9991
|
});
|
|
9329
9992
|
const contents = await Promise.all(files.map((file) => readOptional(join22(root, file))));
|
|
9330
|
-
return contents.some(
|
|
9993
|
+
return [...contents, ...staged].some(containsMath);
|
|
9331
9994
|
};
|
|
9332
9995
|
var writeIfChanged = async (path, content) => {
|
|
9333
9996
|
let existing = null;
|
|
@@ -9518,6 +10181,7 @@ var buildRuntimeData = (project) => {
|
|
|
9518
10181
|
appleIcon: resolveAppleIcon(project),
|
|
9519
10182
|
ask: config.ai.ask?.enabled ? { suggestions: config.ai.ask.suggestions } : null,
|
|
9520
10183
|
banner: resolveBanner(config),
|
|
10184
|
+
basePath: config.basePath,
|
|
9521
10185
|
codeThemes: config.markdown.codeBlocks.theme,
|
|
9522
10186
|
codeWrap: config.markdown.code.wrap,
|
|
9523
10187
|
description: config.description,
|
|
@@ -9620,6 +10284,7 @@ var writeMcpFiles = async (project, plan, write) => {
|
|
|
9620
10284
|
}
|
|
9621
10285
|
const data = await buildMcpData(project);
|
|
9622
10286
|
const discoveryInput = {
|
|
10287
|
+
base: data.base,
|
|
9623
10288
|
name: data.name,
|
|
9624
10289
|
route: plan.route,
|
|
9625
10290
|
site: data.site,
|
|
@@ -9651,11 +10316,6 @@ var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
|
9651
10316
|
}
|
|
9652
10317
|
await write(join22(srcDir, "pages", "404.astro"), notFoundPageTemplate());
|
|
9653
10318
|
};
|
|
9654
|
-
var shouldGenerateChangelog = (project, userPages) => {
|
|
9655
|
-
const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
|
|
9656
|
-
const hasChangelogSource = (project.config.content.sources ?? []).some((source) => source.type === "github-releases");
|
|
9657
|
-
return (hasChangelog || hasChangelogSource) && !routeIsTaken(userPages, project.graph.pages, "/changelog");
|
|
9658
|
-
};
|
|
9659
10319
|
var buildComponentSlots = async (componentsFile) => {
|
|
9660
10320
|
const analysis = componentsFile ? analyzeComponentOverrides(await readFile11(componentsFile, "utf-8"), componentsFile) : null;
|
|
9661
10321
|
return {
|
|
@@ -9683,6 +10343,7 @@ var generateRuntime = async (project) => {
|
|
|
9683
10343
|
const askEnabled = config.ai.ask?.enabled ?? false;
|
|
9684
10344
|
const exportPdf = config.export.pdf;
|
|
9685
10345
|
const exportEpub = config.export.epub;
|
|
10346
|
+
const staged = collectStaged(project);
|
|
9686
10347
|
const [
|
|
9687
10348
|
pages,
|
|
9688
10349
|
detectedReact,
|
|
@@ -9695,7 +10356,7 @@ var generateRuntime = async (project) => {
|
|
|
9695
10356
|
] = await Promise.all([
|
|
9696
10357
|
context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),
|
|
9697
10358
|
detectNeedsReact(context.root),
|
|
9698
|
-
detectUsesMath(context.root),
|
|
10359
|
+
detectUsesMath(context.root, staged.values()),
|
|
9699
10360
|
readOptional(context.themeFile),
|
|
9700
10361
|
readOptional(examplesCssFile(context.root, config)),
|
|
9701
10362
|
discoverIslands(context.root),
|
|
@@ -9719,7 +10380,6 @@ var generateRuntime = async (project) => {
|
|
|
9719
10380
|
const ogRoutes = customOgRoutes(pages, config.title);
|
|
9720
10381
|
const mcp = planMcp(project, srcDir, pages);
|
|
9721
10382
|
pages.push(...mcp.discoveryPages);
|
|
9722
|
-
const staged = collectStaged(project);
|
|
9723
10383
|
const hasStaged = staged.size > 0;
|
|
9724
10384
|
const hasFilesystemSource = project.sources.some((source) => !source.staged);
|
|
9725
10385
|
const [structural] = await Promise.all([
|
|
@@ -9785,7 +10445,7 @@ var generateRuntime = async (project) => {
|
|
|
9785
10445
|
if (config.seo.og.enabled) {
|
|
9786
10446
|
await write(join22(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
|
|
9787
10447
|
}
|
|
9788
|
-
if (
|
|
10448
|
+
if (hasGeneratedChangelog(project, pages)) {
|
|
9789
10449
|
await write(join22(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
|
|
9790
10450
|
askEnabled,
|
|
9791
10451
|
exportEpub,
|
|
@@ -9818,8 +10478,8 @@ var generateRuntime = async (project) => {
|
|
|
9818
10478
|
await Promise.all([
|
|
9819
10479
|
write(join22(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
|
|
9820
10480
|
`),
|
|
9821
|
-
write(join22(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate()),
|
|
9822
|
-
write(join22(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate())
|
|
10481
|
+
write(join22(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate("md")),
|
|
10482
|
+
write(join22(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate("mdx"))
|
|
9823
10483
|
]);
|
|
9824
10484
|
const feeds = buildRssFeeds(project);
|
|
9825
10485
|
if (feeds.length > 0) {
|
|
@@ -9844,7 +10504,7 @@ var generateRuntime = async (project) => {
|
|
|
9844
10504
|
...pages.map((page) => page.pattern),
|
|
9845
10505
|
...referenceTabs(config).map((tab) => tab.path)
|
|
9846
10506
|
]);
|
|
9847
|
-
if (
|
|
10507
|
+
if (hasGeneratedChangelog(project, pages)) {
|
|
9848
10508
|
navTargetRoutes.add("/changelog");
|
|
9849
10509
|
}
|
|
9850
10510
|
warnings.push(...validateNavTargets(project.graph.navigation, navTargetRoutes).map((diagnostic) => diagnostic.suggestion ? `${diagnostic.message} ${diagnostic.suggestion}` : diagnostic.message));
|
|
@@ -9858,7 +10518,7 @@ var generateRuntime = async (project) => {
|
|
|
9858
10518
|
warnings.push(`Search provider "${config.search.provider}" needs "${dep}", which isn't installed. Run \`npm install ${dep}\` (or your package manager's equivalent).`);
|
|
9859
10519
|
}
|
|
9860
10520
|
}
|
|
9861
|
-
warnings.push(...islandFrameworkWarnings(frameworks, context.root));
|
|
10521
|
+
warnings.push(...deploymentAdapterWarnings(config.deployment, context.root), ...islandFrameworkWarnings(frameworks, context.root));
|
|
9862
10522
|
if (hasScalarReferences(config)) {
|
|
9863
10523
|
const references = await buildReferenceFiles({
|
|
9864
10524
|
config,
|
|
@@ -10005,6 +10665,7 @@ var segmentKey = (raw) => {
|
|
|
10005
10665
|
const group = raw.match(GROUP_FOLDER2)?.groups?.label;
|
|
10006
10666
|
return (group ?? raw).replace(NUMERIC_PREFIX2, "");
|
|
10007
10667
|
};
|
|
10668
|
+
var isIndexStem = (stem) => stem.replace(NUMERIC_PREFIX2, "") === "index";
|
|
10008
10669
|
var createGroup = (key, path, label, order) => ({
|
|
10009
10670
|
children: [],
|
|
10010
10671
|
index: new Map,
|
|
@@ -10029,7 +10690,7 @@ var pageOrder = (page, filename) => {
|
|
|
10029
10690
|
if (page.meta.sidebar.order !== undefined) {
|
|
10030
10691
|
return page.meta.sidebar.order;
|
|
10031
10692
|
}
|
|
10032
|
-
if (filename.replace(extname6(filename), "")
|
|
10693
|
+
if (isIndexStem(filename.replace(extname6(filename), ""))) {
|
|
10033
10694
|
return Number.NEGATIVE_INFINITY;
|
|
10034
10695
|
}
|
|
10035
10696
|
if (page.contentType === "changelog") {
|
|
@@ -10138,7 +10799,7 @@ var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, display
|
|
|
10138
10799
|
const stem = filename.replace(extname6(filename), "");
|
|
10139
10800
|
const dirs = parts.slice(0, -1);
|
|
10140
10801
|
const routeSegments = page.route.split("/").filter(Boolean);
|
|
10141
|
-
const folderParts = stem
|
|
10802
|
+
const folderParts = isIndexStem(stem) ? routeSegments : routeSegments.slice(0, -1);
|
|
10142
10803
|
const routeDirCount = dirs.filter((dir) => !GROUP_FOLDER2.test(dir)).length;
|
|
10143
10804
|
const offset = Math.max(0, folderParts.length - routeDirCount);
|
|
10144
10805
|
let parent = root;
|
|
@@ -10266,7 +10927,7 @@ var buildNavigation = (pages, options) => {
|
|
|
10266
10927
|
...selector,
|
|
10267
10928
|
items: selector.items.map(rebasePath)
|
|
10268
10929
|
})) : options.selectors ?? [];
|
|
10269
|
-
const
|
|
10930
|
+
const tabs2 = basePath ? (options.tabs ?? []).map((tab) => ({
|
|
10270
10931
|
...tab,
|
|
10271
10932
|
items: tab.items?.map(rebasePath),
|
|
10272
10933
|
path: withBasePath(basePath, tab.path)
|
|
@@ -10288,14 +10949,15 @@ var buildNavigation = (pages, options) => {
|
|
|
10288
10949
|
featured,
|
|
10289
10950
|
selectors,
|
|
10290
10951
|
sidebar: buildConfigSidebar(options.sidebar, byRoute, display, basePath),
|
|
10291
|
-
tabs
|
|
10952
|
+
tabs: tabs2
|
|
10292
10953
|
};
|
|
10293
10954
|
}
|
|
10955
|
+
const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
|
|
10294
10956
|
return {
|
|
10295
10957
|
featured,
|
|
10296
10958
|
selectors,
|
|
10297
|
-
sidebar: buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, display, new Set(
|
|
10298
|
-
tabs
|
|
10959
|
+
sidebar: buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, display, new Set(tabs2.flatMap((tab) => tab.path === rootTabPath ? [] : [tab.path]))),
|
|
10960
|
+
tabs: tabs2
|
|
10299
10961
|
};
|
|
10300
10962
|
};
|
|
10301
10963
|
|
|
@@ -10337,9 +10999,14 @@ var localePagesFor = (code, real, fallback, fallbackByKey, i18n, basePath) => {
|
|
|
10337
10999
|
return [...real, ...filled];
|
|
10338
11000
|
};
|
|
10339
11001
|
var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n) => {
|
|
10340
|
-
const
|
|
11002
|
+
const localizePath = (path) => path.startsWith("/") ? localizeRoute(path, code, i18n) : path;
|
|
11003
|
+
const tabs2 = options.navigation.tabs?.map((tab) => ({
|
|
10341
11004
|
...tab,
|
|
10342
|
-
|
|
11005
|
+
items: tab.items?.map((item) => ({
|
|
11006
|
+
...item,
|
|
11007
|
+
path: localizePath(item.path)
|
|
11008
|
+
})),
|
|
11009
|
+
path: localizePath(tab.path)
|
|
10343
11010
|
}));
|
|
10344
11011
|
const real = pages.filter((page) => page.locale === code);
|
|
10345
11012
|
const localePages = localePagesFor(code, real, fallback, fallbackByKey, i18n, options.basePath ?? "");
|
|
@@ -10348,12 +11015,13 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
|
|
|
10348
11015
|
display: options.navigation.sidebar.display,
|
|
10349
11016
|
featured: options.navigation.featured,
|
|
10350
11017
|
folderMeta: options.folderMeta,
|
|
11018
|
+
localizedRoot: localizeRoute("/", code, i18n),
|
|
10351
11019
|
metaPrefix: i18n.parser === "dir" && code !== i18n.defaultLocale ? code : "",
|
|
10352
11020
|
refByLogical: true,
|
|
10353
11021
|
selectors: options.navigation.selectors,
|
|
10354
11022
|
sharedFolderMeta: options.sharedFolderMeta,
|
|
10355
11023
|
sidebar: options.navigation.sidebar.items,
|
|
10356
|
-
tabs
|
|
11024
|
+
tabs: tabs2
|
|
10357
11025
|
});
|
|
10358
11026
|
};
|
|
10359
11027
|
var buildI18nNavigation = (pages, options, i18n) => {
|
|
@@ -10429,7 +11097,10 @@ var parseGitLog = (output) => {
|
|
|
10429
11097
|
}
|
|
10430
11098
|
return times;
|
|
10431
11099
|
};
|
|
10432
|
-
var gitLastModifiedTimes = (root,
|
|
11100
|
+
var gitLastModifiedTimes = (root, contentRoots, sourcePaths) => {
|
|
11101
|
+
if (sourcePaths.length === 0) {
|
|
11102
|
+
return new Map;
|
|
11103
|
+
}
|
|
10433
11104
|
try {
|
|
10434
11105
|
const gitRoot = execFileSync("git", ["-C", root, "rev-parse", "--show-toplevel"], { encoding: "utf-8" }).trim();
|
|
10435
11106
|
const output = execFileSync("git", [
|
|
@@ -10441,7 +11112,7 @@ var gitLastModifiedTimes = (root, contentRoot, sourcePaths) => {
|
|
|
10441
11112
|
"--format=%x00%cI",
|
|
10442
11113
|
"--name-only",
|
|
10443
11114
|
"--",
|
|
10444
|
-
|
|
11115
|
+
...contentRoots
|
|
10445
11116
|
], { encoding: "utf-8", maxBuffer: 256 * 1024 * 1024 });
|
|
10446
11117
|
const byRepoPath = parseGitLog(output);
|
|
10447
11118
|
const result = new Map;
|
|
@@ -10567,7 +11238,7 @@ var entryIdDiagnostics = (pages, collectionBase) => {
|
|
|
10567
11238
|
file: page.sourcePath,
|
|
10568
11239
|
message: `Content source "${page.source.name}" is rooted outside the docs collection base, so ${page.route} resolves entry id "${entryId}" but the collection would generate "${expected}" — the page would 404 at runtime.`,
|
|
10569
11240
|
severity: "error",
|
|
10570
|
-
suggestion: "
|
|
11241
|
+
suggestion: "Use a single filesystem source (the docs collection roots at it), or root every filesystem source at content.root and partition them with include globs — a root at a subdirectory of content.root still mismatches."
|
|
10571
11242
|
});
|
|
10572
11243
|
}
|
|
10573
11244
|
}
|
|
@@ -10579,7 +11250,7 @@ var scanProject = async (root, options = {}) => {
|
|
|
10579
11250
|
const configResult = await loadConfig(root, {
|
|
10580
11251
|
devServerUrl: options.devServerUrl
|
|
10581
11252
|
});
|
|
10582
|
-
const config = applyConfigOverrides(configResult.config, options.overrides);
|
|
11253
|
+
const config = applyDeploymentEnv(applyConfigOverrides(configResult.config, options.overrides));
|
|
10583
11254
|
const context = resolveProjectContext(root, config, {
|
|
10584
11255
|
runtimeDir: options.runtimeDir
|
|
10585
11256
|
});
|
|
@@ -10620,7 +11291,8 @@ var scanProject = async (root, options = {}) => {
|
|
|
10620
11291
|
const lastModified = resolveLastModifiedConfig(config.lastModified);
|
|
10621
11292
|
if (lastModified.enabled && lastModified.source === "git") {
|
|
10622
11293
|
const fsPaths = pages.map((page) => page.sourcePath).filter((path) => path !== undefined);
|
|
10623
|
-
const
|
|
11294
|
+
const contentRoots = sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [source.contentRoot]);
|
|
11295
|
+
const gitTimes = gitLastModifiedTimes(context.root, contentRoots, fsPaths);
|
|
10624
11296
|
for (const page of pages) {
|
|
10625
11297
|
if (!page.lastModified && page.sourcePath) {
|
|
10626
11298
|
page.lastModified = gitTimes.get(page.sourcePath);
|
|
@@ -10659,11 +11331,18 @@ import { dirname as dirname10, join as join23, resolve as resolve7 } from "pathe
|
|
|
10659
11331
|
var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
|
|
10660
11332
|
var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
|
|
10661
11333
|
var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
|
|
11334
|
+
var ESCAPE = /\\(?<char>[\\nt"])/gu;
|
|
11335
|
+
var UNESCAPED = {
|
|
11336
|
+
'"': '"',
|
|
11337
|
+
"\\": "\\",
|
|
11338
|
+
n: `
|
|
11339
|
+
`,
|
|
11340
|
+
t: "\t"
|
|
11341
|
+
};
|
|
10662
11342
|
var unquote = (raw) => {
|
|
10663
11343
|
const double = raw.match(DOUBLE_QUOTED2)?.groups?.body;
|
|
10664
11344
|
if (double !== undefined) {
|
|
10665
|
-
return double.replaceAll(
|
|
10666
|
-
`).replaceAll("\\t", "\t").replaceAll("\\\"", '"').replaceAll("\\\\", "\\");
|
|
11345
|
+
return double.replaceAll(ESCAPE, (match, char) => UNESCAPED[char] ?? match);
|
|
10667
11346
|
}
|
|
10668
11347
|
const single = raw.match(SINGLE_QUOTED)?.groups?.body;
|
|
10669
11348
|
if (single !== undefined) {
|
|
@@ -10905,6 +11584,47 @@ var enforceBudget = async (distDir, args) => {
|
|
|
10905
11584
|
}
|
|
10906
11585
|
return passed ? "pass" : "fail";
|
|
10907
11586
|
};
|
|
11587
|
+
var runClientAssetChecks = async (staticDir, args) => {
|
|
11588
|
+
if (args.analyze) {
|
|
11589
|
+
await reportBundleSizes(staticDir);
|
|
11590
|
+
}
|
|
11591
|
+
if (await enforceBudget(staticDir, args) === "fail") {
|
|
11592
|
+
process.exit(1);
|
|
11593
|
+
}
|
|
11594
|
+
};
|
|
11595
|
+
var isolatedStaticDir = (config, context) => {
|
|
11596
|
+
const { adapter, output } = config.deployment;
|
|
11597
|
+
if (output === "server" && adapter === "vercel") {
|
|
11598
|
+
return join24(context.outDir, ".vercel", "output", "static");
|
|
11599
|
+
}
|
|
11600
|
+
const dist = context.distDir ?? join24(context.outDir, "dist");
|
|
11601
|
+
if (output === "server" && adapter === "node") {
|
|
11602
|
+
return join24(dist, "client");
|
|
11603
|
+
}
|
|
11604
|
+
return dist;
|
|
11605
|
+
};
|
|
11606
|
+
var publishLlmsFiles = async (project, distDir) => {
|
|
11607
|
+
const indexPath = join24(distDir, "llms.txt");
|
|
11608
|
+
const fullPath = join24(distDir, "llms-full.txt");
|
|
11609
|
+
const writeIndex = !existsSync15(indexPath);
|
|
11610
|
+
const writeFull = !existsSync15(fullPath);
|
|
11611
|
+
if (!(writeIndex || writeFull)) {
|
|
11612
|
+
return;
|
|
11613
|
+
}
|
|
11614
|
+
const { index, full } = await buildLlmsFiles(project);
|
|
11615
|
+
const writes = [];
|
|
11616
|
+
if (writeIndex) {
|
|
11617
|
+
writes.push(writeFile7(indexPath, index, "utf-8"));
|
|
11618
|
+
}
|
|
11619
|
+
if (writeFull) {
|
|
11620
|
+
writes.push(writeFile7(fullPath, full, "utf-8"));
|
|
11621
|
+
}
|
|
11622
|
+
await Promise.all(writes);
|
|
11623
|
+
logger.success(`Generated ${[
|
|
11624
|
+
writeIndex ? "llms.txt" : null,
|
|
11625
|
+
writeFull ? "llms-full.txt" : null
|
|
11626
|
+
].filter(Boolean).join(" and ")}`);
|
|
11627
|
+
};
|
|
10908
11628
|
var publishBuildArtifacts = async (project, distDir, args) => {
|
|
10909
11629
|
if (project.config.search.provider === "pagefind") {
|
|
10910
11630
|
logger.start("Building search index");
|
|
@@ -10916,13 +11636,8 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
10916
11636
|
success: (message) => logger.success(message),
|
|
10917
11637
|
warn: (message) => logger.warn(message)
|
|
10918
11638
|
});
|
|
10919
|
-
if (project.config.ai.llmsTxt) {
|
|
10920
|
-
|
|
10921
|
-
await Promise.all([
|
|
10922
|
-
writeFile7(join24(distDir, "llms.txt"), index, "utf-8"),
|
|
10923
|
-
writeFile7(join24(distDir, "llms-full.txt"), full, "utf-8")
|
|
10924
|
-
]);
|
|
10925
|
-
logger.success("Generated llms.txt and llms-full.txt");
|
|
11639
|
+
if (project.config.ai.llmsTxt.enabled) {
|
|
11640
|
+
await publishLlmsFiles(project, distDir);
|
|
10926
11641
|
}
|
|
10927
11642
|
const sitemap = buildSitemap(project);
|
|
10928
11643
|
if (sitemap && !existsSync15(join24(distDir, "sitemap.xml"))) {
|
|
@@ -10943,25 +11658,21 @@ var publishBuildArtifacts = async (project, distDir, args) => {
|
|
|
10943
11658
|
await emitRedirectFiles(project.config, distDir);
|
|
10944
11659
|
const { config } = project;
|
|
10945
11660
|
const features = serverFeatures(config);
|
|
11661
|
+
const sitemapNote = config.seo.sitemap ? "no (set deployment.site)" : "no (seo.sitemap is false)";
|
|
10946
11662
|
logger.box([
|
|
10947
11663
|
`Output ${config.deployment.output}`,
|
|
10948
11664
|
`Adapter ${config.deployment.adapter ?? "none"}`,
|
|
10949
11665
|
`Site ${config.deployment.site ?? "not set"}`,
|
|
10950
11666
|
`Search ${config.search.provider}`,
|
|
10951
11667
|
`Redirects ${config.redirects.length}`,
|
|
10952
|
-
`Sitemap ${sitemap ? "yes" :
|
|
11668
|
+
`Sitemap ${sitemap ? "yes" : sitemapNote}`,
|
|
10953
11669
|
`Robots ${robots ? "yes" : "no"}`,
|
|
10954
11670
|
`Agent JSON ${agentReadability ? "yes" : "no"}`,
|
|
10955
|
-
`LLM files ${config.ai.llmsTxt ? "yes" : "no"}`,
|
|
11671
|
+
`LLM files ${config.ai.llmsTxt.enabled ? "yes" : "no"}`,
|
|
10956
11672
|
`Server features ${features.length > 0 ? features.join(", ") : "none"}`
|
|
10957
11673
|
].join(`
|
|
10958
11674
|
`));
|
|
10959
|
-
|
|
10960
|
-
await reportBundleSizes(distDir);
|
|
10961
|
-
}
|
|
10962
|
-
if (await enforceBudget(distDir, args) === "fail") {
|
|
10963
|
-
process.exit(1);
|
|
10964
|
-
}
|
|
11675
|
+
await runClientAssetChecks(distDir, args);
|
|
10965
11676
|
logger.success(`Built to ${distDir}`);
|
|
10966
11677
|
};
|
|
10967
11678
|
var buildCommand = defineCommand2({
|
|
@@ -11007,7 +11718,7 @@ var buildCommand = defineCommand2({
|
|
|
11007
11718
|
async run({ args }) {
|
|
11008
11719
|
const root = process.cwd();
|
|
11009
11720
|
const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
|
|
11010
|
-
refuseIfDevRunning(root, "building", runtimeDir);
|
|
11721
|
+
refuseIfDevRunning(root, "building", { isolatedHint: true, runtimeDir });
|
|
11011
11722
|
if (args.isolated) {
|
|
11012
11723
|
await ensureGitignore(root, [".blume-verify/"]);
|
|
11013
11724
|
}
|
|
@@ -11039,6 +11750,7 @@ var buildCommand = defineCommand2({
|
|
|
11039
11750
|
});
|
|
11040
11751
|
const distDir = project.context.distDir ?? join24(root, "dist");
|
|
11041
11752
|
if (runtimeDir) {
|
|
11753
|
+
await runClientAssetChecks(isolatedStaticDir(project.config, project.context), args);
|
|
11042
11754
|
logger.success(`Isolated build OK — output at ${distDir} (not published).`);
|
|
11043
11755
|
return;
|
|
11044
11756
|
}
|
|
@@ -11079,7 +11791,7 @@ var checkCommand = defineCommand3({
|
|
|
11079
11791
|
async run({ args }) {
|
|
11080
11792
|
const root = process.cwd();
|
|
11081
11793
|
const runtimeDir = args.isolated ? ".blume-verify" : process.env.BLUME_RUNTIME_DIR;
|
|
11082
|
-
refuseIfDevRunning(root, "checking", runtimeDir);
|
|
11794
|
+
refuseIfDevRunning(root, "checking", { isolatedHint: true, runtimeDir });
|
|
11083
11795
|
if (args.isolated) {
|
|
11084
11796
|
await ensureGitignore(root, [".blume-verify/"]);
|
|
11085
11797
|
}
|
|
@@ -11191,6 +11903,7 @@ var coalescedRunner = (task) => {
|
|
|
11191
11903
|
};
|
|
11192
11904
|
|
|
11193
11905
|
// src/cli/commands/dev.ts
|
|
11906
|
+
var normalizeHost = (host) => host === "" ? true : host ?? false;
|
|
11194
11907
|
var routeSignature = (routes) => routes.map((route) => `${route.path} ${route.entryId}`).toSorted().join(`
|
|
11195
11908
|
`);
|
|
11196
11909
|
var devCommand = defineCommand4({
|
|
@@ -11246,7 +11959,7 @@ var devCommand = defineCommand4({
|
|
|
11246
11959
|
const createServer = (listenPort, open) => dev({
|
|
11247
11960
|
logLevel: args.debug ? "debug" : "info",
|
|
11248
11961
|
root: project.context.outDir,
|
|
11249
|
-
server: { host: args.host
|
|
11962
|
+
server: { host: normalizeHost(args.host), open, port: listenPort }
|
|
11250
11963
|
});
|
|
11251
11964
|
let server = await createServer(explicitPort, args.open ?? false);
|
|
11252
11965
|
const boundPort = server.address.port;
|
|
@@ -11266,7 +11979,6 @@ var devCommand = defineCommand4({
|
|
|
11266
11979
|
});
|
|
11267
11980
|
const nextSignature = routeSignature(next.manifest.routes);
|
|
11268
11981
|
const structural = nextSignature !== lastSignature;
|
|
11269
|
-
lastSignature = nextSignature;
|
|
11270
11982
|
if (structural) {
|
|
11271
11983
|
await server.stop();
|
|
11272
11984
|
await generateRuntime(next);
|
|
@@ -11274,6 +11986,8 @@ var devCommand = defineCommand4({
|
|
|
11274
11986
|
} else {
|
|
11275
11987
|
await generateRuntime(next);
|
|
11276
11988
|
}
|
|
11989
|
+
lastSignature = nextSignature;
|
|
11990
|
+
reportDiagnostics(next.diagnostics, root);
|
|
11277
11991
|
showBlumeErrorOverlay(next.diagnostics);
|
|
11278
11992
|
} catch (error) {
|
|
11279
11993
|
logger.error(`Regeneration failed: ${error.message}`);
|
|
@@ -11415,15 +12129,27 @@ var doctorCommand = defineCommand5({
|
|
|
11415
12129
|
});
|
|
11416
12130
|
|
|
11417
12131
|
// src/cli/commands/eject.ts
|
|
11418
|
-
import { readFile as readFile13, writeFile as writeFile9 } from "node:fs/promises";
|
|
11419
12132
|
import { defineCommand as defineCommand6 } from "citty";
|
|
11420
|
-
import {
|
|
12133
|
+
import { relative as relative14 } from "pathe";
|
|
11421
12134
|
|
|
11422
12135
|
// src/registry/eject.ts
|
|
11423
12136
|
import { existsSync as existsSync17 } from "node:fs";
|
|
11424
12137
|
import { cp as cp2, mkdir as mkdir7, readFile as readFile12, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
|
|
11425
12138
|
import { join as join27, relative as relative12 } from "pathe";
|
|
11426
12139
|
var toPosix = (path) => path.split("\\").join("/");
|
|
12140
|
+
var LOCAL_BLUME_SOURCE = "../../node_modules/blume/src/**/*.{astro,ts,tsx}";
|
|
12141
|
+
var blumeSourceGlob = (root, genDir, resolveBlumeRoot = packageRoot) => {
|
|
12142
|
+
if (existsSync17(join27(root, "node_modules", "blume"))) {
|
|
12143
|
+
return LOCAL_BLUME_SOURCE;
|
|
12144
|
+
}
|
|
12145
|
+
try {
|
|
12146
|
+
const src = join27(resolveBlumeRoot(), "src");
|
|
12147
|
+
return `${toPosix(relative12(genDir, src))}/**/*.{astro,ts,tsx}`;
|
|
12148
|
+
} catch {
|
|
12149
|
+
console.warn('blume: could not locate the installed blume package; src/generated/app.css keeps its default `@source "../../node_modules/blume/..."` glob. If blume is hoisted elsewhere, point that glob at its install location or Blume\'s utility classes will be missing.');
|
|
12150
|
+
return LOCAL_BLUME_SOURCE;
|
|
12151
|
+
}
|
|
12152
|
+
};
|
|
11427
12153
|
var ejectOpenApiData = (project) => {
|
|
11428
12154
|
const source = project.sources.find(isOpenApiSource);
|
|
11429
12155
|
return source ? source.openApiData() : {};
|
|
@@ -11449,6 +12175,63 @@ var askFiles = async (project, srcDir, genDir) => {
|
|
|
11449
12175
|
}
|
|
11450
12176
|
return files;
|
|
11451
12177
|
};
|
|
12178
|
+
var hostsMcp = (project, userPages) => project.config.mcp.enabled && !routeIsTaken(userPages, project.graph.pages, project.config.mcp.route);
|
|
12179
|
+
var mcpDiscoveryPages = (project, userPages) => hostsMcp(project, userPages) ? [
|
|
12180
|
+
{
|
|
12181
|
+
entrypoint: "src/blume-mcp/discovery.ts",
|
|
12182
|
+
pattern: "/.well-known/mcp.json"
|
|
12183
|
+
},
|
|
12184
|
+
{
|
|
12185
|
+
entrypoint: "src/blume-mcp/server-card.ts",
|
|
12186
|
+
pattern: "/.well-known/mcp/server-card.json"
|
|
12187
|
+
}
|
|
12188
|
+
] : [];
|
|
12189
|
+
var mcpFiles = async (project, userPages, srcDir, genDir) => {
|
|
12190
|
+
if (!hostsMcp(project, userPages)) {
|
|
12191
|
+
return [];
|
|
12192
|
+
}
|
|
12193
|
+
const { route } = project.config.mcp;
|
|
12194
|
+
const data = await buildMcpData(project);
|
|
12195
|
+
const discoveryInput = {
|
|
12196
|
+
base: data.base,
|
|
12197
|
+
name: data.name,
|
|
12198
|
+
route,
|
|
12199
|
+
site: data.site,
|
|
12200
|
+
version: data.version
|
|
12201
|
+
};
|
|
12202
|
+
return [
|
|
12203
|
+
{
|
|
12204
|
+
content: `${JSON.stringify(data)}
|
|
12205
|
+
`,
|
|
12206
|
+
path: join27(genDir, "mcp-data.json")
|
|
12207
|
+
},
|
|
12208
|
+
{
|
|
12209
|
+
content: mcpEndpointTemplate(route),
|
|
12210
|
+
path: join27(srcDir, "pages", mcpPageFile(route))
|
|
12211
|
+
},
|
|
12212
|
+
{
|
|
12213
|
+
content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),
|
|
12214
|
+
path: join27(srcDir, "blume-mcp", "discovery.ts")
|
|
12215
|
+
},
|
|
12216
|
+
{
|
|
12217
|
+
content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),
|
|
12218
|
+
path: join27(srcDir, "blume-mcp", "server-card.ts")
|
|
12219
|
+
}
|
|
12220
|
+
];
|
|
12221
|
+
};
|
|
12222
|
+
var changelogFiles = (project, userPages, srcDir, options) => {
|
|
12223
|
+
const hasChangelog = project.graph.pages.some((page) => page.contentType === "changelog" && !(page.meta.draft || page.meta.sidebar.hidden));
|
|
12224
|
+
const hasChangelogSource = (project.config.content.sources ?? []).some((source) => source.type === "github-releases");
|
|
12225
|
+
if (!(hasChangelog || hasChangelogSource) || routeIsTaken(userPages, project.graph.pages, "/changelog")) {
|
|
12226
|
+
return [];
|
|
12227
|
+
}
|
|
12228
|
+
return [
|
|
12229
|
+
{
|
|
12230
|
+
content: changelogIndexTemplate(options),
|
|
12231
|
+
path: join27(srcDir, "pages", "changelog.astro")
|
|
12232
|
+
}
|
|
12233
|
+
];
|
|
12234
|
+
};
|
|
11452
12235
|
var readExamplesCss = (root, css) => css && existsSync17(join27(root, css)) ? readFile12(join27(root, css), "utf-8") : Promise.resolve("");
|
|
11453
12236
|
var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
|
|
11454
12237
|
{
|
|
@@ -11497,10 +12280,13 @@ var eject = async (root) => {
|
|
|
11497
12280
|
root: "."
|
|
11498
12281
|
};
|
|
11499
12282
|
const componentsImport = context.componentsFile ? `../../${toPosix(relative12(root, context.componentsFile))}` : null;
|
|
11500
|
-
const relPages =
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
12283
|
+
const relPages = [
|
|
12284
|
+
...pages.map((page) => ({
|
|
12285
|
+
entrypoint: toPosix(relative12(root, page.entrypoint)),
|
|
12286
|
+
pattern: page.pattern
|
|
12287
|
+
})),
|
|
12288
|
+
...mcpDiscoveryPages(project, pages)
|
|
12289
|
+
];
|
|
11504
12290
|
const staged = collectStaged(project);
|
|
11505
12291
|
const hasStaged = staged.size > 0;
|
|
11506
12292
|
const stagedDir = "blume-staged";
|
|
@@ -11572,7 +12358,7 @@ var eject = async (root) => {
|
|
|
11572
12358
|
content: tailwindEntryTemplate({
|
|
11573
12359
|
configTokens: buildThemeCss(config.theme),
|
|
11574
12360
|
sources: [
|
|
11575
|
-
|
|
12361
|
+
blumeSourceGlob(root, genDir),
|
|
11576
12362
|
"../../**/*.{astro,mdx,ts,tsx}"
|
|
11577
12363
|
],
|
|
11578
12364
|
twoslashCss: twoslashCss(),
|
|
@@ -11592,11 +12378,11 @@ var eject = async (root) => {
|
|
|
11592
12378
|
path: join27(genDir, "raw-markdown.json")
|
|
11593
12379
|
},
|
|
11594
12380
|
{
|
|
11595
|
-
content: rawMarkdownEndpointTemplate(),
|
|
12381
|
+
content: rawMarkdownEndpointTemplate("md"),
|
|
11596
12382
|
path: join27(srcDir, "pages", "[...slug].md.ts")
|
|
11597
12383
|
},
|
|
11598
12384
|
{
|
|
11599
|
-
content: rawMarkdownEndpointTemplate(),
|
|
12385
|
+
content: rawMarkdownEndpointTemplate("mdx"),
|
|
11600
12386
|
path: join27(srcDir, "pages", "[...slug].mdx.ts")
|
|
11601
12387
|
}
|
|
11602
12388
|
];
|
|
@@ -11609,6 +12395,13 @@ var eject = async (root) => {
|
|
|
11609
12395
|
path: join27(srcDir, "pages", "og", "[...slug].png.ts")
|
|
11610
12396
|
});
|
|
11611
12397
|
}
|
|
12398
|
+
files.push(...await mcpFiles(project, pages, srcDir, genDir), ...changelogFiles(project, pages, srcDir, {
|
|
12399
|
+
askEnabled,
|
|
12400
|
+
exportEpub,
|
|
12401
|
+
exportPdf,
|
|
12402
|
+
needsReact,
|
|
12403
|
+
staged: hasStaged
|
|
12404
|
+
}));
|
|
11612
12405
|
if (!routeIsTaken(pages, project.graph.pages, "/404")) {
|
|
11613
12406
|
files.push({
|
|
11614
12407
|
content: notFoundPageTemplate(),
|
|
@@ -11648,12 +12441,14 @@ var eject = async (root) => {
|
|
|
11648
12441
|
path: join27(srcDir, "pages", "[section]", "rss.xml.ts")
|
|
11649
12442
|
});
|
|
11650
12443
|
}
|
|
12444
|
+
const warnings = [];
|
|
11651
12445
|
if (hasScalarReferences(config)) {
|
|
11652
12446
|
const references = await buildReferenceFiles({
|
|
11653
12447
|
config,
|
|
11654
12448
|
contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
|
|
11655
12449
|
root
|
|
11656
12450
|
});
|
|
12451
|
+
warnings.push(...references.warnings);
|
|
11657
12452
|
for (const file of references.files) {
|
|
11658
12453
|
files.push({
|
|
11659
12454
|
content: file.content,
|
|
@@ -11683,10 +12478,37 @@ var eject = async (root) => {
|
|
|
11683
12478
|
});
|
|
11684
12479
|
}
|
|
11685
12480
|
await rm3(context.outDir, { force: true, recursive: true });
|
|
11686
|
-
return written.map((file) => file.path);
|
|
12481
|
+
return { files: written.map((file) => file.path), warnings };
|
|
11687
12482
|
};
|
|
11688
12483
|
|
|
11689
|
-
// src/cli/
|
|
12484
|
+
// src/cli/eject-scripts.ts
|
|
12485
|
+
import { readFile as readFile13, writeFile as writeFile9 } from "node:fs/promises";
|
|
12486
|
+
import { join as join28 } from "pathe";
|
|
12487
|
+
var droppedArtifactNotices = (config) => {
|
|
12488
|
+
const notices = [];
|
|
12489
|
+
if (config.search.provider === "pagefind") {
|
|
12490
|
+
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).');
|
|
12491
|
+
}
|
|
12492
|
+
if (searchProviderMeta(config.search.provider).syncs) {
|
|
12493
|
+
notices.push(`the hosted ${config.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.`);
|
|
12494
|
+
}
|
|
12495
|
+
if (config.ai.llmsTxt.enabled) {
|
|
12496
|
+
notices.push("llms.txt and llms-full.txt");
|
|
12497
|
+
}
|
|
12498
|
+
if (config.deployment.site && config.seo.sitemap) {
|
|
12499
|
+
notices.push("sitemap.xml — recreate it with the @astrojs/sitemap integration.");
|
|
12500
|
+
}
|
|
12501
|
+
if (config.seo.robots) {
|
|
12502
|
+
notices.push("robots.txt — recreate it as a public/robots.txt file.");
|
|
12503
|
+
}
|
|
12504
|
+
if (config.seo.agentReadability) {
|
|
12505
|
+
notices.push("agent-readability.json");
|
|
12506
|
+
}
|
|
12507
|
+
if (config.redirects.length > 0 && config.deployment.output === "static") {
|
|
12508
|
+
notices.push("the platform redirect files (_redirects, vercel.json) — your redirects still work as Astro-generated meta-refresh pages.");
|
|
12509
|
+
}
|
|
12510
|
+
return notices;
|
|
12511
|
+
};
|
|
11690
12512
|
var updatePackageScripts = async (root) => {
|
|
11691
12513
|
const pkgPath = join28(root, "package.json");
|
|
11692
12514
|
let pkg;
|
|
@@ -11705,47 +12527,21 @@ var updatePackageScripts = async (root) => {
|
|
|
11705
12527
|
await writeFile9(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
11706
12528
|
`, "utf-8");
|
|
11707
12529
|
};
|
|
11708
|
-
var ejectCommand = defineCommand6({
|
|
11709
|
-
args: {
|
|
11710
|
-
yes: { description: "Skip the confirmation prompt.", type: "boolean" }
|
|
11711
|
-
},
|
|
11712
|
-
meta: {
|
|
11713
|
-
description: "Promote the generated runtime into an owned Astro project.",
|
|
11714
|
-
name: "eject"
|
|
11715
|
-
},
|
|
11716
|
-
async run({ args }) {
|
|
11717
|
-
const root = process.cwd();
|
|
11718
|
-
refuseIfDevRunning(root, "ejecting");
|
|
11719
|
-
if (!args.yes) {
|
|
11720
|
-
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.");
|
|
11721
|
-
logger.info("Re-run with --yes to proceed.");
|
|
11722
|
-
return;
|
|
11723
|
-
}
|
|
11724
|
-
const files = await eject(root);
|
|
11725
|
-
await updatePackageScripts(root);
|
|
11726
|
-
logger.success(`Ejected ${files.length} file(s):`);
|
|
11727
|
-
for (const file of files) {
|
|
11728
|
-
process.stdout.write(` ${relative13(root, file)}
|
|
11729
|
-
`);
|
|
11730
|
-
}
|
|
11731
|
-
logger.box(`Your project is now a standalone Astro app.
|
|
11732
|
-
|
|
11733
|
-
bun run dev
|
|
11734
|
-
bun run build
|
|
11735
12530
|
|
|
11736
|
-
|
|
11737
|
-
}
|
|
11738
|
-
});
|
|
11739
|
-
|
|
11740
|
-
// src/cli/commands/init.ts
|
|
12531
|
+
// src/cli/init/scaffold.ts
|
|
11741
12532
|
import { existsSync as existsSync18 } from "node:fs";
|
|
11742
12533
|
import { mkdir as mkdir8, writeFile as writeFile10 } from "node:fs/promises";
|
|
11743
|
-
import {
|
|
11744
|
-
import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute9, join as join29, relative as relative14 } from "pathe";
|
|
12534
|
+
import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute9, join as join29, relative as relative13 } from "pathe";
|
|
11745
12535
|
|
|
11746
12536
|
// src/core/package-json.ts
|
|
11747
12537
|
var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
|
|
11748
|
-
var blumePackageJson = (name) =>
|
|
12538
|
+
var blumePackageJson = (name, extraDeps = {}) => {
|
|
12539
|
+
const dependencies = Object.entries({
|
|
12540
|
+
blume: `^${getBlumeVersion()}`,
|
|
12541
|
+
...extraDeps
|
|
12542
|
+
}).toSorted(([a], [b]) => a < b ? -1 : 1).map(([dep, range]) => ` ${JSON.stringify(dep)}: ${JSON.stringify(range)}`).join(`,
|
|
12543
|
+
`);
|
|
12544
|
+
return `{
|
|
11749
12545
|
"name": ${JSON.stringify(name)},
|
|
11750
12546
|
"private": true,
|
|
11751
12547
|
"type": "module",
|
|
@@ -11755,21 +12551,22 @@ var blumePackageJson = (name) => `{
|
|
|
11755
12551
|
"doctor": "blume doctor"
|
|
11756
12552
|
},
|
|
11757
12553
|
"dependencies": {
|
|
11758
|
-
|
|
12554
|
+
${dependencies}
|
|
11759
12555
|
}
|
|
11760
12556
|
}
|
|
11761
12557
|
`;
|
|
12558
|
+
};
|
|
11762
12559
|
|
|
11763
|
-
// src/cli/
|
|
12560
|
+
// src/cli/init/scaffold.ts
|
|
11764
12561
|
var TEMPLATES = ["docs", "api", "sdk", "changelog"];
|
|
11765
12562
|
var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
|
|
11766
|
-
var
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
12563
|
+
var SOURCE_KINDS = [
|
|
12564
|
+
"filesystem",
|
|
12565
|
+
"github-releases",
|
|
12566
|
+
"notion",
|
|
12567
|
+
"sanity",
|
|
12568
|
+
"mdx-remote"
|
|
12569
|
+
];
|
|
11773
12570
|
var page = (title, description, body) => `---
|
|
11774
12571
|
title: ${title}
|
|
11775
12572
|
description: ${description}
|
|
@@ -11779,7 +12576,7 @@ ${body}
|
|
|
11779
12576
|
`;
|
|
11780
12577
|
var STARTERS = {
|
|
11781
12578
|
api: {
|
|
11782
|
-
|
|
12579
|
+
configExtra: `
|
|
11783
12580
|
openapi: {
|
|
11784
12581
|
enabled: true,
|
|
11785
12582
|
route: "/api",
|
|
@@ -11789,7 +12586,7 @@ var STARTERS = {
|
|
|
11789
12586
|
spec: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
11790
12587
|
},
|
|
11791
12588
|
],
|
|
11792
|
-
}
|
|
12589
|
+
},`,
|
|
11793
12590
|
files: (dir) => [
|
|
11794
12591
|
{
|
|
11795
12592
|
content: page("API Reference", "Explore the API.", "# API Reference\n\nYour OpenAPI spec renders at [`/api`](/api). Point `openapi.sources` at your own spec in `blume.config.ts`."),
|
|
@@ -11798,13 +12595,13 @@ var STARTERS = {
|
|
|
11798
12595
|
]
|
|
11799
12596
|
},
|
|
11800
12597
|
changelog: {
|
|
11801
|
-
|
|
12598
|
+
configExtra: `
|
|
11802
12599
|
navigation: {
|
|
11803
12600
|
tabs: [
|
|
11804
12601
|
{ label: "Docs", path: "/" },
|
|
11805
12602
|
{ label: "Changelog", path: "/changelog" },
|
|
11806
12603
|
],
|
|
11807
|
-
}
|
|
12604
|
+
},`,
|
|
11808
12605
|
files: (dir) => [
|
|
11809
12606
|
{
|
|
11810
12607
|
content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
|
|
@@ -11824,7 +12621,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
|
|
|
11824
12621
|
]
|
|
11825
12622
|
},
|
|
11826
12623
|
docs: {
|
|
11827
|
-
|
|
12624
|
+
configExtra: "",
|
|
11828
12625
|
files: (dir) => [
|
|
11829
12626
|
{
|
|
11830
12627
|
content: page("Introduction", "Welcome to your new Blume docs.", `# Introduction
|
|
@@ -11837,7 +12634,7 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
|
|
|
11837
12634
|
]
|
|
11838
12635
|
},
|
|
11839
12636
|
sdk: {
|
|
11840
|
-
|
|
12637
|
+
configExtra: "",
|
|
11841
12638
|
files: (dir) => [
|
|
11842
12639
|
{
|
|
11843
12640
|
content: page("Introduction", "Get started with the SDK.", `# Introduction
|
|
@@ -11853,26 +12650,331 @@ Install the SDK and make your first call. See [Installation](/installation).`),
|
|
|
11853
12650
|
}
|
|
11854
12651
|
};
|
|
11855
12652
|
var commandsFor = (pm) => ({
|
|
12653
|
+
build: pm === "npm" || pm === "bun" ? `${pm} run build` : `${pm} build`,
|
|
11856
12654
|
dev: pm === "npm" ? "npm run dev" : `${pm} dev`,
|
|
12655
|
+
exec: { bun: "bunx", npm: "npx", pnpm: "pnpm exec", yarn: "yarn" }[pm],
|
|
11857
12656
|
install: `${pm} install`
|
|
11858
12657
|
});
|
|
11859
|
-
var
|
|
11860
|
-
|
|
11861
|
-
|
|
12658
|
+
var detectPackageManager = (userAgent) => {
|
|
12659
|
+
const name = userAgent?.split("/")[0];
|
|
12660
|
+
return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : "npm";
|
|
12661
|
+
};
|
|
12662
|
+
var validateContentDir = (root, dir) => isAbsolute9(dir) || relative13(root, join29(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
|
|
12663
|
+
var titleize = (raw) => {
|
|
12664
|
+
const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
|
|
12665
|
+
return words.length === 0 ? "My Docs" : words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
12666
|
+
};
|
|
12667
|
+
var hasRemoteSource = (sources) => sources.some((source) => source !== "filesystem");
|
|
12668
|
+
var sourceSnippetFor = (kind) => {
|
|
12669
|
+
switch (kind) {
|
|
12670
|
+
case "github-releases": {
|
|
12671
|
+
return ` // Changelog entries from GitHub Releases. Private repos read
|
|
12672
|
+
// GITHUB_TOKEN from the environment.
|
|
12673
|
+
{
|
|
12674
|
+
type: "github-releases",
|
|
12675
|
+
owner: "your-org",
|
|
12676
|
+
repo: "your-repo",
|
|
12677
|
+
prefix: "changelog",
|
|
12678
|
+
},`;
|
|
12679
|
+
}
|
|
12680
|
+
case "notion": {
|
|
12681
|
+
return ` // Pages from a Notion database. Reads NOTION_TOKEN from the environment.
|
|
12682
|
+
{
|
|
12683
|
+
type: "notion",
|
|
12684
|
+
database: "your-database-id",
|
|
12685
|
+
prefix: "notion",
|
|
12686
|
+
},`;
|
|
12687
|
+
}
|
|
12688
|
+
case "sanity": {
|
|
12689
|
+
return ` // Documents from a Sanity dataset. Private datasets read SANITY_TOKEN
|
|
12690
|
+
// from the environment.
|
|
12691
|
+
{
|
|
12692
|
+
type: "sanity",
|
|
12693
|
+
projectId: "your-project-id",
|
|
12694
|
+
dataset: "production",
|
|
12695
|
+
query: \`*[_type == "doc"]\`,
|
|
12696
|
+
prefix: "sanity",
|
|
12697
|
+
},`;
|
|
12698
|
+
}
|
|
12699
|
+
case "mdx-remote": {
|
|
12700
|
+
return ` // MDX fetched from a GitHub repo. Private repos read GITHUB_TOKEN
|
|
12701
|
+
// from the environment.
|
|
12702
|
+
{
|
|
12703
|
+
type: "mdx-remote",
|
|
12704
|
+
github: { owner: "your-org", repo: "your-repo", path: "docs" },
|
|
12705
|
+
prefix: "remote",
|
|
12706
|
+
},`;
|
|
12707
|
+
}
|
|
12708
|
+
default: {
|
|
12709
|
+
return kind;
|
|
12710
|
+
}
|
|
12711
|
+
}
|
|
12712
|
+
};
|
|
12713
|
+
var contentBlockFor = (answers) => {
|
|
12714
|
+
const sources = answers.sources.length === 0 ? ["filesystem"] : answers.sources;
|
|
12715
|
+
if (!hasRemoteSource(sources)) {
|
|
12716
|
+
return answers.contentDir === "docs" ? "" : `
|
|
12717
|
+
content: {
|
|
12718
|
+
root: ${JSON.stringify(answers.contentDir)},
|
|
12719
|
+
},`;
|
|
12720
|
+
}
|
|
12721
|
+
const entries = SOURCE_KINDS.filter((kind) => sources.includes(kind)).map((kind) => kind === "filesystem" ? ` { type: "filesystem", root: ${JSON.stringify(answers.contentDir)} },` : sourceSnippetFor(kind));
|
|
12722
|
+
return `
|
|
12723
|
+
content: {
|
|
12724
|
+
sources: [
|
|
12725
|
+
${entries.join(`
|
|
12726
|
+
`)}
|
|
12727
|
+
],
|
|
12728
|
+
},`;
|
|
12729
|
+
};
|
|
12730
|
+
var buildConfig = (answers) => `import { defineConfig } from "blume";
|
|
12731
|
+
|
|
12732
|
+
export default defineConfig({
|
|
12733
|
+
title: ${JSON.stringify(answers.title)},
|
|
12734
|
+
description: "Documentation powered by Blume.",${STARTERS[answers.template].configExtra}${contentBlockFor(answers)}
|
|
12735
|
+
});
|
|
12736
|
+
`;
|
|
12737
|
+
var extraDepsFor = (sources) => ({
|
|
12738
|
+
...sources.includes("notion") && { "@notionhq/client": "^2.2.15" },
|
|
12739
|
+
...sources.includes("sanity") && { "@sanity/client": "^6.21.0" }
|
|
12740
|
+
});
|
|
12741
|
+
var buildPlan = (root, answers) => {
|
|
12742
|
+
const files = [
|
|
12743
|
+
{
|
|
12744
|
+
content: blumePackageJson(toPackageName(basename4(root)), extraDepsFor(answers.sources)),
|
|
12745
|
+
path: join29(root, "package.json")
|
|
12746
|
+
},
|
|
12747
|
+
{ content: buildConfig(answers), path: join29(root, "blume.config.ts") }
|
|
12748
|
+
];
|
|
12749
|
+
if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
|
|
12750
|
+
files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path: join29(root, file.path) })));
|
|
12751
|
+
}
|
|
12752
|
+
return files;
|
|
12753
|
+
};
|
|
12754
|
+
var writeFileSafe = async (file, log) => {
|
|
12755
|
+
if (existsSync18(file.path)) {
|
|
12756
|
+
log.info(`Skipped existing ${file.path}`);
|
|
11862
12757
|
return false;
|
|
11863
12758
|
}
|
|
11864
|
-
await mkdir8(dirname11(path), { recursive: true });
|
|
11865
|
-
await writeFile10(path, content, "utf-8");
|
|
11866
|
-
|
|
12759
|
+
await mkdir8(dirname11(file.path), { recursive: true });
|
|
12760
|
+
await writeFile10(file.path, file.content, "utf-8");
|
|
12761
|
+
log.success(`Created ${file.path}`);
|
|
11867
12762
|
return true;
|
|
11868
12763
|
};
|
|
12764
|
+
var applyPlan = async (files, log) => {
|
|
12765
|
+
const created = await Promise.all(files.map((file) => writeFileSafe(file, log)));
|
|
12766
|
+
const createdPackage = files.some((file, index) => created[index] && basename4(file.path) === "package.json");
|
|
12767
|
+
return { createdPackage };
|
|
12768
|
+
};
|
|
12769
|
+
var envVarsFor = (sources) => [
|
|
12770
|
+
["GITHUB_TOKEN", ["github-releases", "mdx-remote"]],
|
|
12771
|
+
["NOTION_TOKEN", ["notion"]],
|
|
12772
|
+
["SANITY_TOKEN", ["sanity"]]
|
|
12773
|
+
].filter(([, kinds]) => kinds.some((kind) => sources.includes(kind))).map(([envVar]) => envVar);
|
|
12774
|
+
var nextSteps = (answers, createdPackage) => {
|
|
12775
|
+
const commands = commandsFor(answers.packageManager);
|
|
12776
|
+
const lines = [];
|
|
12777
|
+
if (answers.directory !== ".") {
|
|
12778
|
+
lines.push(`cd ${answers.directory}`);
|
|
12779
|
+
}
|
|
12780
|
+
if (createdPackage) {
|
|
12781
|
+
lines.push(commands.install);
|
|
12782
|
+
}
|
|
12783
|
+
lines.push(commands.dev);
|
|
12784
|
+
const envVars = envVarsFor(answers.sources);
|
|
12785
|
+
const auth = envVars.length > 0 ? `
|
|
12786
|
+
Set ${envVars.join(" and ")} in .env.local so your sources can authenticate.
|
|
12787
|
+
` : "";
|
|
12788
|
+
return `Next steps:
|
|
12789
|
+
|
|
12790
|
+
${lines.join(`
|
|
12791
|
+
`)}
|
|
12792
|
+
${auth}`;
|
|
12793
|
+
};
|
|
12794
|
+
|
|
12795
|
+
// src/cli/commands/eject.ts
|
|
12796
|
+
var reportDroppedArtifacts = (notices) => {
|
|
12797
|
+
if (notices.length === 0) {
|
|
12798
|
+
return;
|
|
12799
|
+
}
|
|
12800
|
+
logger.warn([
|
|
12801
|
+
"The ejected build script runs plain `astro build`, which stops producing these `blume build` artifacts:",
|
|
12802
|
+
...notices.map((notice) => ` - ${notice}`)
|
|
12803
|
+
].join(`
|
|
12804
|
+
`));
|
|
12805
|
+
};
|
|
12806
|
+
var ejectCommand = defineCommand6({
|
|
12807
|
+
args: {
|
|
12808
|
+
yes: { description: "Skip the confirmation prompt.", type: "boolean" }
|
|
12809
|
+
},
|
|
12810
|
+
meta: {
|
|
12811
|
+
description: "Promote the generated runtime into an owned Astro project.",
|
|
12812
|
+
name: "eject"
|
|
12813
|
+
},
|
|
12814
|
+
async run({ args }) {
|
|
12815
|
+
const root = process.cwd();
|
|
12816
|
+
refuseIfDevRunning(root, "ejecting");
|
|
12817
|
+
let notices = [];
|
|
12818
|
+
try {
|
|
12819
|
+
const { config } = await loadConfig(root);
|
|
12820
|
+
notices = droppedArtifactNotices(config);
|
|
12821
|
+
} catch {}
|
|
12822
|
+
if (!args.yes) {
|
|
12823
|
+
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.");
|
|
12824
|
+
reportDroppedArtifacts(notices);
|
|
12825
|
+
logger.info("Re-run with --yes to proceed.");
|
|
12826
|
+
return;
|
|
12827
|
+
}
|
|
12828
|
+
const { files, warnings } = await eject(root);
|
|
12829
|
+
await updatePackageScripts(root);
|
|
12830
|
+
for (const warning of warnings) {
|
|
12831
|
+
logger.warn(warning);
|
|
12832
|
+
}
|
|
12833
|
+
logger.success(`Ejected ${files.length} file(s):`);
|
|
12834
|
+
for (const file of files) {
|
|
12835
|
+
process.stdout.write(` ${relative14(root, file)}
|
|
12836
|
+
`);
|
|
12837
|
+
}
|
|
12838
|
+
reportDroppedArtifacts(notices);
|
|
12839
|
+
const pm = detectPackageManager(process.env.npm_config_user_agent);
|
|
12840
|
+
const { build: build2, dev: dev2 } = commandsFor(pm);
|
|
12841
|
+
logger.box(`Your project is now a standalone Astro app.
|
|
12842
|
+
|
|
12843
|
+
${dev2}
|
|
12844
|
+
${build2}
|
|
12845
|
+
|
|
12846
|
+
The blume package remains importable.`);
|
|
12847
|
+
}
|
|
12848
|
+
});
|
|
12849
|
+
|
|
12850
|
+
// src/cli/commands/init.ts
|
|
12851
|
+
import * as clack from "@clack/prompts";
|
|
12852
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
12853
|
+
import { resolve as resolve9 } from "pathe";
|
|
12854
|
+
|
|
12855
|
+
// src/cli/init/questions.ts
|
|
12856
|
+
import { basename as basename5, resolve as resolve8 } from "pathe";
|
|
12857
|
+
var cancelled = (value) => typeof value === "symbol";
|
|
12858
|
+
var collectAnswers = async (prompter, flags, defaults) => {
|
|
12859
|
+
const directory = flags.directory ?? await prompter.text({
|
|
12860
|
+
defaultValue: ".",
|
|
12861
|
+
message: "Where should we create your project?",
|
|
12862
|
+
placeholder: "./my-docs"
|
|
12863
|
+
});
|
|
12864
|
+
if (cancelled(directory)) {
|
|
12865
|
+
return null;
|
|
12866
|
+
}
|
|
12867
|
+
const root = resolve8(defaults.cwd, directory);
|
|
12868
|
+
const title = await prompter.text({
|
|
12869
|
+
initialValue: titleize(basename5(root)),
|
|
12870
|
+
message: "What's your docs site called?",
|
|
12871
|
+
validate: (value) => value?.trim() ? undefined : "Give your docs site a name."
|
|
12872
|
+
});
|
|
12873
|
+
if (cancelled(title)) {
|
|
12874
|
+
return null;
|
|
12875
|
+
}
|
|
12876
|
+
const template = flags.template ?? await prompter.select({
|
|
12877
|
+
message: "Which template?",
|
|
12878
|
+
options: [
|
|
12879
|
+
{ hint: "Markdown docs site", label: "docs", value: "docs" },
|
|
12880
|
+
{ hint: "OpenAPI reference at /api", label: "api", value: "api" },
|
|
12881
|
+
{ hint: "SDK docs with an install page", label: "sdk", value: "sdk" },
|
|
12882
|
+
{
|
|
12883
|
+
hint: "Docs plus a changelog tab",
|
|
12884
|
+
label: "changelog",
|
|
12885
|
+
value: "changelog"
|
|
12886
|
+
}
|
|
12887
|
+
]
|
|
12888
|
+
});
|
|
12889
|
+
if (cancelled(template)) {
|
|
12890
|
+
return null;
|
|
12891
|
+
}
|
|
12892
|
+
const picked = await prompter.multiselect({
|
|
12893
|
+
initialValues: ["filesystem"],
|
|
12894
|
+
message: "Where does your content live?",
|
|
12895
|
+
options: [
|
|
12896
|
+
{ hint: "Local .mdx files", label: "filesystem", value: "filesystem" },
|
|
12897
|
+
{
|
|
12898
|
+
hint: "Changelog from GitHub Releases",
|
|
12899
|
+
label: "github-releases",
|
|
12900
|
+
value: "github-releases"
|
|
12901
|
+
},
|
|
12902
|
+
{ hint: "A Notion database", label: "notion", value: "notion" },
|
|
12903
|
+
{ hint: "A Sanity dataset", label: "sanity", value: "sanity" },
|
|
12904
|
+
{
|
|
12905
|
+
hint: "MDX fetched from a GitHub repo",
|
|
12906
|
+
label: "mdx-remote",
|
|
12907
|
+
value: "mdx-remote"
|
|
12908
|
+
}
|
|
12909
|
+
],
|
|
12910
|
+
required: false
|
|
12911
|
+
});
|
|
12912
|
+
if (cancelled(picked)) {
|
|
12913
|
+
return null;
|
|
12914
|
+
}
|
|
12915
|
+
const sources = picked.length === 0 ? ["filesystem"] : picked;
|
|
12916
|
+
let contentDir = flags.contentDir ?? "docs";
|
|
12917
|
+
if (flags.contentDir === undefined && sources.includes("filesystem")) {
|
|
12918
|
+
const answer = await prompter.text({
|
|
12919
|
+
defaultValue: "docs",
|
|
12920
|
+
message: "Content directory?",
|
|
12921
|
+
placeholder: "docs",
|
|
12922
|
+
validate: (value) => validateContentDir(root, value || "docs")
|
|
12923
|
+
});
|
|
12924
|
+
if (cancelled(answer)) {
|
|
12925
|
+
return null;
|
|
12926
|
+
}
|
|
12927
|
+
contentDir = answer;
|
|
12928
|
+
}
|
|
12929
|
+
return {
|
|
12930
|
+
contentDir,
|
|
12931
|
+
directory,
|
|
12932
|
+
packageManager: flags.packageManager ?? detectPackageManager(defaults.userAgent),
|
|
12933
|
+
sources,
|
|
12934
|
+
template,
|
|
12935
|
+
title
|
|
12936
|
+
};
|
|
12937
|
+
};
|
|
12938
|
+
|
|
12939
|
+
// src/cli/commands/init.ts
|
|
12940
|
+
var ejectScaffold = async (root, answers) => {
|
|
12941
|
+
const commands = commandsFor(answers.packageManager);
|
|
12942
|
+
const cd = answers.directory === "." ? [] : [`cd ${answers.directory}`];
|
|
12943
|
+
try {
|
|
12944
|
+
await eject(root);
|
|
12945
|
+
await updatePackageScripts(root);
|
|
12946
|
+
logger.success("Ejected to a standalone Astro project.");
|
|
12947
|
+
const steps2 = [...cd, commands.install, commands.dev];
|
|
12948
|
+
logger.box(`Next steps:
|
|
12949
|
+
|
|
12950
|
+
${steps2.join(`
|
|
12951
|
+
`)}
|
|
12952
|
+
`);
|
|
12953
|
+
} catch (error) {
|
|
12954
|
+
logger.warn(`Scaffolded, but eject needs the project's dependencies installed to load blume.config.ts: ${error.message}`);
|
|
12955
|
+
const steps2 = [
|
|
12956
|
+
...cd,
|
|
12957
|
+
commands.install,
|
|
12958
|
+
`${commands.exec} blume eject --yes`
|
|
12959
|
+
];
|
|
12960
|
+
logger.box(`Next steps:
|
|
12961
|
+
|
|
12962
|
+
${steps2.join(`
|
|
12963
|
+
`)}
|
|
12964
|
+
`);
|
|
12965
|
+
}
|
|
12966
|
+
};
|
|
11869
12967
|
var initCommand = defineCommand7({
|
|
11870
12968
|
args: {
|
|
11871
12969
|
"content-dir": {
|
|
11872
|
-
default: "docs",
|
|
11873
12970
|
description: "Content directory.",
|
|
11874
12971
|
type: "string"
|
|
11875
12972
|
},
|
|
12973
|
+
dir: {
|
|
12974
|
+
description: "Directory to scaffold into (default: current directory).",
|
|
12975
|
+
required: false,
|
|
12976
|
+
type: "positional"
|
|
12977
|
+
},
|
|
11876
12978
|
eject: {
|
|
11877
12979
|
description: "Eject to a standalone Astro project after scaffolding.",
|
|
11878
12980
|
type: "boolean"
|
|
@@ -11885,66 +12987,74 @@ var initCommand = defineCommand7({
|
|
|
11885
12987
|
description: "Starter template: docs | api | sdk | changelog.",
|
|
11886
12988
|
type: "string"
|
|
11887
12989
|
},
|
|
11888
|
-
yes: {
|
|
12990
|
+
yes: {
|
|
12991
|
+
description: "Skip prompts and scaffold with defaults.",
|
|
12992
|
+
type: "boolean"
|
|
12993
|
+
}
|
|
11889
12994
|
},
|
|
11890
12995
|
meta: {
|
|
11891
12996
|
description: "Scaffold a minimal Blume project.",
|
|
11892
12997
|
name: "init"
|
|
11893
12998
|
},
|
|
11894
12999
|
async run({ args }) {
|
|
11895
|
-
const
|
|
11896
|
-
const
|
|
11897
|
-
if (
|
|
11898
|
-
logger.error(`Invalid --content-dir "${contentDir}" (must be a path inside the project).`);
|
|
11899
|
-
process.exit(1);
|
|
11900
|
-
}
|
|
11901
|
-
const template = args.template ?? "docs";
|
|
11902
|
-
if (!TEMPLATES.includes(template)) {
|
|
13000
|
+
const cwd = process.cwd();
|
|
13001
|
+
const template = args.template;
|
|
13002
|
+
if (template !== undefined && !TEMPLATES.includes(template)) {
|
|
11903
13003
|
logger.error(`Unknown template "${args.template}" (use ${TEMPLATES.join(" | ")}).`);
|
|
11904
13004
|
process.exit(1);
|
|
11905
13005
|
}
|
|
11906
|
-
const pm = args["package-manager"]
|
|
11907
|
-
if (!PACKAGE_MANAGERS.includes(pm)) {
|
|
13006
|
+
const pm = args["package-manager"];
|
|
13007
|
+
if (pm !== undefined && !PACKAGE_MANAGERS.includes(pm)) {
|
|
11908
13008
|
logger.error(`Unknown package manager "${args["package-manager"]}" (use ${PACKAGE_MANAGERS.join(" | ")}).`);
|
|
11909
13009
|
process.exit(1);
|
|
11910
13010
|
}
|
|
11911
|
-
const
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
13011
|
+
const interactive = !args.yes && process.stdin.isTTY === true && clack.isTTY(process.stdout) && !clack.isCI();
|
|
13012
|
+
let answers;
|
|
13013
|
+
if (interactive) {
|
|
13014
|
+
clack.intro("blume init");
|
|
13015
|
+
const collected = await collectAnswers(clack, {
|
|
13016
|
+
contentDir: args["content-dir"],
|
|
13017
|
+
directory: args.dir,
|
|
13018
|
+
packageManager: pm,
|
|
13019
|
+
template
|
|
13020
|
+
}, { cwd, userAgent: process.env.npm_config_user_agent });
|
|
13021
|
+
if (collected === null) {
|
|
13022
|
+
clack.cancel("Cancelled — nothing was written.");
|
|
13023
|
+
process.exit(0);
|
|
13024
|
+
}
|
|
13025
|
+
answers = collected;
|
|
13026
|
+
} else {
|
|
13027
|
+
answers = {
|
|
13028
|
+
contentDir: args["content-dir"] ?? "docs",
|
|
13029
|
+
directory: args.dir ?? ".",
|
|
13030
|
+
packageManager: pm ?? detectPackageManager(process.env.npm_config_user_agent),
|
|
13031
|
+
sources: ["filesystem"],
|
|
13032
|
+
template: template ?? "docs",
|
|
13033
|
+
title: "My Docs"
|
|
13034
|
+
};
|
|
13035
|
+
}
|
|
13036
|
+
const root = resolve9(cwd, answers.directory);
|
|
13037
|
+
if (validateContentDir(root, answers.contentDir) !== undefined) {
|
|
13038
|
+
logger.error(`Invalid --content-dir "${answers.contentDir}" (must be a path inside the project).`);
|
|
13039
|
+
process.exit(1);
|
|
13040
|
+
}
|
|
13041
|
+
const sink = interactive ? clack.log : logger;
|
|
13042
|
+
const { createdPackage } = await applyPlan(buildPlan(root, answers), sink);
|
|
11915
13043
|
const ignored = await ensureGitignore(root, [".blume/", "dist/"]);
|
|
11916
13044
|
if (ignored.length > 0) {
|
|
11917
|
-
|
|
13045
|
+
sink.success(`Added ${ignored.join(", ")} to .gitignore`);
|
|
11918
13046
|
}
|
|
11919
|
-
const commands = commandsFor(pm);
|
|
11920
13047
|
if (args.eject) {
|
|
11921
|
-
|
|
11922
|
-
await eject(root);
|
|
11923
|
-
logger.success("Ejected to a standalone Astro project.");
|
|
11924
|
-
logger.box(`Next steps:
|
|
11925
|
-
|
|
11926
|
-
${commands.install}
|
|
11927
|
-
npx astro dev
|
|
11928
|
-
`);
|
|
11929
|
-
} catch (error) {
|
|
11930
|
-
logger.warn(`Scaffolded, but eject failed: ${error.message}`);
|
|
11931
|
-
logger.box(`Next steps:
|
|
11932
|
-
|
|
11933
|
-
${commands.install}
|
|
11934
|
-
blume eject --yes
|
|
11935
|
-
`);
|
|
11936
|
-
}
|
|
13048
|
+
await ejectScaffold(root, answers);
|
|
11937
13049
|
return;
|
|
11938
13050
|
}
|
|
11939
|
-
const
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
`;
|
|
11947
|
-
logger.box(nextSteps);
|
|
13051
|
+
const steps2 = nextSteps(answers, createdPackage);
|
|
13052
|
+
if (interactive) {
|
|
13053
|
+
clack.note(steps2.trimEnd());
|
|
13054
|
+
clack.outro("You're all set.");
|
|
13055
|
+
} else {
|
|
13056
|
+
logger.box(steps2);
|
|
13057
|
+
}
|
|
11948
13058
|
}
|
|
11949
13059
|
});
|
|
11950
13060
|
|
|
@@ -11974,7 +13084,7 @@ var previewCommand = defineCommand8({
|
|
|
11974
13084
|
logLevel: "info",
|
|
11975
13085
|
root: context.outDir,
|
|
11976
13086
|
server: {
|
|
11977
|
-
host: args.host
|
|
13087
|
+
host: normalizeHost(args.host),
|
|
11978
13088
|
port: parsePort(args.port)
|
|
11979
13089
|
}
|
|
11980
13090
|
});
|
|
@@ -12030,7 +13140,7 @@ import { join as join33 } from "pathe";
|
|
|
12030
13140
|
|
|
12031
13141
|
// src/core/links.ts
|
|
12032
13142
|
import { existsSync as existsSync20 } from "node:fs";
|
|
12033
|
-
import { basename as
|
|
13143
|
+
import { basename as basename6, join as join32 } from "pathe";
|
|
12034
13144
|
var HTTP = /^https?:\/\//iu;
|
|
12035
13145
|
var PROTOCOL_RELATIVE = /^\/\//u;
|
|
12036
13146
|
var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
|
|
@@ -12050,10 +13160,8 @@ var STATUS_GONE = 410;
|
|
|
12050
13160
|
var STATUS_METHOD_NOT_ALLOWED = 405;
|
|
12051
13161
|
var STATUS_NOT_IMPLEMENTED = 501;
|
|
12052
13162
|
var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null && existsSync20(join32(ctx.publicDir, resolved));
|
|
12053
|
-
var
|
|
12054
|
-
|
|
12055
|
-
return /^index\.(?:md|mdx)$/iu.test(basename5(ref));
|
|
12056
|
-
};
|
|
13163
|
+
var NUMERIC_PREFIX3 = /^\d+[-_.]/u;
|
|
13164
|
+
var isIndexPage = (page2) => /^index\.(?:md|mdx)$/iu.test(basename6(page2.navPath).replace(NUMERIC_PREFIX3, ""));
|
|
12057
13165
|
var applyRelativePart = (segments, part) => {
|
|
12058
13166
|
if (part === "" || part === ".") {
|
|
12059
13167
|
return;
|
|
@@ -12107,6 +13215,9 @@ var checkPathLink = (resolved, fragment, target, site, ctx) => {
|
|
|
12107
13215
|
if (ctx.routes.has(route)) {
|
|
12108
13216
|
return fragment ? checkAnchor(route, fragment, site, ctx) : null;
|
|
12109
13217
|
}
|
|
13218
|
+
if (ctx.extraRoutes.has(route)) {
|
|
13219
|
+
return null;
|
|
13220
|
+
}
|
|
12110
13221
|
if (ctx.redirects.has(route)) {
|
|
12111
13222
|
return null;
|
|
12112
13223
|
}
|
|
@@ -12244,6 +13355,7 @@ var validateLinks = async (graph, options) => {
|
|
|
12244
13355
|
const ctx = {
|
|
12245
13356
|
anchors: buildAnchorIndex(graph.pages),
|
|
12246
13357
|
basePath,
|
|
13358
|
+
extraRoutes: new Set((options.extraRoutes ?? []).map(toRoute)),
|
|
12247
13359
|
publicDir: options.publicDir,
|
|
12248
13360
|
redirects: new Set((options.redirects ?? []).map((redirect) => toRoute(withBasePath(basePath, redirect.from)))),
|
|
12249
13361
|
routes: new Set(graph.routes.keys())
|
|
@@ -12300,10 +13412,24 @@ var validateCommand = defineCommand10({
|
|
|
12300
13412
|
try {
|
|
12301
13413
|
const project = await scanProject(root, { mode: "build" });
|
|
12302
13414
|
diagnostics.push(...project.diagnostics);
|
|
13415
|
+
const userPages = project.context.pagesRoot ? await discoverPages(project.context.pagesRoot) : [];
|
|
13416
|
+
const extraRoutes = customStaticRoutes(userPages);
|
|
13417
|
+
if (hasGeneratedChangelog(project, userPages)) {
|
|
13418
|
+
extraRoutes.push("/changelog");
|
|
13419
|
+
}
|
|
13420
|
+
if (project.config.i18n) {
|
|
13421
|
+
const manifest = buildManifest({
|
|
13422
|
+
config: project.config,
|
|
13423
|
+
context: project.context,
|
|
13424
|
+
graph: project.graph
|
|
13425
|
+
});
|
|
13426
|
+
extraRoutes.push(...manifest.routes.flatMap((route) => route.fallback ? [route.path] : []));
|
|
13427
|
+
}
|
|
12303
13428
|
const publicDir = join33(root, "public");
|
|
12304
13429
|
diagnostics.push(...await validateLinks(project.graph, {
|
|
12305
13430
|
basePath: project.config.basePath,
|
|
12306
13431
|
checkExternal: Boolean(args.external),
|
|
13432
|
+
extraRoutes,
|
|
12307
13433
|
publicDir: existsSync21(publicDir) ? publicDir : null,
|
|
12308
13434
|
redirects: project.config.redirects
|
|
12309
13435
|
}));
|
|
@@ -12315,9 +13441,10 @@ var validateCommand = defineCommand10({
|
|
|
12315
13441
|
process.exit(1);
|
|
12316
13442
|
}
|
|
12317
13443
|
}
|
|
13444
|
+
const strictFailure = Boolean(args.strict) && diagnostics.some((diagnostic) => diagnostic.severity !== "info");
|
|
12318
13445
|
if (args.json) {
|
|
12319
13446
|
const hadErrors2 = reportDiagnosticsJson(diagnostics, root);
|
|
12320
|
-
if (hadErrors2 ||
|
|
13447
|
+
if (hadErrors2 || strictFailure) {
|
|
12321
13448
|
await flushStdout();
|
|
12322
13449
|
process.exit(1);
|
|
12323
13450
|
}
|
|
@@ -12327,7 +13454,7 @@ var validateCommand = defineCommand10({
|
|
|
12327
13454
|
if (diagnostics.length === 0) {
|
|
12328
13455
|
logger.success("No broken links found.");
|
|
12329
13456
|
}
|
|
12330
|
-
if (hadErrors ||
|
|
13457
|
+
if (hadErrors || strictFailure) {
|
|
12331
13458
|
process.exit(1);
|
|
12332
13459
|
}
|
|
12333
13460
|
}
|
|
@@ -12364,5 +13491,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
12364
13491
|
});
|
|
12365
13492
|
runMain(main);
|
|
12366
13493
|
|
|
12367
|
-
//# debugId=
|
|
13494
|
+
//# debugId=0106CF055BC5BCA264756E2164756E21
|
|
12368
13495
|
//# sourceMappingURL=index.js.map
|