@lupinum/ginko-docs 0.4.0-rc.9 → 0.4.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 +78 -0
- package/LICENSE +1 -1
- package/README.md +206 -54
- package/app/app.config.ts +42 -55
- package/app/assets/docs.css +555 -0
- package/app/components/DocsBanner.vue +29 -0
- package/app/components/DocsExample.vue +43 -0
- package/app/components/DocsFooter.vue +19 -0
- package/app/components/DocsHeader.vue +112 -0
- package/app/components/DocsLanding.vue +55 -0
- package/app/components/DocsLanguage.vue +29 -0
- package/app/components/DocsNavTree.vue +38 -0
- package/app/components/DocsPage.vue +84 -0
- package/app/components/DocsPageActions.vue +47 -0
- package/app/components/DocsPost.vue +58 -0
- package/app/components/DocsSearch.vue +134 -0
- package/app/components/DocsShell.vue +20 -0
- package/app/components/DocsSidebar.vue +18 -0
- package/app/components/DocsTable.vue +11 -0
- package/app/components/DocsThemeToggle.vue +45 -0
- package/app/components/DocsToc.vue +73 -0
- package/app/composables/useDocs.ts +52 -0
- package/app/composables/useDocsBlog.ts +32 -0
- package/app/composables/useDocsCopy.ts +17 -0
- package/app/composables/useDocsPopover.ts +36 -0
- package/app/composables/useDocsSite.ts +10 -0
- package/app/docs-pages/blog.vue +47 -0
- package/app/docs-pages/changelog.vue +27 -0
- package/app/docs-pages/doc.vue +14 -0
- package/app/docs-pages/docs.vue +9 -0
- package/app/docs-pages/index.vue +12 -0
- package/app/docs-pages/post.vue +8 -0
- package/app/error.vue +23 -115
- package/app/layouts/default.vue +25 -7
- package/app/lib/blogTypes.ts +8 -0
- package/app/lib/docsFirst.ts +11 -0
- package/app/lib/docsInit.ts +18 -0
- package/app/lib/docsNav.ts +36 -0
- package/app/lib/docsPage.ts +44 -0
- package/app/lib/markdownComponents.ts +16 -0
- package/app/lib/previewToc.ts +18 -0
- package/dist/config.d.mts +232 -0
- package/dist/config.d.ts +232 -0
- package/dist/config.mjs +114 -0
- package/dist/pages.d.mts +5 -0
- package/dist/pages.d.ts +5 -0
- package/dist/pages.mjs +36 -0
- package/dist/shared/ginko-docs.BwbF7JBR.mjs +4 -0
- package/i18n/de.json +1 -0
- package/i18n/en.json +1 -0
- package/icons/README.md +2 -0
- package/icons/bluesky.svg +1 -0
- package/icons/claude.svg +1 -0
- package/icons/discord.svg +1 -0
- package/icons/github.svg +1 -0
- package/icons/linkedin.svg +1 -0
- package/icons/mastodon.svg +1 -0
- package/icons/openai.svg +1 -0
- package/icons/x.svg +1 -0
- package/icons/youtube.svg +1 -0
- package/nuxt.config.ts +34 -209
- package/package.json +43 -76
- package/public/_docs/fonts/LICENSE +188 -0
- package/public/_docs/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/public/_docs/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/public/_docs/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
- package/server/feeds/blog-locale.get.ts +9 -0
- package/server/feeds/blog.get.ts +4 -0
- package/server/lib/blogFeed.ts +35 -0
- package/server/lib/rss.ts +57 -0
- package/server/mcp/handler.ts +108 -0
- package/server/routes/__ginko/docs-mcp.ts +35 -0
- package/shared/paths.ts +4 -0
- package/app/app.vue +0 -98
- package/app/assets/css/prose.css +0 -3240
- package/app/assets/css/tailwind.css +0 -394
- package/app/assets/css/theme-palettes.css +0 -309
- package/app/assets/css/theme-presets.css +0 -141
- package/app/components/OgImage/GinkoDocs.satori.vue +0 -238
- package/app/components/content/DocumentPageShell.vue +0 -55
- package/app/components/content/Feedback.vue +0 -102
- package/app/components/content/ImageZoomDialog.vue +0 -84
- package/app/components/content/PageMarkdownCopy.vue +0 -104
- package/app/components/content/imageZoom.ts +0 -25
- package/app/components/mdc/MdcAccordion.vue +0 -67
- package/app/components/mdc/MdcAccordionItem.vue +0 -42
- package/app/components/mdc/MdcApi.vue +0 -159
- package/app/components/mdc/MdcAside.vue +0 -23
- package/app/components/mdc/MdcCallout.vue +0 -61
- package/app/components/mdc/MdcCard.vue +0 -175
- package/app/components/mdc/MdcCards.vue +0 -30
- package/app/components/mdc/MdcCenter.vue +0 -71
- package/app/components/mdc/MdcCodeGroup.vue +0 -197
- package/app/components/mdc/MdcCodeTree.vue +0 -234
- package/app/components/mdc/MdcCollapse.vue +0 -61
- package/app/components/mdc/MdcColumn.vue +0 -70
- package/app/components/mdc/MdcDropcap.vue +0 -84
- package/app/components/mdc/MdcError.vue +0 -23
- package/app/components/mdc/MdcExcerpt.vue +0 -26
- package/app/components/mdc/MdcFigure.vue +0 -85
- package/app/components/mdc/MdcFiles.vue +0 -120
- package/app/components/mdc/MdcIdea.vue +0 -17
- package/app/components/mdc/MdcInfo.vue +0 -17
- package/app/components/mdc/MdcInlineToc.vue +0 -51
- package/app/components/mdc/MdcKbd.vue +0 -10
- package/app/components/mdc/MdcLayout.vue +0 -57
- package/app/components/mdc/MdcNote.vue +0 -17
- package/app/components/mdc/MdcQuiz.vue +0 -241
- package/app/components/mdc/MdcQuizQuestion.vue +0 -221
- package/app/components/mdc/MdcReadMore.vue +0 -70
- package/app/components/mdc/MdcSteps.vue +0 -59
- package/app/components/mdc/MdcSuccess.vue +0 -23
- package/app/components/mdc/MdcTab.vue +0 -17
- package/app/components/mdc/MdcTabs.vue +0 -150
- package/app/components/mdc/MdcTimeline.vue +0 -17
- package/app/components/mdc/MdcTimelineItem.vue +0 -40
- package/app/components/mdc/MdcWarning.vue +0 -23
- package/app/components/mdc/api.utils.ts +0 -132
- package/app/components/mdc/code-tree.utils.ts +0 -22
- package/app/components/mdc/icons.ts +0 -10
- package/app/components/mdc/quiz.utils.ts +0 -38
- package/app/components/prose/ProseA.vue +0 -27
- package/app/components/prose/ProseBlockquote.vue +0 -30
- package/app/components/prose/ProseCode.vue +0 -14
- package/app/components/prose/ProseEm.vue +0 -14
- package/app/components/prose/ProseH1.vue +0 -18
- package/app/components/prose/ProseH2.vue +0 -29
- package/app/components/prose/ProseH3.vue +0 -29
- package/app/components/prose/ProseH4.vue +0 -29
- package/app/components/prose/ProseH5.vue +0 -15
- package/app/components/prose/ProseH6.vue +0 -15
- package/app/components/prose/ProseHr.vue +0 -12
- package/app/components/prose/ProseImg.vue +0 -55
- package/app/components/prose/ProseLi.vue +0 -14
- package/app/components/prose/ProseOl.vue +0 -14
- package/app/components/prose/ProseP.vue +0 -14
- package/app/components/prose/ProsePre.vue +0 -180
- package/app/components/prose/ProseStrong.vue +0 -14
- package/app/components/prose/ProseTable.vue +0 -18
- package/app/components/prose/ProseTbody.vue +0 -14
- package/app/components/prose/ProseTd.vue +0 -14
- package/app/components/prose/ProseTh.vue +0 -14
- package/app/components/prose/ProseThead.vue +0 -14
- package/app/components/prose/ProseTr.vue +0 -14
- package/app/components/prose/ProseUl.vue +0 -14
- package/app/components/site/ModeToggle.vue +0 -81
- package/app/components/site/SiteBanner.vue +0 -97
- package/app/components/site/SiteFooter.vue +0 -90
- package/app/components/site/SiteHeader.vue +0 -371
- package/app/components/site/SiteHeroCode.vue +0 -173
- package/app/components/site/SiteInteractionLayer.vue +0 -42
- package/app/components/site/SiteLocaleSwitcher.vue +0 -190
- package/app/components/site/SiteLogoMark.vue +0 -12
- package/app/components/site/SiteSkipLink.vue +0 -14
- package/app/components/site/SiteSocialLinks.vue +0 -33
- package/app/components/site/header-utils.ts +0 -3
- package/app/components/site/mode-toggle.utils.ts +0 -18
- package/app/components/ui/badge/Badge.vue +0 -28
- package/app/components/ui/badge/index.ts +0 -24
- package/app/components/ui/button/Button.vue +0 -31
- package/app/components/ui/button/index.ts +0 -35
- package/app/components/ui/collapsible/Collapsible.vue +0 -15
- package/app/components/ui/collapsible/CollapsibleContent.vue +0 -25
- package/app/components/ui/collapsible/CollapsibleTrigger.vue +0 -12
- package/app/components/ui/collapsible/index.ts +0 -3
- package/app/components/ui/dropdown-menu/DropdownMenu.vue +0 -15
- package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +0 -50
- package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +0 -41
- package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +0 -15
- package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +0 -37
- package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +0 -23
- package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +0 -14
- package/app/components/ui/dropdown-menu/index.ts +0 -7
- package/app/components/ui/kbd/Kbd.vue +0 -23
- package/app/components/ui/kbd/index.ts +0 -21
- package/app/components/ui/scroll-area/ScrollArea.vue +0 -39
- package/app/components/ui/scroll-area/ScrollBar.vue +0 -33
- package/app/components/ui/scroll-area/index.ts +0 -2
- package/app/components/ui/separator/Separator.vue +0 -27
- package/app/components/ui/separator/index.ts +0 -1
- package/app/components/ui/sheet/Sheet.vue +0 -15
- package/app/components/ui/sheet/SheetContent.vue +0 -59
- package/app/components/ui/sheet/SheetDescription.vue +0 -21
- package/app/components/ui/sheet/SheetOverlay.vue +0 -26
- package/app/components/ui/sheet/SheetTitle.vue +0 -21
- package/app/components/ui/sheet/SheetTrigger.vue +0 -12
- package/app/components/ui/sheet/index.ts +0 -5
- package/app/components/ui/tabs/Tabs.vue +0 -24
- package/app/components/ui/tabs/TabsList.vue +0 -26
- package/app/components/ui/tabs/TabsTrigger.vue +0 -28
- package/app/components/ui/tabs/index.ts +0 -3
- package/app/composables/site-navigation.utils.ts +0 -133
- package/app/composables/useCanonicalUrl.ts +0 -9
- package/app/composables/useContentRouteAlternates.ts +0 -35
- package/app/composables/useGinkoAnalytics.ts +0 -52
- package/app/composables/useGinkoDocsConfig.ts +0 -6
- package/app/composables/useGinkoOgImage.ts +0 -20
- package/app/composables/useLocalizedPath.ts +0 -12
- package/app/composables/useLocalizedRouteSwitch.ts +0 -31
- package/app/composables/useMetaKey.ts +0 -18
- package/app/composables/usePageMarkdownActions.ts +0 -79
- package/app/composables/useProseAppearance.ts +0 -24
- package/app/composables/useSchemaJsonLd.ts +0 -25
- package/app/composables/useSiteNavigation.ts +0 -64
- package/app/config/site.utils.ts +0 -36
- package/app/features/docs/components/DocsBreadcrumb.vue +0 -37
- package/app/features/docs/components/DocsContributeLinks.vue +0 -75
- package/app/features/docs/components/DocsMobileToc.vue +0 -108
- package/app/features/docs/components/DocsPageContent.vue +0 -234
- package/app/features/docs/components/DocsPageNav.vue +0 -57
- package/app/features/docs/components/DocsSidebar.vue +0 -145
- package/app/features/docs/components/DocsSidebarDropdown.vue +0 -70
- package/app/features/docs/components/DocsSidebarItem.vue +0 -122
- package/app/features/docs/components/DocsSidebarList.vue +0 -35
- package/app/features/docs/components/DocsSidebarRow.vue +0 -27
- package/app/features/docs/components/DocsSidebarTabs.vue +0 -59
- package/app/features/docs/components/DocsToc.vue +0 -106
- package/app/features/docs/composables/useDocsNavigation.ts +0 -29
- package/app/features/docs/composables/useDocsNavigationData.ts +0 -18
- package/app/features/docs/composables/useRevealActive.ts +0 -58
- package/app/features/docs/composables/useScrollspy.ts +0 -135
- package/app/features/docs/docs-navigation.ts +0 -157
- package/app/features/docs/toc-context.ts +0 -6
- package/app/features/search/command-center.ts +0 -110
- package/app/features/search/components/SiteCommandCenter.vue +0 -359
- package/app/features/search/components/SiteSearchPageHighlight.vue +0 -126
- package/app/features/search/useCommandCenter.ts +0 -292
- package/app/layouts/blog.vue +0 -15
- package/app/layouts/docs.vue +0 -20
- package/app/lib/errors.ts +0 -37
- package/app/lib/schema-org.ts +0 -38
- package/app/pages/blog/[slug].vue +0 -228
- package/app/pages/blog/index.vue +0 -130
- package/app/pages/docs/[...slug].vue +0 -10
- package/app/pages/docs/index.vue +0 -19
- package/app/pages/index.vue +0 -278
- package/app/plugins/ginko-docs-theme.ts +0 -14
- package/app/plugins/ssr-width.ts +0 -6
- package/app/utils/content.ts +0 -67
- package/app/utils/file-icons.ts +0 -90
- package/app/utils/index.ts +0 -7
- package/app/utils/repository.ts +0 -20
- package/components.ts +0 -9
- package/content-collections.ts +0 -89
- package/content.js +0 -200
- package/content.ts +0 -104
- package/i18n/i18n.config.ts +0 -10
- package/i18n/locales.ts +0 -81
- package/i18n/messages/global/banner.ts +0 -8
- package/i18n/messages/global/blog.ts +0 -18
- package/i18n/messages/global/command.ts +0 -49
- package/i18n/messages/global/docs.ts +0 -36
- package/i18n/messages/global/errors.ts +0 -36
- package/i18n/messages/global/feedback.ts +0 -11
- package/i18n/messages/global/index.ts +0 -21
- package/i18n/messages/global/nav.ts +0 -26
- package/i18n/messages/global/site.ts +0 -4
- package/i18n/messages/global/theme.ts +0 -9
- package/i18n/messages/index.ts +0 -20
- package/i18n/messages/localize.ts +0 -35
- package/i18n/routes.ts +0 -33
- package/icon-bundle.ts +0 -116
- package/index.d.ts +0 -37
- package/modules/analytics-boundary.ts +0 -23
- package/modules/feature-routing.ts +0 -68
- package/modules/syntax-highlighting.ts +0 -120
- package/public/fonts/public-sans/LICENSE +0 -93
- package/public/fonts/public-sans/README.md +0 -12
- package/public/fonts/public-sans/public-sans-400-normal-latin.woff +0 -0
- package/public/fonts/public-sans/public-sans-500-normal-latin.woff +0 -0
- package/public/fonts/public-sans/public-sans-600-normal-latin.woff +0 -0
- package/public/fonts/public-sans/public-sans-700-normal-latin.woff +0 -0
- package/runtime/generate-favicons.mjs +0 -124
- package/runtime/server/agent-markdown.ts +0 -19
- package/server/api/_ginko-docs/redirects.get.ts +0 -28
- package/server/mcp/tools/get-page.ts +0 -14
- package/server/mcp/tools/list-pages.ts +0 -10
- package/server/middleware/redirects.ts +0 -22
- package/server/plugins/agent-markdown.ts +0 -6
- package/server/plugins/sitemap-policy.ts +0 -12
- package/server/routes/.well-known/ai-catalog.json.get.ts +0 -10
- package/server/routes/[locale]/blog/rss.xml.get.ts +0 -13
- package/server/routes/blog/rss.xml.get.ts +0 -5
- package/server/routes/mcp/server-card.get.ts +0 -14
- package/server/utils/blog-feed.ts +0 -51
- package/server/utils/feed.ts +0 -70
- package/server/utils/mcp-server-card.ts +0 -45
- package/server/utils/redirects.ts +0 -38
- package/server/utils/redirects.utils.ts +0 -58
- package/server/utils/sitemap.ts +0 -45
- package/shared/route-slugs.ts +0 -5
- package/shared/theme-palettes.ts +0 -37
- package/shared/theme-presets.ts +0 -3
- package/shared/types/app-config.ts +0 -172
- package/shared/types/nuxt-config.ts +0 -14
- package/tags.ts +0 -192
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
-
import { extname, join, resolve } from "node:path";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import sharp from "sharp";
|
|
7
|
-
|
|
8
|
-
const ICO_SIZES = [16, 32, 48];
|
|
9
|
-
|
|
10
|
-
function createIco(images) {
|
|
11
|
-
const headerSize = 6;
|
|
12
|
-
const entrySize = 16;
|
|
13
|
-
const directory = Buffer.alloc(headerSize + entrySize * images.length);
|
|
14
|
-
|
|
15
|
-
directory.writeUInt16LE(0, 0);
|
|
16
|
-
directory.writeUInt16LE(1, 2);
|
|
17
|
-
directory.writeUInt16LE(images.length, 4);
|
|
18
|
-
|
|
19
|
-
let imageOffset = directory.length;
|
|
20
|
-
images.forEach(({ size, data }, index) => {
|
|
21
|
-
const entryOffset = headerSize + entrySize * index;
|
|
22
|
-
directory.writeUInt8(size === 256 ? 0 : size, entryOffset);
|
|
23
|
-
directory.writeUInt8(size === 256 ? 0 : size, entryOffset + 1);
|
|
24
|
-
directory.writeUInt8(0, entryOffset + 2);
|
|
25
|
-
directory.writeUInt8(0, entryOffset + 3);
|
|
26
|
-
directory.writeUInt16LE(1, entryOffset + 4);
|
|
27
|
-
directory.writeUInt16LE(32, entryOffset + 6);
|
|
28
|
-
directory.writeUInt32LE(data.length, entryOffset + 8);
|
|
29
|
-
directory.writeUInt32LE(imageOffset, entryOffset + 12);
|
|
30
|
-
imageOffset += data.length;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return Buffer.concat([directory, ...images.map(({ data }) => data)]);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function renderSquare(source, size) {
|
|
37
|
-
return sharp(source, { density: 512 }).resize(size, size, { fit: "contain" }).png().toBuffer();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function embeddedSvg(png) {
|
|
41
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img">
|
|
42
|
-
<image width="512" height="512" href="data:image/png;base64,${png.toString("base64")}" />
|
|
43
|
-
</svg>
|
|
44
|
-
`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function generateFavicons({ source, outputDirectory }) {
|
|
48
|
-
const sourcePath = resolve(source);
|
|
49
|
-
const outputPath = resolve(outputDirectory);
|
|
50
|
-
const sourceBuffer = await readFile(sourcePath);
|
|
51
|
-
const metadata = await sharp(sourceBuffer, { density: 512 }).metadata();
|
|
52
|
-
|
|
53
|
-
if (!metadata.width || !metadata.height) {
|
|
54
|
-
throw new Error(`Could not read icon dimensions from ${sourcePath}`);
|
|
55
|
-
}
|
|
56
|
-
if (metadata.width !== metadata.height) {
|
|
57
|
-
throw new Error(
|
|
58
|
-
`The favicon source must be square. Received ${metadata.width}x${metadata.height}.`,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const [png96, appleTouchIcon, ...icoPngs] = await Promise.all([
|
|
63
|
-
renderSquare(sourceBuffer, 96),
|
|
64
|
-
renderSquare(sourceBuffer, 180),
|
|
65
|
-
...ICO_SIZES.map(async (size) => ({ size, data: await renderSquare(sourceBuffer, size) })),
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
const svg =
|
|
69
|
-
extname(sourcePath).toLowerCase() === ".svg"
|
|
70
|
-
? sourceBuffer
|
|
71
|
-
: Buffer.from(embeddedSvg(await renderSquare(sourceBuffer, 512)));
|
|
72
|
-
|
|
73
|
-
await mkdir(outputPath, { recursive: true });
|
|
74
|
-
await Promise.all([
|
|
75
|
-
writeFile(join(outputPath, "favicon.svg"), svg),
|
|
76
|
-
writeFile(join(outputPath, "favicon-96x96.png"), png96),
|
|
77
|
-
writeFile(join(outputPath, "apple-touch-icon.png"), appleTouchIcon),
|
|
78
|
-
writeFile(join(outputPath, "favicon.ico"), createIco(icoPngs)),
|
|
79
|
-
]);
|
|
80
|
-
|
|
81
|
-
return ["favicon.svg", "favicon-96x96.png", "apple-touch-icon.png", "favicon.ico"];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function parseArguments(arguments_) {
|
|
85
|
-
const options = {
|
|
86
|
-
source: "public/favicon.svg",
|
|
87
|
-
outputDirectory: "public",
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
for (let index = 0; index < arguments_.length; index += 1) {
|
|
91
|
-
const argument = arguments_[index];
|
|
92
|
-
if (argument === "--source") options.source = arguments_[++index];
|
|
93
|
-
else if (argument === "--out") options.outputDirectory = arguments_[++index];
|
|
94
|
-
else if (argument === "--help" || argument === "-h") return undefined;
|
|
95
|
-
else throw new Error(`Unknown argument: ${argument}`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!options.source || !options.outputDirectory) {
|
|
99
|
-
throw new Error("Both --source and --out require a path.");
|
|
100
|
-
}
|
|
101
|
-
return options;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function main() {
|
|
105
|
-
const options = parseArguments(process.argv.slice(2));
|
|
106
|
-
if (!options) {
|
|
107
|
-
console.log(`Usage: ginko-docs-favicons [--source <path>] [--out <directory>]
|
|
108
|
-
|
|
109
|
-
Defaults:
|
|
110
|
-
--source public/favicon.svg
|
|
111
|
-
--out public`);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const files = await generateFavicons(options);
|
|
116
|
-
console.log(`Generated ${files.join(", ")} in ${resolve(options.outputDirectory)}`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) {
|
|
120
|
-
main().catch((error) => {
|
|
121
|
-
console.error(error instanceof Error ? error.message : error);
|
|
122
|
-
process.exitCode = 1;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
registerAgentMarkdownSerializers,
|
|
3
|
-
type AgentMarkdownSerializer,
|
|
4
|
-
type AgentMarkdownSerializerMap,
|
|
5
|
-
} from "@lupinum/ginko-content/agent-registry";
|
|
6
|
-
import { contentComponentTags } from "../../tags";
|
|
7
|
-
|
|
8
|
-
const renderXmlComponent: AgentMarkdownSerializer = (node, ctx) =>
|
|
9
|
-
ctx.xmlComponent(node.tag || "component", ctx.cleanProps(node), ctx.renderChildren(node));
|
|
10
|
-
|
|
11
|
-
export function registerContentComponentAgentMarkdownSerializers() {
|
|
12
|
-
const serializers: AgentMarkdownSerializerMap = {};
|
|
13
|
-
for (const [tag, component] of Object.entries(contentComponentTags)) {
|
|
14
|
-
serializers[tag] = renderXmlComponent;
|
|
15
|
-
serializers[component] = renderXmlComponent;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
registerAgentMarkdownSerializers(serializers);
|
|
19
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { defineEventHandler, setHeader } from "h3";
|
|
2
|
-
import { loadRedirectMap } from "../../utils/redirects";
|
|
3
|
-
|
|
4
|
-
const escapeHtml = (value: string) =>
|
|
5
|
-
value
|
|
6
|
-
.replaceAll("&", "&")
|
|
7
|
-
.replaceAll("<", "<")
|
|
8
|
-
.replaceAll(">", ">")
|
|
9
|
-
.replaceAll('"', """);
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Prerender seed: returns a link page of every `redirectFrom` source so the
|
|
13
|
-
* crawler visits each old path and materializes its redirect stub. Conflict
|
|
14
|
-
* validation throws here, which fails the build via `failOnError`.
|
|
15
|
-
*/
|
|
16
|
-
export default defineEventHandler(async (event) => {
|
|
17
|
-
const redirects = await loadRedirectMap(event);
|
|
18
|
-
|
|
19
|
-
if (import.meta.prerender) {
|
|
20
|
-
setHeader(event, "content-type", "text/html; charset=utf-8");
|
|
21
|
-
const links = Array.from(redirects.keys())
|
|
22
|
-
.map((source) => `<a href="${escapeHtml(source)}"></a>`)
|
|
23
|
-
.join("");
|
|
24
|
-
return `<!doctype html><html><head><meta charset="utf-8"></head><body>${links}</body></html>`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return { count: redirects.size, redirects: Object.fromEntries(redirects) };
|
|
28
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { defineMcpTool } from "@nuxtjs/mcp-toolkit/server";
|
|
2
|
-
import { agentRawPathForRoute } from "@lupinum/ginko-content/agent-paths";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
export default defineMcpTool({
|
|
6
|
-
description: "Read a documentation page as Markdown.",
|
|
7
|
-
annotations: { readOnlyHint: true },
|
|
8
|
-
inputSchema: {
|
|
9
|
-
path: z.string().startsWith("/").describe("Documentation path, for example /docs/introduction"),
|
|
10
|
-
},
|
|
11
|
-
handler: async ({ path }) => {
|
|
12
|
-
return await $fetch<string>(agentRawPathForRoute(path), { responseType: "text" });
|
|
13
|
-
},
|
|
14
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { defineMcpTool } from "@nuxtjs/mcp-toolkit/server";
|
|
2
|
-
|
|
3
|
-
export default defineMcpTool({
|
|
4
|
-
description: "List the documentation pages available to agents.",
|
|
5
|
-
annotations: { readOnlyHint: true },
|
|
6
|
-
handler: async () => {
|
|
7
|
-
const index = await $fetch<string>("/llms.txt", { responseType: "text" });
|
|
8
|
-
return index;
|
|
9
|
-
},
|
|
10
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { defineEventHandler, sendRedirect } from "h3";
|
|
2
|
-
import { loadRedirectMap } from "../utils/redirects";
|
|
3
|
-
import { normalizeRedirectPath } from "../utils/redirects.utils";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Serves authored `redirectFrom` moves as 301s. During prerender this runs
|
|
7
|
-
* before the catch-all page would SSR a 404 for the crawled old path, so the
|
|
8
|
-
* static build materializes the same meta-refresh stub the docs root uses.
|
|
9
|
-
*/
|
|
10
|
-
export default defineEventHandler(async (event) => {
|
|
11
|
-
if (event.method !== "GET" && event.method !== "HEAD") return;
|
|
12
|
-
|
|
13
|
-
const path = normalizeRedirectPath(event.path);
|
|
14
|
-
const lastSegment = path.slice(path.lastIndexOf("/") + 1);
|
|
15
|
-
if (path.startsWith("/api/") || path.startsWith("/_") || lastSegment.includes(".")) return;
|
|
16
|
-
|
|
17
|
-
const redirects = await loadRedirectMap(event);
|
|
18
|
-
const target = redirects.get(path);
|
|
19
|
-
if (target) {
|
|
20
|
-
return sendRedirect(event, target, 301);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { defineNitroPlugin } from "nitropack/runtime/plugin";
|
|
2
|
-
import { registerContentComponentAgentMarkdownSerializers } from "../../runtime/server/agent-markdown";
|
|
3
|
-
|
|
4
|
-
export default defineNitroPlugin(() => {
|
|
5
|
-
registerContentComponentAgentMarkdownSerializers();
|
|
6
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineNitroPlugin } from "nitropack/runtime/plugin";
|
|
2
|
-
import { filterSitemapEntries } from "../utils/sitemap";
|
|
3
|
-
|
|
4
|
-
export default defineNitroPlugin((nitro) => {
|
|
5
|
-
nitro.hooks.hook("sitemap:resolved", (context) => {
|
|
6
|
-
const content = useRuntimeConfig(context.event).content;
|
|
7
|
-
context.urls = filterSitemapEntries(context.urls, {
|
|
8
|
-
locales: content.locales,
|
|
9
|
-
blogEnabled: Boolean(content.collections.blog),
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { useAppConfig } from "#imports";
|
|
2
|
-
import { createGinkoDocsAiCatalog } from "../../utils/mcp-server-card";
|
|
3
|
-
|
|
4
|
-
export default defineEventHandler((event) => {
|
|
5
|
-
event.node.res.setHeader("access-control-allow-origin", "*");
|
|
6
|
-
event.node.res.setHeader("cache-control", "public, max-age=3600");
|
|
7
|
-
event.node.res.setHeader("content-type", "application/ai-catalog+json; charset=utf-8");
|
|
8
|
-
|
|
9
|
-
return createGinkoDocsAiCatalog(useAppConfig().ginkoDocs.site.url);
|
|
10
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { createError, defineEventHandler, getRouterParam } from "h3";
|
|
2
|
-
import { defaultLocale, isLocaleCode } from "../../../../i18n/locales";
|
|
3
|
-
import { serveBlogFeed } from "../../../utils/blog-feed";
|
|
4
|
-
|
|
5
|
-
// A param route keeps the router free of static locale segments; a static
|
|
6
|
-
// /de/... node would shadow ginko-content's /:locale/llms.txt routes.
|
|
7
|
-
export default defineEventHandler(async (event) => {
|
|
8
|
-
const locale = getRouterParam(event, "locale");
|
|
9
|
-
if (!locale || !isLocaleCode(locale) || locale === defaultLocale) {
|
|
10
|
-
throw createError({ statusCode: 404, statusMessage: "Page not found" });
|
|
11
|
-
}
|
|
12
|
-
return serveBlogFeed(event, locale);
|
|
13
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { useAppConfig } from "#imports";
|
|
2
|
-
import packageMetadata from "../../../package.json" with { type: "json" };
|
|
3
|
-
import { createGinkoDocsServerCard } from "../../utils/mcp-server-card";
|
|
4
|
-
|
|
5
|
-
export default defineEventHandler((event) => {
|
|
6
|
-
event.node.res.setHeader("access-control-allow-origin", "*");
|
|
7
|
-
event.node.res.setHeader("cache-control", "public, max-age=3600");
|
|
8
|
-
event.node.res.setHeader("content-type", "application/mcp-server-card+json; charset=utf-8");
|
|
9
|
-
|
|
10
|
-
return createGinkoDocsServerCard({
|
|
11
|
-
origin: useAppConfig().ginkoDocs.site.url,
|
|
12
|
-
version: packageMetadata.version,
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { H3Event } from "h3";
|
|
2
|
-
import { createError, setHeader } from "h3";
|
|
3
|
-
import { many } from "@lupinum/ginko-content/server";
|
|
4
|
-
import { useAppConfig, useRuntimeConfig } from "#imports";
|
|
5
|
-
import { blog } from "../../i18n/messages/global/blog";
|
|
6
|
-
import { locales, localizedPath, type LocaleCode } from "../../i18n/locales";
|
|
7
|
-
import { routeSlugs } from "../../shared/route-slugs";
|
|
8
|
-
import { getLocalizedSiteText } from "../../app/config/site.utils";
|
|
9
|
-
import { createGinkoDocsCollections } from "../../content-collections";
|
|
10
|
-
import { buildRssFeed } from "./feed";
|
|
11
|
-
|
|
12
|
-
export const MAX_FEED_POSTS = 50;
|
|
13
|
-
const { blog: blogCollection, authors: authorsCollection } = createGinkoDocsCollections(true);
|
|
14
|
-
|
|
15
|
-
export function blogFeedPath(locale: LocaleCode): string {
|
|
16
|
-
return `${localizedPath(locale, routeSlugs.blog[locale])}/rss.xml`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function serveBlogFeed(event: H3Event, locale: LocaleCode) {
|
|
20
|
-
const contentRuntime = useRuntimeConfig(event).public.content as
|
|
21
|
-
| { collections?: Record<string, unknown> }
|
|
22
|
-
| undefined;
|
|
23
|
-
if (!contentRuntime?.collections?.blog) {
|
|
24
|
-
throw createError({ statusCode: 404, statusMessage: "Blog is not enabled" });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const site = useAppConfig().ginkoDocs.site;
|
|
28
|
-
const posts = await many(event, blogCollection, {
|
|
29
|
-
locale,
|
|
30
|
-
fallback: true,
|
|
31
|
-
populate: { author: authorsCollection },
|
|
32
|
-
sort: { date: "desc" },
|
|
33
|
-
limit: MAX_FEED_POSTS,
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
setHeader(event, "content-type", "application/rss+xml; charset=utf-8");
|
|
37
|
-
return buildRssFeed({
|
|
38
|
-
title: `${blog.title[locale]} - ${getLocalizedSiteText(site.name, locale)}`,
|
|
39
|
-
description: blog.description[locale],
|
|
40
|
-
siteUrl: site.url,
|
|
41
|
-
feedPath: blogFeedPath(locale),
|
|
42
|
-
language: locales.find((entry) => entry.code === locale)?.language ?? locale,
|
|
43
|
-
items: posts.map((post) => ({
|
|
44
|
-
title: post.title,
|
|
45
|
-
path: post.route.resolvedPath,
|
|
46
|
-
description: post.description,
|
|
47
|
-
date: post.date,
|
|
48
|
-
authorName: post.author?.name,
|
|
49
|
-
})),
|
|
50
|
-
});
|
|
51
|
-
}
|
package/server/utils/feed.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
export interface RssFeedItem {
|
|
2
|
-
title: string;
|
|
3
|
-
/** Site-relative path of the post, e.g. "/blog/my-post". */
|
|
4
|
-
path: string;
|
|
5
|
-
description: string;
|
|
6
|
-
/** Authored publication date, YYYY-MM-DD. */
|
|
7
|
-
date: string;
|
|
8
|
-
authorName?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface RssFeedInput {
|
|
12
|
-
title: string;
|
|
13
|
-
description: string;
|
|
14
|
-
/** Absolute site origin, e.g. "https://docs.example.com". */
|
|
15
|
-
siteUrl: string;
|
|
16
|
-
/** Site-relative path of the feed itself, e.g. "/blog/rss.xml". */
|
|
17
|
-
feedPath: string;
|
|
18
|
-
language: string;
|
|
19
|
-
items: RssFeedItem[];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function escapeXml(value: string): string {
|
|
23
|
-
return value
|
|
24
|
-
.replaceAll("&", "&")
|
|
25
|
-
.replaceAll("<", "<")
|
|
26
|
-
.replaceAll(">", ">")
|
|
27
|
-
.replaceAll('"', """)
|
|
28
|
-
.replaceAll("'", "'");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function toRfc822(date: string): string {
|
|
32
|
-
return new Date(`${date}T00:00:00Z`).toUTCString();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function buildRssFeed(feed: RssFeedInput): string {
|
|
36
|
-
const base = feed.siteUrl.replace(/\/$/, "");
|
|
37
|
-
const newestDate = feed.items
|
|
38
|
-
.map((item) => item.date)
|
|
39
|
-
.sort()
|
|
40
|
-
.at(-1);
|
|
41
|
-
|
|
42
|
-
const items = feed.items
|
|
43
|
-
.map((item) => {
|
|
44
|
-
const link = `${base}${item.path}`;
|
|
45
|
-
const author = item.authorName
|
|
46
|
-
? `\n <dc:creator>${escapeXml(item.authorName)}</dc:creator>`
|
|
47
|
-
: "";
|
|
48
|
-
return ` <item>
|
|
49
|
-
<title>${escapeXml(item.title)}</title>
|
|
50
|
-
<link>${escapeXml(link)}</link>
|
|
51
|
-
<guid>${escapeXml(link)}</guid>
|
|
52
|
-
<pubDate>${toRfc822(item.date)}</pubDate>
|
|
53
|
-
<description>${escapeXml(item.description)}</description>${author}
|
|
54
|
-
</item>`;
|
|
55
|
-
})
|
|
56
|
-
.join("\n");
|
|
57
|
-
|
|
58
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
59
|
-
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
60
|
-
<channel>
|
|
61
|
-
<title>${escapeXml(feed.title)}</title>
|
|
62
|
-
<link>${escapeXml(`${base}${feed.feedPath.replace(/rss\.xml$/, "")}`)}</link>
|
|
63
|
-
<atom:link href="${escapeXml(`${base}${feed.feedPath}`)}" rel="self" type="application/rss+xml"/>
|
|
64
|
-
<description>${escapeXml(feed.description)}</description>
|
|
65
|
-
<language>${escapeXml(feed.language)}</language>${newestDate ? `\n <lastBuildDate>${toRfc822(newestDate)}</lastBuildDate>` : ""}
|
|
66
|
-
${items}
|
|
67
|
-
</channel>
|
|
68
|
-
</rss>
|
|
69
|
-
`;
|
|
70
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
export interface GinkoDocsServerCardOptions {
|
|
2
|
-
origin: string;
|
|
3
|
-
version: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function createGinkoDocsServerCard({ origin, version }: GinkoDocsServerCardOptions) {
|
|
7
|
-
const baseUrl = new URL(origin);
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
$schema: "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json",
|
|
11
|
-
name: "dev.lupinum/ginko-docs",
|
|
12
|
-
title: "Ginko Docs",
|
|
13
|
-
version,
|
|
14
|
-
description: "Read published Ginko Docs pages through its public MCP tools.",
|
|
15
|
-
websiteUrl: new URL("/docs/features/agent-readable-output", baseUrl).toString(),
|
|
16
|
-
repository: {
|
|
17
|
-
source: "github",
|
|
18
|
-
url: "https://github.com/lupinum-dev/ginko-docs",
|
|
19
|
-
},
|
|
20
|
-
remotes: [
|
|
21
|
-
{
|
|
22
|
-
type: "streamable-http",
|
|
23
|
-
url: new URL("/mcp", baseUrl).toString(),
|
|
24
|
-
supportedProtocolVersions: ["2025-11-25"],
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
} as const;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function createGinkoDocsAiCatalog(origin: string) {
|
|
31
|
-
const baseUrl = new URL(origin);
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
specVersion: "1.0",
|
|
35
|
-
entries: [
|
|
36
|
-
{
|
|
37
|
-
identifier: "urn:air:ginko-docs.lupinum.com:mcp:ginko-docs",
|
|
38
|
-
displayName: "Ginko Docs MCP Server",
|
|
39
|
-
type: "application/mcp-server-card+json",
|
|
40
|
-
description: "Read the public Ginko Docs documentation through MCP.",
|
|
41
|
-
url: new URL("/mcp/server-card", baseUrl).toString(),
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
} as const;
|
|
45
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { H3Event } from "h3";
|
|
2
|
-
import { many } from "@lupinum/ginko-content/server";
|
|
3
|
-
import { useRuntimeConfig } from "#imports";
|
|
4
|
-
import { localeCodes } from "../../i18n/locales";
|
|
5
|
-
import { buildRedirectMap, type RedirectSourceDocument } from "./redirects.utils";
|
|
6
|
-
|
|
7
|
-
async function queryRedirectDocuments(event: H3Event): Promise<RedirectSourceDocument[]> {
|
|
8
|
-
const contentRuntime = useRuntimeConfig(event).public.content as
|
|
9
|
-
| { collections?: Record<string, unknown> }
|
|
10
|
-
| undefined;
|
|
11
|
-
const collections = ["docs", ...(contentRuntime?.collections?.blog ? ["blog"] : [])];
|
|
12
|
-
|
|
13
|
-
const results = await Promise.all(
|
|
14
|
-
collections.flatMap((collection) =>
|
|
15
|
-
localeCodes.map((locale) =>
|
|
16
|
-
// fallback: false — an English page resolved into a German route would
|
|
17
|
-
// otherwise register its redirectFrom entries twice.
|
|
18
|
-
many(event, collection, { locale, fallback: false }),
|
|
19
|
-
),
|
|
20
|
-
),
|
|
21
|
-
);
|
|
22
|
-
return results.flat() as RedirectSourceDocument[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let cachedMap: Promise<Map<string, string>> | undefined;
|
|
26
|
-
|
|
27
|
-
export function loadRedirectMap(event: H3Event): Promise<Map<string, string>> {
|
|
28
|
-
if (import.meta.dev) {
|
|
29
|
-
return queryRedirectDocuments(event).then(buildRedirectMap);
|
|
30
|
-
}
|
|
31
|
-
cachedMap ??= queryRedirectDocuments(event)
|
|
32
|
-
.then(buildRedirectMap)
|
|
33
|
-
.catch((error) => {
|
|
34
|
-
cachedMap = undefined;
|
|
35
|
-
throw error;
|
|
36
|
-
});
|
|
37
|
-
return cachedMap;
|
|
38
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { localeCodes, localizedPath } from "../../i18n/locales";
|
|
2
|
-
import { routeSlugs } from "../../shared/route-slugs";
|
|
3
|
-
|
|
4
|
-
export interface RedirectSourceDocument {
|
|
5
|
-
route: { resolvedPath: string };
|
|
6
|
-
redirectFrom?: string[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function normalizeRedirectPath(path: string): string {
|
|
10
|
-
const trimmed = path.split("?")[0] ?? path;
|
|
11
|
-
if (trimmed === "/") return "/";
|
|
12
|
-
return trimmed.replace(/\/+$/, "");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Routes the theme itself owns; a redirect must never shadow one of them. */
|
|
16
|
-
export function themeStaticRoutes(): string[] {
|
|
17
|
-
return localeCodes.flatMap((locale) => [
|
|
18
|
-
localizedPath(locale, routeSlugs.home[locale]),
|
|
19
|
-
localizedPath(locale, routeSlugs.docs[locale]),
|
|
20
|
-
localizedPath(locale, routeSlugs.blog[locale]),
|
|
21
|
-
]);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Builds old-path → live-path pairs from every document's `redirectFrom`.
|
|
26
|
-
* Conflicts throw so `failOnError` stops the build instead of shipping a
|
|
27
|
-
* redirect that shadows a live page.
|
|
28
|
-
*/
|
|
29
|
-
export function buildRedirectMap(documents: RedirectSourceDocument[]): Map<string, string> {
|
|
30
|
-
const livePaths = new Set(
|
|
31
|
-
documents.map((document) => normalizeRedirectPath(document.route.resolvedPath)),
|
|
32
|
-
);
|
|
33
|
-
const reserved = new Set(themeStaticRoutes().map(normalizeRedirectPath));
|
|
34
|
-
const map = new Map<string, string>();
|
|
35
|
-
const problems: string[] = [];
|
|
36
|
-
|
|
37
|
-
for (const document of documents) {
|
|
38
|
-
const target = normalizeRedirectPath(document.route.resolvedPath);
|
|
39
|
-
for (const source of document.redirectFrom ?? []) {
|
|
40
|
-
const from = normalizeRedirectPath(source);
|
|
41
|
-
if (livePaths.has(from)) {
|
|
42
|
-
problems.push(`"${from}" redirects to "${target}" but is also a live page`);
|
|
43
|
-
} else if (reserved.has(from)) {
|
|
44
|
-
problems.push(`"${from}" redirects to "${target}" but is a theme route`);
|
|
45
|
-
} else if (map.has(from) && map.get(from) !== target) {
|
|
46
|
-
problems.push(`"${from}" is claimed by both "${map.get(from)}" and "${target}"`);
|
|
47
|
-
} else {
|
|
48
|
-
map.set(from, target);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (problems.length) {
|
|
54
|
-
throw new Error(`Invalid redirectFrom entries:\n${problems.map((p) => `- ${p}`).join("\n")}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return map;
|
|
58
|
-
}
|
package/server/utils/sitemap.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { localeCodes, localizedPath } from "../../i18n/locales";
|
|
2
|
-
import { routeSlugs } from "../../shared/route-slugs";
|
|
3
|
-
|
|
4
|
-
interface SitemapAlternative {
|
|
5
|
-
href: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface SitemapEntry {
|
|
9
|
-
loc: string;
|
|
10
|
-
alternatives?: SitemapAlternative[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface SitemapContentPolicy {
|
|
14
|
-
locales: readonly string[];
|
|
15
|
-
blogEnabled: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const pathname = (url: string) => new URL(url, "https://ginko.invalid").pathname;
|
|
19
|
-
|
|
20
|
-
export function filterSitemapEntries<Entry extends SitemapEntry>(
|
|
21
|
-
entries: Entry[],
|
|
22
|
-
policy: SitemapContentPolicy,
|
|
23
|
-
): Entry[] {
|
|
24
|
-
const disabledLocaleRoots = localeCodes
|
|
25
|
-
.filter((locale) => !policy.locales.includes(locale))
|
|
26
|
-
.map((locale) => `/${locale}`);
|
|
27
|
-
const disabledBlogRoots = policy.blogEnabled
|
|
28
|
-
? []
|
|
29
|
-
: localeCodes.map((locale) => localizedPath(locale, routeSlugs.blog[locale]));
|
|
30
|
-
const excludedRoots = [...disabledLocaleRoots, ...disabledBlogRoots];
|
|
31
|
-
const isExcluded = (url: string) => {
|
|
32
|
-
const path = pathname(url);
|
|
33
|
-
return excludedRoots.some((root) => path === root || path.startsWith(`${root}/`));
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
return entries
|
|
37
|
-
.filter((entry) => !isExcluded(entry.loc))
|
|
38
|
-
.map((entry) => {
|
|
39
|
-
if (!entry.alternatives) return entry;
|
|
40
|
-
return {
|
|
41
|
-
...entry,
|
|
42
|
-
alternatives: entry.alternatives.filter((alternative) => !isExcluded(alternative.href)),
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
}
|
package/shared/route-slugs.ts
DELETED
package/shared/theme-palettes.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export const GINKO_DOCS_NEUTRAL_PALETTES = [
|
|
2
|
-
"slate",
|
|
3
|
-
"gray",
|
|
4
|
-
"zinc",
|
|
5
|
-
"neutral",
|
|
6
|
-
"stone",
|
|
7
|
-
"taupe",
|
|
8
|
-
"mauve",
|
|
9
|
-
"mist",
|
|
10
|
-
"olive",
|
|
11
|
-
"custom",
|
|
12
|
-
] as const;
|
|
13
|
-
|
|
14
|
-
export const GINKO_DOCS_PRIMARY_PALETTES = [
|
|
15
|
-
"neutral",
|
|
16
|
-
"red",
|
|
17
|
-
"orange",
|
|
18
|
-
"amber",
|
|
19
|
-
"yellow",
|
|
20
|
-
"lime",
|
|
21
|
-
"green",
|
|
22
|
-
"emerald",
|
|
23
|
-
"teal",
|
|
24
|
-
"cyan",
|
|
25
|
-
"sky",
|
|
26
|
-
"blue",
|
|
27
|
-
"indigo",
|
|
28
|
-
"violet",
|
|
29
|
-
"purple",
|
|
30
|
-
"fuchsia",
|
|
31
|
-
"pink",
|
|
32
|
-
"rose",
|
|
33
|
-
"custom",
|
|
34
|
-
] as const;
|
|
35
|
-
|
|
36
|
-
export type GinkoDocsNeutralPalette = (typeof GINKO_DOCS_NEUTRAL_PALETTES)[number];
|
|
37
|
-
export type GinkoDocsPrimaryPalette = (typeof GINKO_DOCS_PRIMARY_PALETTES)[number];
|
package/shared/theme-presets.ts
DELETED