blume 0.6.7 → 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 +2609 -1041
- package/dist/cli/index.js.map +110 -103
- 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 +47 -0
- package/dist/types/core/config-input.d.ts +110 -12
- package/dist/types/core/config.d.ts +6 -4
- package/dist/types/core/data.d.ts +4 -0
- package/dist/types/core/i18n-ui.d.ts +477 -135
- package/dist/types/core/schema.d.ts +309 -195
- package/dist/types/core/sources/types.d.ts +2 -0
- package/dist/types/core/types.d.ts +6 -1
- 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 +24 -9
- 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 +33 -3
- package/docs/configuration/seo.mdx +2 -2
- package/docs/content/components.mdx +30 -3
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/islands.mdx +8 -0
- package/docs/content/navigation.mdx +3 -3
- package/docs/content/sources.mdx +1 -1
- package/docs/content/syntax.mdx +17 -2
- package/docs/index.mdx +2 -2
- package/docs/reference/cli.mdx +8 -6
- package/package.json +15 -4
- package/skills/blume/SKILL.md +5 -3
- package/skills/blume-update-docs/SKILL.md +3 -2
- package/src/ai/agent-readability.ts +11 -5
- package/src/ai/ask-context.ts +7 -2
- package/src/ai/ask-data.ts +3 -0
- package/src/ai/ask.ts +12 -7
- package/src/ai/component-markdown.ts +461 -0
- package/src/ai/llms.ts +143 -23
- package/src/ai/markdown.ts +35 -6
- package/src/ai/mcp/data.ts +33 -8
- package/src/ai/mcp/discovery.ts +10 -3
- package/src/ai/mcp/server.ts +24 -7
- package/src/ai/visibility.ts +74 -0
- package/src/astro/component-slots.ts +16 -4
- package/src/astro/examples.ts +12 -7
- package/src/astro/generate.ts +393 -189
- package/src/astro/index.ts +5 -1
- package/src/astro/integration.ts +9 -5
- package/src/astro/islands.ts +11 -5
- package/src/astro/markdown-negotiation.ts +2 -2
- package/src/astro/pages.ts +89 -22
- package/src/astro/templates.ts +259 -25
- package/src/blume-modules.d.ts +8 -0
- package/src/cli/commands/build.ts +131 -38
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/dev.ts +71 -17
- package/src/cli/commands/doctor.ts +2 -2
- 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 +44 -2
- package/src/cli/dev-lock.ts +34 -19
- 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/cli/required-secrets.ts +2 -1
- 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/CodeBlock.astro +3 -0
- package/src/components/content/Component.astro +30 -16
- package/src/components/content/Diff.astro +3 -1
- 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 +25 -9
- package/src/components/content/base-href.ts +33 -0
- package/src/components/content/changelog-element.ts +9 -2
- package/src/components/content/diff.ts +12 -6
- package/src/components/content/mermaid-element.ts +10 -2
- package/src/components/index.ts +23 -1
- package/src/components/islands/AskAI.astro +5 -2
- package/src/components/islands/ask-ai.tsx +68 -12
- package/src/components/islands/base-path.ts +28 -0
- package/src/components/islands/hooks.ts +44 -9
- package/src/components/layout/Banner.astro +12 -3
- package/src/components/layout/Breadcrumbs.astro +2 -1
- package/src/components/layout/Favicon.astro +3 -2
- package/src/components/layout/Header.astro +15 -5
- package/src/components/layout/LanguageSwitcher.astro +2 -1
- package/src/components/layout/Logo.astro +13 -4
- package/src/components/layout/NavSelector.astro +2 -1
- package/src/components/layout/NavTree.astro +22 -7
- package/src/components/layout/PageActions.astro +25 -10
- package/src/components/layout/PageFeedback.astro +4 -1
- package/src/components/layout/PageLayout.astro +51 -9
- package/src/components/layout/Pagination.astro +3 -2
- package/src/components/layout/ReferenceLayout.astro +8 -1
- package/src/components/layout/RootLayout.astro +74 -13
- package/src/components/layout/Search.astro +107 -27
- package/src/components/layout/nav-utils.ts +18 -10
- 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/pagefind.ts +3 -0
- 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 +8 -2
- package/src/components/openapi/ApiTagOperations.astro +2 -1
- package/src/components/openapi/Operation.astro +47 -40
- package/src/components/openapi/RequestPanel.astro +8 -2
- package/src/components/openapi/helpers.ts +71 -3
- package/src/components/openapi/panel.ts +1 -1
- package/src/components/openapi/snippets.ts +25 -11
- package/src/core/base-path.ts +94 -0
- package/src/core/builtin-tags.ts +2 -0
- package/src/core/component-overrides.ts +103 -74
- package/src/core/config-input.ts +118 -17
- package/src/core/config.ts +8 -5
- package/src/core/content.ts +2 -0
- package/src/core/data.ts +4 -0
- package/src/core/diagnostics.ts +54 -34
- package/src/core/gitignore.ts +4 -1
- package/src/core/graph.ts +166 -88
- package/src/core/i18n-ui.ts +63 -3
- package/src/core/last-modified.ts +15 -6
- package/src/core/links.ts +69 -25
- package/src/core/manifest.ts +62 -45
- package/src/core/nav-diagnostics.ts +1 -1
- package/src/core/navigation.ts +144 -58
- package/src/core/package-json.ts +17 -2
- package/src/core/project-graph.ts +25 -15
- package/src/core/schema.ts +605 -620
- package/src/core/sources/assets.ts +6 -1
- package/src/core/sources/filesystem.ts +4 -0
- package/src/core/sources/github-releases.ts +2 -1
- package/src/core/sources/mdx-remote.ts +76 -63
- package/src/core/sources/normalize.ts +236 -91
- package/src/core/sources/notion.ts +27 -18
- package/src/core/sources/types.ts +2 -0
- package/src/core/tsconfig-aliases.ts +59 -30
- package/src/core/types.ts +6 -1
- 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 +25 -2
- package/src/deploy/robots.ts +6 -1
- package/src/deploy/rss.ts +10 -3
- package/src/deploy/sitemap.ts +59 -13
- package/src/index.ts +5 -0
- package/src/markdown/base-links.ts +60 -0
- package/src/markdown/code-title.ts +11 -14
- package/src/markdown/index.ts +46 -9
- package/src/markdown/inline-code.ts +14 -4
- package/src/markdown/package-commands.ts +10 -4
- package/src/markdown/themes.ts +24 -0
- package/src/openapi/model.ts +15 -5
- package/src/openapi/parse.ts +21 -0
- package/src/openapi/references.ts +75 -21
- package/src/openapi/render-mdx.ts +11 -6
- package/src/openapi/scalar.ts +32 -16
- package/src/openapi/source.ts +59 -10
- package/src/registry/eject.ts +247 -19
- package/src/registry/registry.ts +0 -3
- package/src/search/build.ts +3 -0
- package/src/search/documents.ts +36 -4
- package/src/search/sync/typesense.ts +6 -4
- package/src/seo/jsonld.ts +28 -17
- package/src/theme/entry.ts +85 -20
package/src/core/schema.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
import type { ComponentMarkdown } from "../ai/component-markdown.ts";
|
|
4
|
+
import { normalizeRoute } from "../openapi/references.ts";
|
|
3
5
|
import { FONT_SLUGS, isFontSlug } from "../theme/fonts.ts";
|
|
6
|
+
import { normalizeBasePath } from "./base-path.ts";
|
|
4
7
|
import { uiLocaleOverridesSchema } from "./i18n-ui.ts";
|
|
5
8
|
import type { ContentSource } from "./sources/types.ts";
|
|
6
9
|
|
|
@@ -18,6 +21,9 @@ import type { ContentSource } from "./sources/types.ts";
|
|
|
18
21
|
/** Icon inputs in serializable contexts (frontmatter, meta files). */
|
|
19
22
|
const iconName = z.string().min(1);
|
|
20
23
|
|
|
24
|
+
/** Default include glob for filesystem-backed content sources. */
|
|
25
|
+
const DEFAULT_CONTENT_GLOB = "**/*.{md,mdx}";
|
|
26
|
+
|
|
21
27
|
const hydrationMode = z.enum(["load", "idle", "visible", "media", "only"]);
|
|
22
28
|
export type HydrationMode = z.infer<typeof hydrationMode>;
|
|
23
29
|
|
|
@@ -33,41 +39,36 @@ const dateSchema = z
|
|
|
33
39
|
// Page frontmatter
|
|
34
40
|
// ---------------------------------------------------------------------------
|
|
35
41
|
|
|
36
|
-
const sidebarMetaSchema = z
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
})
|
|
44
|
-
.strict();
|
|
45
|
-
|
|
46
|
-
const seoMetaSchema = z
|
|
47
|
-
.object({
|
|
48
|
-
canonical: z.string().url().optional(),
|
|
49
|
-
description: z.string().optional(),
|
|
50
|
-
image: z.string().optional(),
|
|
51
|
-
noindex: z.boolean().default(false),
|
|
52
|
-
title: z.string().optional(),
|
|
53
|
-
})
|
|
54
|
-
.strict();
|
|
42
|
+
const sidebarMetaSchema = z.strictObject({
|
|
43
|
+
badge: z.string().optional(),
|
|
44
|
+
hidden: z.boolean().default(false),
|
|
45
|
+
icon: iconName.optional(),
|
|
46
|
+
label: z.string().optional(),
|
|
47
|
+
order: z.number().optional(),
|
|
48
|
+
});
|
|
55
49
|
|
|
56
|
-
const
|
|
57
|
-
.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.
|
|
50
|
+
const seoMetaSchema = z.strictObject({
|
|
51
|
+
// blume bundles Zod 3; top-level `z.url()` is undefined at runtime and
|
|
52
|
+
// schemas must stay dual-compatible with consumer projects on Zod 4.
|
|
53
|
+
// oxlint-disable-next-line react-doctor/zod-v4-prefer-top-level-string-formats
|
|
54
|
+
canonical: z.string().url().optional(),
|
|
55
|
+
description: z.string().optional(),
|
|
56
|
+
image: z.string().optional(),
|
|
57
|
+
noindex: z.boolean().default(false),
|
|
58
|
+
title: z.string().optional(),
|
|
59
|
+
});
|
|
63
60
|
|
|
64
|
-
const
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
const searchMetaSchema = z.strictObject({
|
|
62
|
+
boost: z.number().optional(),
|
|
63
|
+
exclude: z.boolean().default(false),
|
|
64
|
+
tags: z.array(z.string()).optional(),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const changelogMetaSchema = z.strictObject({
|
|
68
|
+
category: z.string().optional(),
|
|
69
|
+
date: dateSchema.optional(),
|
|
70
|
+
version: z.string().optional(),
|
|
71
|
+
});
|
|
71
72
|
|
|
72
73
|
/**
|
|
73
74
|
* A post author: a bare name/handle, or an object with a name plus optional
|
|
@@ -85,34 +86,32 @@ const authorSchema = z.union([
|
|
|
85
86
|
name: z.string(),
|
|
86
87
|
url: z.string().optional(),
|
|
87
88
|
})
|
|
88
|
-
.
|
|
89
|
+
.catchall(z.unknown()),
|
|
89
90
|
]);
|
|
90
91
|
|
|
91
92
|
/** Frontmatter accepted on any content page. */
|
|
92
|
-
const pageMetaBaseSchema = z
|
|
93
|
-
.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
})
|
|
115
|
-
.strict();
|
|
93
|
+
const pageMetaBaseSchema = z.strictObject({
|
|
94
|
+
/** Post author(s) for blog/changelog content; preserved, not yet rendered. */
|
|
95
|
+
authors: z.union([authorSchema, z.array(authorSchema)]).optional(),
|
|
96
|
+
changelog: changelogMetaSchema.optional(),
|
|
97
|
+
/** Publish date for feed-backed content like blog/changelog. */
|
|
98
|
+
date: dateSchema.optional(),
|
|
99
|
+
deprecated: z.boolean().default(false),
|
|
100
|
+
description: z.string().optional(),
|
|
101
|
+
draft: z.boolean().default(false),
|
|
102
|
+
hidden: z.boolean().default(false),
|
|
103
|
+
icon: iconName.optional(),
|
|
104
|
+
/** Overrides the git-derived last-modified date when `lastModified` is on. */
|
|
105
|
+
lastModified: dateSchema.optional(),
|
|
106
|
+
noindex: z.boolean().default(false),
|
|
107
|
+
search: searchMetaSchema.default({}),
|
|
108
|
+
seo: seoMetaSchema.default({}),
|
|
109
|
+
sidebar: sidebarMetaSchema.default({}),
|
|
110
|
+
slug: z.string().optional(),
|
|
111
|
+
title: z.string().optional(),
|
|
112
|
+
// No default: an absent `type` must fall through to `content.defaultType`.
|
|
113
|
+
type: z.string().optional(),
|
|
114
|
+
});
|
|
116
115
|
|
|
117
116
|
export const pageMetaSchema = pageMetaBaseSchema;
|
|
118
117
|
|
|
@@ -133,16 +132,14 @@ export type PageMetaInput = z.input<typeof pageMetaBaseSchema>;
|
|
|
133
132
|
const sidebarDisplaySchema = z.enum(["flat", "group", "page"]);
|
|
134
133
|
export type SidebarDisplay = z.infer<typeof sidebarDisplaySchema>;
|
|
135
134
|
|
|
136
|
-
export const folderMetaSchema = z
|
|
137
|
-
.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
})
|
|
145
|
-
.strict();
|
|
135
|
+
export const folderMetaSchema = z.strictObject({
|
|
136
|
+
collapsed: z.boolean().optional(),
|
|
137
|
+
icon: iconName.optional(),
|
|
138
|
+
order: z.number().optional(),
|
|
139
|
+
/** Explicit child ordering by slug segment (without numeric prefix). */
|
|
140
|
+
pages: z.array(z.string()).optional(),
|
|
141
|
+
title: z.string().optional(),
|
|
142
|
+
});
|
|
146
143
|
|
|
147
144
|
export type FolderMeta = z.infer<typeof folderMetaSchema>;
|
|
148
145
|
|
|
@@ -153,13 +150,11 @@ export type FolderMeta = z.infer<typeof folderMetaSchema>;
|
|
|
153
150
|
/** The logo mark: a single image path/URL, or light/dark variants with alt text. */
|
|
154
151
|
const logoImageSchema = z.union([
|
|
155
152
|
z.string(),
|
|
156
|
-
z
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
})
|
|
162
|
-
.strict(),
|
|
153
|
+
z.strictObject({
|
|
154
|
+
alt: z.string().optional(),
|
|
155
|
+
dark: z.string().optional(),
|
|
156
|
+
light: z.string().optional(),
|
|
157
|
+
}),
|
|
163
158
|
]);
|
|
164
159
|
|
|
165
160
|
/**
|
|
@@ -171,75 +166,63 @@ const logoImageSchema = z.union([
|
|
|
171
166
|
*/
|
|
172
167
|
const logoConfigSchema = z.union([
|
|
173
168
|
z.string(),
|
|
174
|
-
z
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
})
|
|
180
|
-
.strict(),
|
|
169
|
+
z.strictObject({
|
|
170
|
+
href: z.string().optional(),
|
|
171
|
+
image: logoImageSchema.optional(),
|
|
172
|
+
text: z.string().optional(),
|
|
173
|
+
}),
|
|
181
174
|
]);
|
|
182
175
|
|
|
183
176
|
/** Site-wide announcement banner: a string, or text with an optional link. */
|
|
184
177
|
const bannerConfigSchema = z.union([
|
|
185
178
|
z.string(),
|
|
186
|
-
z
|
|
187
|
-
.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
.object({ href: z.string(), text: z.string() })
|
|
195
|
-
.strict()
|
|
196
|
-
.optional(),
|
|
197
|
-
})
|
|
198
|
-
.strict(),
|
|
179
|
+
z.strictObject({
|
|
180
|
+
content: z.string(),
|
|
181
|
+
/** Show a dismiss button; the choice is remembered per visitor. */
|
|
182
|
+
dismissible: z.boolean().default(false),
|
|
183
|
+
/** Stable key for remembering dismissal; defaults to the content. */
|
|
184
|
+
id: z.string().optional(),
|
|
185
|
+
link: z.strictObject({ href: z.string(), text: z.string() }).optional(),
|
|
186
|
+
}),
|
|
199
187
|
]);
|
|
200
188
|
|
|
201
189
|
/** A local filesystem content source. */
|
|
202
|
-
const filesystemSourceSchema = z
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
})
|
|
211
|
-
.strict();
|
|
190
|
+
const filesystemSourceSchema = z.strictObject({
|
|
191
|
+
exclude: z.array(z.string()).default(["**/_*", "**/.*"]),
|
|
192
|
+
include: z.array(z.string()).default([DEFAULT_CONTENT_GLOB]),
|
|
193
|
+
/** Namespaces the source's routes under `/<prefix>/`. */
|
|
194
|
+
prefix: z.string().optional(),
|
|
195
|
+
root: z.string().default("docs"),
|
|
196
|
+
type: z.literal("filesystem"),
|
|
197
|
+
});
|
|
212
198
|
|
|
213
199
|
/**
|
|
214
200
|
* Remote Markdown/MDX fetched over HTTP. Enumerate files either explicitly
|
|
215
201
|
* (`files` against a raw `url` base) or from a GitHub repo subtree (`github`).
|
|
216
202
|
* The token, when needed, comes from `GITHUB_TOKEN` — never inlined here.
|
|
217
203
|
*/
|
|
218
|
-
const mdxRemoteSourceSchema = z
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
url: z.string().optional(),
|
|
241
|
-
})
|
|
242
|
-
.strict();
|
|
204
|
+
const mdxRemoteSourceSchema = z.strictObject({
|
|
205
|
+
/** Explicit list of source-relative file paths to fetch from `url`. */
|
|
206
|
+
files: z.array(z.string()).optional(),
|
|
207
|
+
/** Enumerate a GitHub repo subtree via the git-trees API. */
|
|
208
|
+
github: z
|
|
209
|
+
.strictObject({
|
|
210
|
+
owner: z.string(),
|
|
211
|
+
path: z.string().default(""),
|
|
212
|
+
ref: z.string().default("main"),
|
|
213
|
+
repo: z.string(),
|
|
214
|
+
})
|
|
215
|
+
.optional(),
|
|
216
|
+
/** Glob patterns applied to enumerated refs. */
|
|
217
|
+
include: z.array(z.string()).default([DEFAULT_CONTENT_GLOB]),
|
|
218
|
+
/** Opt-in dev polling interval (seconds); omit to freeze for the session. */
|
|
219
|
+
pollInterval: z.number().positive().optional(),
|
|
220
|
+
/** Namespaces the source's routes under `/<prefix>/`. */
|
|
221
|
+
prefix: z.string().optional(),
|
|
222
|
+
type: z.literal("mdx-remote"),
|
|
223
|
+
/** Raw base URL, e.g. `https://raw.githubusercontent.com/acme/sdk/main/docs`. */
|
|
224
|
+
url: z.string().optional(),
|
|
225
|
+
});
|
|
243
226
|
|
|
244
227
|
/** A Sanity dataset queried with GROQ; Portable Text bodies become Markdown. */
|
|
245
228
|
const sanitySourceSchema = z.object({
|
|
@@ -248,14 +231,13 @@ const sanitySourceSchema = z.object({
|
|
|
248
231
|
dataset: z.string(),
|
|
249
232
|
/** Field paths mapping a document onto Blume meta + body. */
|
|
250
233
|
fields: z
|
|
251
|
-
.
|
|
234
|
+
.strictObject({
|
|
252
235
|
body: z.string().optional(),
|
|
253
236
|
description: z.string().optional(),
|
|
254
237
|
lastModified: z.string().optional(),
|
|
255
238
|
slug: z.string().optional(),
|
|
256
239
|
title: z.string().optional(),
|
|
257
240
|
})
|
|
258
|
-
.strict()
|
|
259
241
|
.optional(),
|
|
260
242
|
/** Opt-in dev polling interval (seconds); omit to freeze for the session. */
|
|
261
243
|
pollInterval: z.number().positive().optional(),
|
|
@@ -274,14 +256,13 @@ const notionSourceSchema = z.object({
|
|
|
274
256
|
prefix: z.string().optional(),
|
|
275
257
|
/** Notion property names mapped onto Blume meta. */
|
|
276
258
|
properties: z
|
|
277
|
-
.
|
|
259
|
+
.strictObject({
|
|
278
260
|
description: z.string().optional(),
|
|
279
261
|
order: z.string().optional(),
|
|
280
262
|
slug: z.string().optional(),
|
|
281
263
|
status: z.string().optional(),
|
|
282
264
|
title: z.string().optional(),
|
|
283
265
|
})
|
|
284
|
-
.strict()
|
|
285
266
|
.optional(),
|
|
286
267
|
/** Status value treated as published; others map to `draft`. Default `Published`. */
|
|
287
268
|
publishedValue: z.string().optional(),
|
|
@@ -293,25 +274,23 @@ const notionSourceSchema = z.object({
|
|
|
293
274
|
* notes become the changelog with no files to maintain. A private repo reads a
|
|
294
275
|
* token from `GITHUB_TOKEN`; it is never inlined here.
|
|
295
276
|
*/
|
|
296
|
-
const githubReleasesSourceSchema = z
|
|
297
|
-
.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
})
|
|
314
|
-
.strict();
|
|
277
|
+
const githubReleasesSourceSchema = z.strictObject({
|
|
278
|
+
/** Include draft releases (needs a token with repo write access). */
|
|
279
|
+
drafts: z.boolean().optional(),
|
|
280
|
+
/** Cap the number of releases materialized, newest-first. Default 100. */
|
|
281
|
+
limit: z.number().positive().optional(),
|
|
282
|
+
/** Repository owner (user or org). */
|
|
283
|
+
owner: z.string(),
|
|
284
|
+
/** Opt-in dev polling interval (seconds); omit to freeze for the session. */
|
|
285
|
+
pollInterval: z.number().positive().optional(),
|
|
286
|
+
/** Namespaces the source's routes under `/<prefix>/`; e.g. `changelog`. */
|
|
287
|
+
prefix: z.string().optional(),
|
|
288
|
+
/** Include prereleases. */
|
|
289
|
+
prereleases: z.boolean().optional(),
|
|
290
|
+
/** Repository name. */
|
|
291
|
+
repo: z.string(),
|
|
292
|
+
type: z.literal("github-releases"),
|
|
293
|
+
});
|
|
315
294
|
|
|
316
295
|
/**
|
|
317
296
|
* A user-provided `ContentSource` instance, passed straight through from
|
|
@@ -343,60 +322,50 @@ const contentSourceSchema = z.discriminatedUnion("type", [
|
|
|
343
322
|
/** A resolved content-source config entry (post-defaults). */
|
|
344
323
|
export type ContentSourceConfig = z.infer<typeof contentSourceSchema>;
|
|
345
324
|
|
|
346
|
-
const contentConfigSchema = z
|
|
347
|
-
.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
})
|
|
360
|
-
.strict();
|
|
361
|
-
|
|
362
|
-
const navTabSchema = z
|
|
363
|
-
.object({
|
|
364
|
-
icon: iconName.optional(),
|
|
365
|
-
items: z
|
|
366
|
-
.array(
|
|
367
|
-
z
|
|
368
|
-
.object({
|
|
369
|
-
description: z.string().optional(),
|
|
370
|
-
icon: iconName.optional(),
|
|
371
|
-
label: z.string(),
|
|
372
|
-
path: z.string(),
|
|
373
|
-
tag: z.string().optional(),
|
|
374
|
-
})
|
|
375
|
-
.strict()
|
|
376
|
-
)
|
|
377
|
-
.optional(),
|
|
378
|
-
label: z.string(),
|
|
379
|
-
path: z.string(),
|
|
380
|
-
})
|
|
381
|
-
.strict();
|
|
382
|
-
|
|
383
|
-
const navSelectorItemSchema = z
|
|
384
|
-
.object({
|
|
385
|
-
description: z.string().optional(),
|
|
386
|
-
icon: iconName.optional(),
|
|
387
|
-
label: z.string(),
|
|
388
|
-
path: z.string(),
|
|
389
|
-
tag: z.string().optional(),
|
|
390
|
-
})
|
|
391
|
-
.strict();
|
|
325
|
+
const contentConfigSchema = z.strictObject({
|
|
326
|
+
defaultType: z.string().default("doc"),
|
|
327
|
+
exclude: z.array(z.string()).default(["**/_*", "**/.*"]),
|
|
328
|
+
include: z.array(z.string()).default([DEFAULT_CONTENT_GLOB]),
|
|
329
|
+
pages: z.string().default("pages"),
|
|
330
|
+
root: z.string().default("docs"),
|
|
331
|
+
/**
|
|
332
|
+
* Pluggable content sources. When omitted, the top-level
|
|
333
|
+
* `root`/`include`/`exclude` desugar to one implicit filesystem source, so
|
|
334
|
+
* existing projects are unchanged.
|
|
335
|
+
*/
|
|
336
|
+
sources: z.array(contentSourceSchema).optional(),
|
|
337
|
+
});
|
|
392
338
|
|
|
393
|
-
const
|
|
394
|
-
.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
339
|
+
const navTabSchema = z.strictObject({
|
|
340
|
+
icon: iconName.optional(),
|
|
341
|
+
items: z
|
|
342
|
+
.array(
|
|
343
|
+
z.strictObject({
|
|
344
|
+
description: z.string().optional(),
|
|
345
|
+
icon: iconName.optional(),
|
|
346
|
+
label: z.string(),
|
|
347
|
+
path: z.string(),
|
|
348
|
+
tag: z.string().optional(),
|
|
349
|
+
})
|
|
350
|
+
)
|
|
351
|
+
.optional(),
|
|
352
|
+
label: z.string(),
|
|
353
|
+
path: z.string(),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const navSelectorItemSchema = z.strictObject({
|
|
357
|
+
description: z.string().optional(),
|
|
358
|
+
icon: iconName.optional(),
|
|
359
|
+
label: z.string(),
|
|
360
|
+
path: z.string(),
|
|
361
|
+
tag: z.string().optional(),
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const navSelectorSchema = z.strictObject({
|
|
365
|
+
items: z.array(navSelectorItemSchema).default([]),
|
|
366
|
+
kind: z.enum(["dropdown", "language", "product", "version"]),
|
|
367
|
+
label: z.string(),
|
|
368
|
+
});
|
|
400
369
|
|
|
401
370
|
const directoryModeSchema = z.enum(["accordion", "card", "none"]);
|
|
402
371
|
export type DirectoryMode = z.infer<typeof directoryModeSchema>;
|
|
@@ -419,26 +388,29 @@ export type SidebarItemConfig =
|
|
|
419
388
|
const sidebarItemSchema: z.ZodType<SidebarItemConfig> = z.lazy(() =>
|
|
420
389
|
z.union([
|
|
421
390
|
z.string(),
|
|
422
|
-
z
|
|
423
|
-
.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
})
|
|
434
|
-
.strict(),
|
|
391
|
+
z.strictObject({
|
|
392
|
+
badge: z.string().optional(),
|
|
393
|
+
collapsed: z.boolean().optional(),
|
|
394
|
+
directory: directoryModeSchema.optional(),
|
|
395
|
+
display: sidebarDisplaySchema.optional(),
|
|
396
|
+
href: z.string().optional(),
|
|
397
|
+
icon: iconName.optional(),
|
|
398
|
+
items: z.array(sidebarItemSchema).optional(),
|
|
399
|
+
label: z.string(),
|
|
400
|
+
root: z.string().optional(),
|
|
401
|
+
}),
|
|
435
402
|
])
|
|
436
403
|
);
|
|
437
404
|
|
|
438
405
|
/** A curated Google Font slug (see `theme/fonts.ts`). */
|
|
439
|
-
const fontSlug = z.string().
|
|
440
|
-
|
|
441
|
-
|
|
406
|
+
const fontSlug = z.string().superRefine((value, ctx) => {
|
|
407
|
+
if (!isFontSlug(value)) {
|
|
408
|
+
ctx.addIssue({
|
|
409
|
+
code: z.ZodIssueCode.custom,
|
|
410
|
+
message: `Unknown font "${value}". Supported fonts: ${FONT_SLUGS.join(", ")}.`,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
});
|
|
442
414
|
|
|
443
415
|
/**
|
|
444
416
|
* An optional per-mode theme value: a string applies to both color modes; a
|
|
@@ -448,79 +420,69 @@ const fontSlug = z.string().refine(isFontSlug, (value) => ({
|
|
|
448
420
|
const perModeValueSchema = z
|
|
449
421
|
.union([
|
|
450
422
|
z.string(),
|
|
451
|
-
z
|
|
452
|
-
|
|
453
|
-
.
|
|
423
|
+
z.strictObject({
|
|
424
|
+
dark: z.string().optional(),
|
|
425
|
+
light: z.string().optional(),
|
|
426
|
+
}),
|
|
454
427
|
])
|
|
455
428
|
.optional()
|
|
456
429
|
.transform((value) =>
|
|
457
430
|
typeof value === "string" ? { dark: value, light: value } : value
|
|
458
431
|
);
|
|
459
432
|
|
|
460
|
-
const themeConfigSchema = z
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
.
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
.
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
radius: z.enum(["none", "sm", "md", "lg"]).default("md"),
|
|
485
|
-
})
|
|
486
|
-
.strict();
|
|
433
|
+
const themeConfigSchema = z.strictObject({
|
|
434
|
+
accent: z
|
|
435
|
+
.union([
|
|
436
|
+
z.string(),
|
|
437
|
+
z.strictObject({ dark: z.string(), light: z.string() }),
|
|
438
|
+
])
|
|
439
|
+
.default("blue")
|
|
440
|
+
.transform((value) =>
|
|
441
|
+
typeof value === "string" ? { dark: value, light: value } : value
|
|
442
|
+
),
|
|
443
|
+
action: z.string().optional(),
|
|
444
|
+
background: perModeValueSchema,
|
|
445
|
+
backgroundImage: perModeValueSchema,
|
|
446
|
+
fonts: z
|
|
447
|
+
.strictObject({
|
|
448
|
+
body: fontSlug.default("inter"),
|
|
449
|
+
display: fontSlug.default("inter-tight"),
|
|
450
|
+
mono: fontSlug.default("ibm-plex-mono"),
|
|
451
|
+
})
|
|
452
|
+
.default({}),
|
|
453
|
+
layout: z.enum(["sidebar"]).default("sidebar"),
|
|
454
|
+
mode: z.enum(["system", "light", "dark"]).default("system"),
|
|
455
|
+
radius: z.enum(["none", "sm", "md", "lg"]).default("md"),
|
|
456
|
+
});
|
|
487
457
|
|
|
488
458
|
/** Public credentials for the Algolia search backend (sync key is an env var). */
|
|
489
|
-
const algoliaSearchSchema = z
|
|
490
|
-
.
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
})
|
|
495
|
-
.strict();
|
|
459
|
+
const algoliaSearchSchema = z.strictObject({
|
|
460
|
+
appId: z.string(),
|
|
461
|
+
indexName: z.string(),
|
|
462
|
+
searchApiKey: z.string(),
|
|
463
|
+
});
|
|
496
464
|
|
|
497
465
|
/** Public credentials for the Orama Cloud search backend. */
|
|
498
|
-
const oramaCloudSearchSchema = z
|
|
499
|
-
.
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
})
|
|
505
|
-
.strict();
|
|
466
|
+
const oramaCloudSearchSchema = z.strictObject({
|
|
467
|
+
apiKey: z.string(),
|
|
468
|
+
endpoint: z.string(),
|
|
469
|
+
/** Index id used by the build-time sync (with `ORAMA_PRIVATE_API_KEY`). */
|
|
470
|
+
indexId: z.string().optional(),
|
|
471
|
+
});
|
|
506
472
|
|
|
507
473
|
/** Public credentials for a (self-hosted or cloud) Typesense backend. */
|
|
508
|
-
const typesenseSearchSchema = z
|
|
509
|
-
.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
})
|
|
516
|
-
.strict();
|
|
474
|
+
const typesenseSearchSchema = z.strictObject({
|
|
475
|
+
collection: z.string(),
|
|
476
|
+
host: z.string(),
|
|
477
|
+
port: z.number().int().positive().optional(),
|
|
478
|
+
protocol: z.enum(["http", "https"]).optional(),
|
|
479
|
+
searchApiKey: z.string(),
|
|
480
|
+
});
|
|
517
481
|
|
|
518
482
|
/** Mixedbread semantic search: the store the server endpoint queries. */
|
|
519
|
-
const mixedbreadSearchSchema = z
|
|
520
|
-
.
|
|
521
|
-
|
|
522
|
-
})
|
|
523
|
-
.strict();
|
|
483
|
+
const mixedbreadSearchSchema = z.strictObject({
|
|
484
|
+
storeId: z.string(),
|
|
485
|
+
});
|
|
524
486
|
|
|
525
487
|
export const searchProviders = [
|
|
526
488
|
"orama",
|
|
@@ -542,20 +504,18 @@ const PROVIDER_CONFIG_KEY = {
|
|
|
542
504
|
} as const;
|
|
543
505
|
|
|
544
506
|
const searchConfigSchema = z
|
|
545
|
-
.
|
|
507
|
+
.strictObject({
|
|
546
508
|
algolia: algoliaSearchSchema.optional(),
|
|
547
509
|
indexing: z
|
|
548
|
-
.
|
|
510
|
+
.strictObject({
|
|
549
511
|
includeHiddenPages: z.boolean().default(false),
|
|
550
512
|
})
|
|
551
|
-
.strict()
|
|
552
513
|
.default({}),
|
|
553
514
|
mixedbread: mixedbreadSearchSchema.optional(),
|
|
554
515
|
oramaCloud: oramaCloudSearchSchema.optional(),
|
|
555
516
|
provider: z.enum(searchProviders).default("orama"),
|
|
556
517
|
typesense: typesenseSearchSchema.optional(),
|
|
557
518
|
})
|
|
558
|
-
.strict()
|
|
559
519
|
.superRefine((value, ctx) => {
|
|
560
520
|
// Hosted providers can't work without their credentials; flag a missing
|
|
561
521
|
// block with a path so the diagnostic points at `search.<provider>`.
|
|
@@ -579,95 +539,112 @@ export const askAiProviders = [
|
|
|
579
539
|
"openai-compatible",
|
|
580
540
|
] as const;
|
|
581
541
|
|
|
582
|
-
const aiConfigSchema = z
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
542
|
+
const aiConfigSchema = z.strictObject({
|
|
543
|
+
ask: z
|
|
544
|
+
.strictObject({
|
|
545
|
+
// Name of the env var holding the provider's API key; each provider has
|
|
546
|
+
// a sensible default, so this only needs setting to override it.
|
|
547
|
+
apiKeyEnv: z.string().optional(),
|
|
548
|
+
// Base URL of the backend. Required for `openai-compatible`; for the
|
|
549
|
+
// named providers it overrides the built-in preset.
|
|
550
|
+
// blume bundles Zod 3; top-level `z.url()` is undefined at runtime.
|
|
551
|
+
// oxlint-disable-next-line react-doctor/zod-v4-prefer-top-level-string-formats
|
|
552
|
+
baseUrl: z.string().url().optional(),
|
|
553
|
+
enabled: z.boolean().default(false),
|
|
554
|
+
model: z.string().default("openai/gpt-5.5"),
|
|
555
|
+
provider: z.enum(askAiProviders).default("gateway"),
|
|
556
|
+
// Empty-state prompts shown before the first question. Each renders as a
|
|
557
|
+
// clickable suggestion; `icon` is an optional Lucide name beside it.
|
|
558
|
+
suggestions: z
|
|
559
|
+
.array(
|
|
560
|
+
z.strictObject({
|
|
561
|
+
icon: iconName.optional(),
|
|
562
|
+
label: z.string().min(1),
|
|
563
|
+
})
|
|
564
|
+
)
|
|
565
|
+
.default([]),
|
|
566
|
+
})
|
|
567
|
+
.superRefine((value, ctx) => {
|
|
568
|
+
// A generic OpenAI-compatible backend has no preset URL, so the user
|
|
569
|
+
// must supply one; the named providers fall back to their preset.
|
|
570
|
+
if (value.provider === "openai-compatible" && !value.baseUrl) {
|
|
571
|
+
ctx.addIssue({
|
|
572
|
+
code: z.ZodIssueCode.custom,
|
|
573
|
+
message:
|
|
574
|
+
'ai.ask.baseUrl is required when provider is "openai-compatible".',
|
|
575
|
+
path: ["baseUrl"],
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
})
|
|
579
|
+
.optional(),
|
|
580
|
+
/**
|
|
581
|
+
* `llms.txt`/`llms-full.txt` emission. A bare boolean toggles it; the object
|
|
582
|
+
* form adds `openapi: false` to keep generated API reference pages out of
|
|
583
|
+
* both files (e.g. when the configured spec is example content).
|
|
584
|
+
*/
|
|
585
|
+
llmsTxt: z
|
|
586
|
+
.union([
|
|
587
|
+
z.boolean(),
|
|
588
|
+
z.strictObject({
|
|
589
|
+
enabled: z.boolean().default(true),
|
|
590
|
+
openapi: z.boolean().default(true),
|
|
591
|
+
}),
|
|
592
|
+
])
|
|
593
|
+
.default(true)
|
|
594
|
+
.transform((value) =>
|
|
595
|
+
typeof value === "boolean" ? { enabled: value, openapi: true } : value
|
|
596
|
+
),
|
|
597
|
+
// Serializers for the agent-facing Markdown downlevel (the `.md` mirror,
|
|
598
|
+
// llms-full.txt, MCP get_page), keyed by JSX name. Functions live here —
|
|
599
|
+
// not in components.tsx — because the config file is executed at build
|
|
600
|
+
// time while the components file is only statically analyzed. A same-name
|
|
601
|
+
// entry replaces the built-in serializer.
|
|
602
|
+
markdownComponents: z
|
|
603
|
+
.record(
|
|
604
|
+
z.custom<ComponentMarkdown>((value) => typeof value === "function", {
|
|
605
|
+
message: "Expected a serializer function.",
|
|
620
606
|
})
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
.strict();
|
|
607
|
+
)
|
|
608
|
+
.default({}),
|
|
609
|
+
});
|
|
625
610
|
|
|
626
611
|
/**
|
|
627
612
|
* A pinned link rendered above the sidebar sections — a blog, changelog, or
|
|
628
613
|
* contact page that should always be reachable, regardless of the active tab.
|
|
629
614
|
* `href` may be an external URL or an internal route.
|
|
630
615
|
*/
|
|
631
|
-
const featuredLinkSchema = z
|
|
632
|
-
.
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
.
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
z.array(sidebarItemSchema),
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
.transform((value) =>
|
|
664
|
-
Array.isArray(value)
|
|
665
|
-
? { display: "flat" as const, items: value }
|
|
666
|
-
: value
|
|
667
|
-
),
|
|
668
|
-
tabs: z.array(navTabSchema).optional(),
|
|
669
|
-
})
|
|
670
|
-
.strict();
|
|
616
|
+
const featuredLinkSchema = z.strictObject({
|
|
617
|
+
href: z.string(),
|
|
618
|
+
icon: iconName.optional(),
|
|
619
|
+
label: z.string(),
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
const navigationConfigSchema = z.strictObject({
|
|
623
|
+
/** Pinned links shown above the generated sidebar sections. */
|
|
624
|
+
featured: z.array(featuredLinkSchema).default([]),
|
|
625
|
+
/** Show a GitHub repo link in the header (requires `github` configured). */
|
|
626
|
+
repo: z.boolean().default(true),
|
|
627
|
+
selectors: z.array(navSelectorSchema).default([]),
|
|
628
|
+
/**
|
|
629
|
+
* Sidebar behavior. `display` sets how every group renders (a group in an
|
|
630
|
+
* explicit `items` config may still override it); `items` is an explicit
|
|
631
|
+
* sidebar — when omitted the sidebar is generated from the content tree.
|
|
632
|
+
* A bare array is shorthand for `{ items }`.
|
|
633
|
+
*/
|
|
634
|
+
sidebar: z
|
|
635
|
+
.union([
|
|
636
|
+
z.array(sidebarItemSchema),
|
|
637
|
+
z.strictObject({
|
|
638
|
+
display: sidebarDisplaySchema.default("flat"),
|
|
639
|
+
items: z.array(sidebarItemSchema).optional(),
|
|
640
|
+
}),
|
|
641
|
+
])
|
|
642
|
+
.default({})
|
|
643
|
+
.transform((value) =>
|
|
644
|
+
Array.isArray(value) ? { display: "flat" as const, items: value } : value
|
|
645
|
+
),
|
|
646
|
+
tabs: z.array(navTabSchema).optional(),
|
|
647
|
+
});
|
|
671
648
|
|
|
672
649
|
export type AskAiProvider = (typeof askAiProviders)[number];
|
|
673
650
|
export type AskAiConfig = NonNullable<z.infer<typeof aiConfigSchema>["ask"]>;
|
|
@@ -679,37 +656,35 @@ export type AskAiConfig = NonNullable<z.infer<typeof aiConfigSchema>["ask"]>;
|
|
|
679
656
|
const exportConfigSchema = z
|
|
680
657
|
.union([
|
|
681
658
|
z.boolean(),
|
|
682
|
-
z
|
|
683
|
-
.
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
})
|
|
687
|
-
.strict(),
|
|
659
|
+
z.strictObject({
|
|
660
|
+
epub: z.boolean().default(false),
|
|
661
|
+
pdf: z.boolean().default(false),
|
|
662
|
+
}),
|
|
688
663
|
])
|
|
689
664
|
.transform((value) =>
|
|
690
665
|
typeof value === "boolean" ? { epub: value, pdf: value } : value
|
|
691
666
|
);
|
|
692
667
|
|
|
693
|
-
const mcpConfigSchema = z
|
|
694
|
-
.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
668
|
+
const mcpConfigSchema = z.strictObject({
|
|
669
|
+
enabled: z.boolean().default(false),
|
|
670
|
+
/** Optional system hint passed to connecting agents. */
|
|
671
|
+
instructions: z.string().optional(),
|
|
672
|
+
/** Server name shown to clients; defaults to the site title. */
|
|
673
|
+
name: z.string().optional(),
|
|
674
|
+
/**
|
|
675
|
+
* Normalized like `openapi.route`: a slash-less value would otherwise be
|
|
676
|
+
* string-concatenated onto the site origin (`https://acme.comdocs-mcp`).
|
|
677
|
+
*/
|
|
678
|
+
route: z.string().default("/mcp").transform(normalizeRoute),
|
|
679
|
+
});
|
|
703
680
|
|
|
704
681
|
/** A configured locale: ISO-ish code plus display metadata for the switcher. */
|
|
705
|
-
const localeSchema = z
|
|
706
|
-
.
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
})
|
|
712
|
-
.strict();
|
|
682
|
+
const localeSchema = z.strictObject({
|
|
683
|
+
code: z.string().min(1),
|
|
684
|
+
/** Text direction; drives `<html dir>` and a future RTL pass. */
|
|
685
|
+
dir: z.enum(["ltr", "rtl"]).default("ltr"),
|
|
686
|
+
label: z.string(),
|
|
687
|
+
});
|
|
713
688
|
|
|
714
689
|
/**
|
|
715
690
|
* Internationalization. Opt-in: when absent, Blume is single-locale and behaves
|
|
@@ -717,7 +692,7 @@ const localeSchema = z
|
|
|
717
692
|
* are top-level directories named by `code` (the `dir` parser).
|
|
718
693
|
*/
|
|
719
694
|
const i18nConfigSchema = z
|
|
720
|
-
.
|
|
695
|
+
.strictObject({
|
|
721
696
|
defaultLocale: z.string().default("en"),
|
|
722
697
|
/** Locale rendered for a missing translation; `null` disables fallback. */
|
|
723
698
|
fallbackLocale: z.string().nullable().optional(),
|
|
@@ -729,7 +704,6 @@ const i18nConfigSchema = z
|
|
|
729
704
|
/** Per-locale UI string overrides: `{ fr: { search: { button: "…" } } }`. */
|
|
730
705
|
ui: uiLocaleOverridesSchema.optional(),
|
|
731
706
|
})
|
|
732
|
-
.strict()
|
|
733
707
|
.superRefine((value, ctx) => {
|
|
734
708
|
const codes = new Set(value.locales.map((locale) => locale.code));
|
|
735
709
|
if (!codes.has(value.defaultLocale)) {
|
|
@@ -753,7 +727,7 @@ const i18nConfigSchema = z
|
|
|
753
727
|
});
|
|
754
728
|
|
|
755
729
|
const analyticsScriptSchema = z
|
|
756
|
-
.
|
|
730
|
+
.strictObject({
|
|
757
731
|
// Extra attributes (e.g. `data-domain`, `id`) spread onto the <script>.
|
|
758
732
|
attributes: z.record(z.string(), z.string()).optional(),
|
|
759
733
|
// Inline script body, mutually exclusive with `src`.
|
|
@@ -763,69 +737,59 @@ const analyticsScriptSchema = z
|
|
|
763
737
|
// Load strategy for an external script.
|
|
764
738
|
strategy: z.enum(["async", "defer"]).optional(),
|
|
765
739
|
})
|
|
766
|
-
.strict()
|
|
767
740
|
.refine((value) => Boolean(value.src) !== Boolean(value.content), {
|
|
768
741
|
message: "An analytics script must set exactly one of `src` or `content`.",
|
|
769
742
|
});
|
|
770
743
|
|
|
771
|
-
const analyticsConfigSchema = z
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
.
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
.
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
.
|
|
819
|
-
|
|
820
|
-
const rssConfigSchema = z
|
|
821
|
-
.object({
|
|
822
|
-
enabled: z.boolean().default(true),
|
|
823
|
-
/** Max items per feed, newest first. */
|
|
824
|
-
limit: z.number().int().positive().default(50),
|
|
825
|
-
/** Content types that each get a feed at `/<type>/rss.xml`. */
|
|
826
|
-
types: z.array(z.string()).default(["blog", "changelog"]),
|
|
827
|
-
})
|
|
828
|
-
.strict();
|
|
744
|
+
const analyticsConfigSchema = z.strictObject({
|
|
745
|
+
posthog: z
|
|
746
|
+
.strictObject({
|
|
747
|
+
host: z.string().optional(),
|
|
748
|
+
key: z.string(),
|
|
749
|
+
})
|
|
750
|
+
.optional(),
|
|
751
|
+
// Escape hatch for any other provider (Plausible, Fathom, GA, Umami, …).
|
|
752
|
+
scripts: z.array(analyticsScriptSchema).optional(),
|
|
753
|
+
vercel: z.boolean().optional(),
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
const deploymentConfigSchema = z.strictObject({
|
|
757
|
+
adapter: z
|
|
758
|
+
.enum(["vercel", "node", "netlify", "cloudflare"])
|
|
759
|
+
.nullable()
|
|
760
|
+
.default(null),
|
|
761
|
+
base: z.string().optional(),
|
|
762
|
+
output: z.enum(["static", "server"]).default("static"),
|
|
763
|
+
// blume bundles Zod 3; top-level `z.url()` is undefined at runtime.
|
|
764
|
+
// oxlint-disable-next-line react-doctor/zod-v4-prefer-top-level-string-formats
|
|
765
|
+
site: z.string().url().optional(),
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
const redirectSchema = z.strictObject({
|
|
769
|
+
from: z.string(),
|
|
770
|
+
status: z
|
|
771
|
+
.union([z.literal(301), z.literal(302), z.literal(307), z.literal(308)])
|
|
772
|
+
.default(301),
|
|
773
|
+
to: z.string(),
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
const ogConfigSchema = z.strictObject({
|
|
777
|
+
/**
|
|
778
|
+
* Generate a per-page Open Graph image. Defaults to on once a deployment
|
|
779
|
+
* site URL is known (set or auto-detected) and off otherwise, since
|
|
780
|
+
* `og:image` must be absolute to be useful to crawlers — resolved in
|
|
781
|
+
* `loadConfig`. An explicit value here always wins.
|
|
782
|
+
*/
|
|
783
|
+
enabled: z.boolean().optional(),
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
const rssConfigSchema = z.strictObject({
|
|
787
|
+
enabled: z.boolean().default(true),
|
|
788
|
+
/** Max items per feed, newest first. */
|
|
789
|
+
limit: z.number().int().positive().default(50),
|
|
790
|
+
/** Content types that each get a feed at `/<type>/rss.xml`. */
|
|
791
|
+
types: z.array(z.string()).default(["blog", "changelog"]),
|
|
792
|
+
});
|
|
829
793
|
|
|
830
794
|
/**
|
|
831
795
|
* robots.txt `Content-Signal` preferences — the emerging content-usage
|
|
@@ -835,13 +799,11 @@ const rssConfigSchema = z
|
|
|
835
799
|
* - `aiInput` → `ai-input` (grounding / RAG use at answer time)
|
|
836
800
|
* - `aiTrain` → `ai-train` (model training)
|
|
837
801
|
*/
|
|
838
|
-
const contentSignalsObjectSchema = z
|
|
839
|
-
.
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
})
|
|
844
|
-
.strict();
|
|
802
|
+
const contentSignalsObjectSchema = z.strictObject({
|
|
803
|
+
aiInput: z.boolean().default(true),
|
|
804
|
+
aiTrain: z.boolean().default(true),
|
|
805
|
+
search: z.boolean().default(true),
|
|
806
|
+
});
|
|
845
807
|
|
|
846
808
|
/**
|
|
847
809
|
* Content signals accept a boolean shorthand or a per-signal object, and
|
|
@@ -863,49 +825,70 @@ const contentSignalsSchema = z
|
|
|
863
825
|
});
|
|
864
826
|
|
|
865
827
|
/** Discoverability features: OG images, feeds, sitemap, structured data. */
|
|
866
|
-
const seoConfigSchema = z
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
})
|
|
885
|
-
.strict();
|
|
886
|
-
|
|
887
|
-
const githubConfigSchema = z
|
|
888
|
-
.object({
|
|
889
|
-
branch: z.string().default("main"),
|
|
890
|
-
/** Path from the repo root to the project root (for monorepos). */
|
|
891
|
-
dir: z.string().optional(),
|
|
892
|
-
owner: z.string(),
|
|
893
|
-
repo: z.string(),
|
|
894
|
-
})
|
|
895
|
-
.strict();
|
|
828
|
+
const seoConfigSchema = z.strictObject({
|
|
829
|
+
/**
|
|
830
|
+
* Emit `agent-readability.json` at the site root: a manifest that indexes
|
|
831
|
+
* the agent-facing surface (llms.txt, Markdown mirrors, MCP server, feeds)
|
|
832
|
+
* so agents can discover it without scraping HTML.
|
|
833
|
+
*/
|
|
834
|
+
agentReadability: z.boolean().default(true),
|
|
835
|
+
/** robots.txt `Content-Signal` usage declaration (on by default). */
|
|
836
|
+
contentSignals: contentSignalsSchema.default(true),
|
|
837
|
+
og: ogConfigSchema.default({}),
|
|
838
|
+
/** Generate robots.txt (with a Sitemap reference when available). */
|
|
839
|
+
robots: z.boolean().default(true),
|
|
840
|
+
rss: rssConfigSchema.default({}),
|
|
841
|
+
/** Generate sitemap.xml (requires deployment.site). */
|
|
842
|
+
sitemap: z.boolean().default(true),
|
|
843
|
+
/** Emit schema.org JSON-LD in each page's <head>. */
|
|
844
|
+
structuredData: z.boolean().default(true),
|
|
845
|
+
});
|
|
896
846
|
|
|
897
|
-
const
|
|
898
|
-
.
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
.
|
|
847
|
+
const githubConfigSchema = z.strictObject({
|
|
848
|
+
branch: z.string().default("main"),
|
|
849
|
+
/** Path from the repo root to the project root (for monorepos). */
|
|
850
|
+
dir: z.string().optional(),
|
|
851
|
+
owner: z.string(),
|
|
852
|
+
repo: z.string(),
|
|
853
|
+
});
|
|
903
854
|
|
|
904
|
-
const
|
|
905
|
-
.
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
855
|
+
const codeBlockThemeSchema = z.strictObject({
|
|
856
|
+
dark: z.string().default("github-dark"),
|
|
857
|
+
light: z.string().default("github-light"),
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
const codeBlocksConfigSchema = z.strictObject({
|
|
861
|
+
theme: codeBlockThemeSchema.default({}),
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* `<Component />` example previews. A string is shorthand for `{ source }`:
|
|
866
|
+
* where examples live, relative to the project root (default `examples`).
|
|
867
|
+
* `source` may be a glob (anything with `*`/`?`/`[]`/`{}`/`!`), in which case
|
|
868
|
+
* only matching files are discovered and each `<Component path>` key is
|
|
869
|
+
* relative to the glob's static prefix — use this for a registry layout that
|
|
870
|
+
* colocates component sources with their examples
|
|
871
|
+
* (`registry/<pkg>/**\/examples/*`), leaving the sources (which have no
|
|
872
|
+
* default export to wrap) out.
|
|
873
|
+
*
|
|
874
|
+
* `css` names a stylesheet (relative to the project root) injected into every
|
|
875
|
+
* preview frame after Blume's default tokens. Previews render in an isolated
|
|
876
|
+
* iframe that the site's docs styles never reach, so this is where design
|
|
877
|
+
* tokens for the previewed components live — e.g. shadcn variables and
|
|
878
|
+
* `@theme` mappings. Tailwind itself is already provided; the file should
|
|
879
|
+
* hold tokens and custom styles, not another `@import "tailwindcss"`.
|
|
880
|
+
*/
|
|
881
|
+
const examplesConfigSchema = z
|
|
882
|
+
.union([
|
|
883
|
+
z.string(),
|
|
884
|
+
z.strictObject({
|
|
885
|
+
css: z.string().optional(),
|
|
886
|
+
source: z.string().default("examples"),
|
|
887
|
+
}),
|
|
888
|
+
])
|
|
889
|
+
.transform((value): { css?: string; source: string } =>
|
|
890
|
+
typeof value === "string" ? { source: value } : value
|
|
891
|
+
);
|
|
909
892
|
|
|
910
893
|
/**
|
|
911
894
|
* "Last updated" timestamps for content pages. `false` (default) disables the
|
|
@@ -914,58 +897,63 @@ const codeBlocksConfigSchema = z
|
|
|
914
897
|
*/
|
|
915
898
|
const lastModifiedConfigSchema = z.union([
|
|
916
899
|
z.boolean(),
|
|
917
|
-
z.
|
|
900
|
+
z.strictObject({ type: z.enum(["git", "frontmatter"]).default("git") }),
|
|
918
901
|
]);
|
|
919
902
|
|
|
920
903
|
/** Code-block rendering options (`markdown.code`). */
|
|
921
|
-
const codeConfigSchema = z
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
.
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
904
|
+
const codeConfigSchema = z.strictObject({
|
|
905
|
+
/**
|
|
906
|
+
* Show a brand language icon in the code-block header (TypeScript, Python,
|
|
907
|
+
* …). On by default; recognized languages only.
|
|
908
|
+
*/
|
|
909
|
+
icons: z.boolean().default(true),
|
|
910
|
+
/**
|
|
911
|
+
* Wrap long lines instead of scrolling horizontally. Off by default, so
|
|
912
|
+
* code keeps its original line breaks and overflows into a scroll area.
|
|
913
|
+
*/
|
|
914
|
+
wrap: z.boolean().default(false),
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
const markdownConfigSchema = z.strictObject({
|
|
918
|
+
/** Code-block rendering: language icons and line wrapping. */
|
|
919
|
+
code: codeConfigSchema.default({}),
|
|
920
|
+
codeBlocks: codeBlocksConfigSchema.default({}),
|
|
921
|
+
/**
|
|
922
|
+
* Wrap each `##`–`######` heading in a link to its own anchor so readers can
|
|
923
|
+
* click to copy, bookmark, or share a permalink to that section. On by
|
|
924
|
+
* default; set to `false` to render plain headings.
|
|
925
|
+
*/
|
|
926
|
+
headingAnchors: z.boolean().default(true),
|
|
927
|
+
/**
|
|
928
|
+
* Make content images click-to-zoom (open in a lightbox). On by default;
|
|
929
|
+
* opt a single image out with `data-no-zoom`.
|
|
930
|
+
*/
|
|
931
|
+
imageZoom: z.boolean().default(true),
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
/** React island behavior (`react`). */
|
|
935
|
+
const reactConfigSchema = z.strictObject({
|
|
936
|
+
/**
|
|
937
|
+
* Auto-memoize React components/hooks with the React Compiler
|
|
938
|
+
* (`babel-plugin-react-compiler`). On by default whenever React is enabled
|
|
939
|
+
* (a project `.tsx`/`.jsx`, a React island/example/override, or Ask AI); set
|
|
940
|
+
* to `false` to skip the compiler's babel pass.
|
|
941
|
+
*/
|
|
942
|
+
compiler: z.boolean().default(true),
|
|
943
|
+
});
|
|
954
944
|
|
|
955
945
|
/**
|
|
956
946
|
* A single spec rendered by the API reference. `spec` is a local path or an
|
|
957
947
|
* `http(s)` URL (OpenAPI for the Blume renderer; OpenAPI or AsyncAPI for Scalar).
|
|
958
948
|
*/
|
|
959
|
-
const openapiSourceSchema = z
|
|
960
|
-
.
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
})
|
|
968
|
-
.strict();
|
|
949
|
+
const openapiSourceSchema = z.strictObject({
|
|
950
|
+
/** Nav/section label for this source. */
|
|
951
|
+
label: z.string().optional(),
|
|
952
|
+
/** Per-source route; defaults to the block's `route` (or a derived path). */
|
|
953
|
+
route: z.string().optional(),
|
|
954
|
+
/** Local path or `http(s)` URL to the spec. */
|
|
955
|
+
spec: z.string(),
|
|
956
|
+
});
|
|
969
957
|
|
|
970
958
|
export type OpenApiSource = z.infer<typeof openapiSourceSchema>;
|
|
971
959
|
|
|
@@ -976,39 +964,35 @@ export type OpenApiSource = z.infer<typeof openapiSourceSchema>;
|
|
|
976
964
|
* `renderer: "scalar"` to fall back to the embedded Scalar SPA (a single
|
|
977
965
|
* self-contained route that doesn't weave into the sidebar or search).
|
|
978
966
|
*/
|
|
979
|
-
const openapiConfigSchema = z
|
|
980
|
-
.
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
})
|
|
997
|
-
.strict();
|
|
967
|
+
const openapiConfigSchema = z.strictObject({
|
|
968
|
+
/** Code-sample languages shown per operation (Blume renderer). */
|
|
969
|
+
codeSamples: z.array(z.string()).default(["curl", "js", "python"]),
|
|
970
|
+
enabled: z.boolean().default(false),
|
|
971
|
+
/** Start nested schema rows expanded rather than collapsed (Blume renderer). */
|
|
972
|
+
expandSchemas: z.boolean().default(false),
|
|
973
|
+
/** Who renders the reference: Blume's own UI, or the embedded Scalar SPA. */
|
|
974
|
+
renderer: z.enum(["blume", "scalar"]).default("blume"),
|
|
975
|
+
/** Where the reference mounts. */
|
|
976
|
+
route: z.string().default("/reference"),
|
|
977
|
+
/** One or more specs; each renders on its own route by default. */
|
|
978
|
+
sources: z.array(openapiSourceSchema).default([]),
|
|
979
|
+
/** Shorthand for a single source: `sources: [{ spec }]`. */
|
|
980
|
+
spec: z.string().optional(),
|
|
981
|
+
/** Scalar theme name (Scalar renderer only). */
|
|
982
|
+
theme: z.string().optional(),
|
|
983
|
+
});
|
|
998
984
|
|
|
999
985
|
/**
|
|
1000
986
|
* AsyncAPI reference. Same shape and Scalar pipeline as {@link openapiConfigSchema}
|
|
1001
987
|
* (Scalar auto-detects the document type); only the default `route` differs.
|
|
1002
988
|
*/
|
|
1003
|
-
const asyncapiConfigSchema = z
|
|
1004
|
-
.
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
})
|
|
1011
|
-
.strict();
|
|
989
|
+
const asyncapiConfigSchema = z.strictObject({
|
|
990
|
+
enabled: z.boolean().default(false),
|
|
991
|
+
route: z.string().default("/events"),
|
|
992
|
+
sources: z.array(openapiSourceSchema).default([]),
|
|
993
|
+
spec: z.string().optional(),
|
|
994
|
+
theme: z.string().optional(),
|
|
995
|
+
});
|
|
1012
996
|
|
|
1013
997
|
/** Full user-facing config schema. All fields optional with defaults. */
|
|
1014
998
|
/**
|
|
@@ -1019,12 +1003,10 @@ const asyncapiConfigSchema = z
|
|
|
1019
1003
|
const tocConfigSchema = z
|
|
1020
1004
|
.union([
|
|
1021
1005
|
z.boolean(),
|
|
1022
|
-
z
|
|
1023
|
-
.
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
})
|
|
1027
|
-
.strict(),
|
|
1006
|
+
z.strictObject({
|
|
1007
|
+
maxHeadingLevel: z.number().int().min(1).max(6).optional(),
|
|
1008
|
+
minHeadingLevel: z.number().int().min(1).max(6).optional(),
|
|
1009
|
+
}),
|
|
1028
1010
|
])
|
|
1029
1011
|
.default(true)
|
|
1030
1012
|
.transform((value) => {
|
|
@@ -1038,47 +1020,50 @@ const tocConfigSchema = z
|
|
|
1038
1020
|
};
|
|
1039
1021
|
});
|
|
1040
1022
|
|
|
1041
|
-
export const blumeConfigSchema = z
|
|
1042
|
-
.
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
})
|
|
1081
|
-
.
|
|
1023
|
+
export const blumeConfigSchema = z.strictObject({
|
|
1024
|
+
ai: aiConfigSchema.default({}),
|
|
1025
|
+
analytics: analyticsConfigSchema.optional(),
|
|
1026
|
+
asyncapi: asyncapiConfigSchema.default({}),
|
|
1027
|
+
banner: bannerConfigSchema.optional(),
|
|
1028
|
+
/**
|
|
1029
|
+
* Site-wide mount point prepended to every generated route (e.g. `/docs`),
|
|
1030
|
+
* while staying invisible to the sidebar/nav tree. Distinct from a per-source
|
|
1031
|
+
* `prefix` (which creates a group) and from `deployment.base` (Astro's
|
|
1032
|
+
* host-subdirectory base); the two compose. Normalized to `""` or `/seg`.
|
|
1033
|
+
*/
|
|
1034
|
+
basePath: z
|
|
1035
|
+
.string()
|
|
1036
|
+
.optional()
|
|
1037
|
+
.transform((value) => normalizeBasePath(value)),
|
|
1038
|
+
content: contentConfigSchema.default({}),
|
|
1039
|
+
deployment: deploymentConfigSchema.default({}),
|
|
1040
|
+
description: z.string().optional(),
|
|
1041
|
+
/**
|
|
1042
|
+
* Where `<Component path>` resolves live previews and their source from.
|
|
1043
|
+
* A string is shorthand for `{ source }` — the directory (or glob, for
|
|
1044
|
+
* colocated registry layouts) under the project root that holds example
|
|
1045
|
+
* files. The object form adds `css`: a stylesheet injected into every
|
|
1046
|
+
* preview frame (design tokens, shadcn variables, `@theme` mappings).
|
|
1047
|
+
*/
|
|
1048
|
+
examples: examplesConfigSchema.default("examples"),
|
|
1049
|
+
export: exportConfigSchema.default(false),
|
|
1050
|
+
feedback: z.boolean().default(true),
|
|
1051
|
+
github: githubConfigSchema.optional(),
|
|
1052
|
+
i18n: i18nConfigSchema.optional(),
|
|
1053
|
+
lastModified: lastModifiedConfigSchema.default(false),
|
|
1054
|
+
logo: logoConfigSchema.optional(),
|
|
1055
|
+
markdown: markdownConfigSchema.default({}),
|
|
1056
|
+
mcp: mcpConfigSchema.default({}),
|
|
1057
|
+
navigation: navigationConfigSchema.default({}),
|
|
1058
|
+
openapi: openapiConfigSchema.default({}),
|
|
1059
|
+
react: reactConfigSchema.default({}),
|
|
1060
|
+
redirects: z.array(redirectSchema).default([]),
|
|
1061
|
+
search: searchConfigSchema.default({}),
|
|
1062
|
+
seo: seoConfigSchema.default({}),
|
|
1063
|
+
theme: themeConfigSchema.default({}),
|
|
1064
|
+
title: z.string().default("Documentation"),
|
|
1065
|
+
toc: tocConfigSchema,
|
|
1066
|
+
});
|
|
1082
1067
|
|
|
1083
1068
|
/** Resolved config: every field present after defaults are applied. */
|
|
1084
1069
|
export type ResolvedConfig = z.infer<typeof blumeConfigSchema>;
|