blume 1.4.2 → 1.5.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.
Files changed (227) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/dist/cli/index.js +2260 -1100
  3. package/dist/cli/index.js.map +123 -117
  4. package/dist/types/ai/component-markdown.d.ts +14 -4
  5. package/dist/types/core/base-path.d.ts +8 -0
  6. package/dist/types/core/config-input.d.ts +87 -27
  7. package/dist/types/core/config.d.ts +2 -1
  8. package/dist/types/core/data.d.ts +16 -1
  9. package/dist/types/core/diagnostics.d.ts +5 -1
  10. package/dist/types/core/i18n-ui.d.ts +12 -0
  11. package/dist/types/core/schema.d.ts +116 -15
  12. package/dist/types/core/sources/types.d.ts +11 -1
  13. package/dist/types/core/standard-schema.d.ts +7 -3
  14. package/dist/types/core/types.d.ts +43 -2
  15. package/dist/types/core/ui-packs/index.d.ts +9 -1
  16. package/dist/types/openapi/references.d.ts +14 -7
  17. package/dist/types/seo/x-handle.d.ts +3 -2
  18. package/docs/advanced/api-reference.mdx +8 -6
  19. package/docs/configuration/ai.mdx +26 -8
  20. package/docs/configuration/search.mdx +2 -0
  21. package/docs/configuration/seo.mdx +1 -1
  22. package/docs/content/i18n.mdx +1 -1
  23. package/docs/content/meta.mdx +2 -1
  24. package/docs/content/meta.ts +1 -0
  25. package/docs/content/navigation.mdx +35 -1
  26. package/docs/content/sources.mdx +1 -1
  27. package/docs/content/versioning.mdx +106 -0
  28. package/docs/reference/cli.mdx +1 -0
  29. package/docs/reference/frontmatter.mdx +3 -0
  30. package/package.json +13 -1
  31. package/skills/blume-migrate/SKILL.md +2 -2
  32. package/skills/blume-migrate/references/docusaurus.md +1 -1
  33. package/skills/blume-migrate/references/fumadocs.md +1 -1
  34. package/skills/blume-migrate/references/mintlify.md +1 -1
  35. package/src/ai/agent-readability.ts +40 -12
  36. package/src/ai/api-catalog.ts +2 -2
  37. package/src/ai/ask-context.ts +49 -12
  38. package/src/ai/ask.ts +10 -1
  39. package/src/ai/component-markdown.ts +80 -43
  40. package/src/ai/llms.ts +42 -17
  41. package/src/ai/mcp/data.ts +48 -12
  42. package/src/ai/mcp/discovery.ts +52 -16
  43. package/src/ai/mcp/server.ts +280 -125
  44. package/src/ai/mcp/tools.ts +3 -3
  45. package/src/ai/skills.ts +32 -9
  46. package/src/ai/tar.ts +29 -70
  47. package/src/ai/visibility.ts +2 -2
  48. package/src/astro/component-slots.ts +2 -0
  49. package/src/astro/examples.ts +13 -5
  50. package/src/astro/generate.ts +113 -63
  51. package/src/astro/integration.ts +13 -2
  52. package/src/astro/islands.ts +23 -12
  53. package/src/astro/templates.ts +185 -41
  54. package/src/audit/agent.ts +16 -31
  55. package/src/audit/checks/content.ts +26 -11
  56. package/src/audit/checks/dns-aid.ts +3 -0
  57. package/src/audit/checks/indexability.ts +24 -6
  58. package/src/audit/checks/llms.ts +9 -4
  59. package/src/audit/checks/network.ts +2 -0
  60. package/src/audit/checks/social.ts +18 -10
  61. package/src/audit/crawl.ts +78 -25
  62. package/src/audit/report.ts +20 -19
  63. package/src/audit/run.ts +15 -5
  64. package/src/audit/snapshot.ts +29 -6
  65. package/src/audit/types.ts +25 -3
  66. package/src/blume-modules.d.ts +5 -1
  67. package/src/cli/commands/audit.ts +21 -21
  68. package/src/cli/commands/build.ts +30 -16
  69. package/src/cli/commands/dev.ts +15 -15
  70. package/src/cli/commands/doctor.ts +2 -0
  71. package/src/cli/commands/eject.ts +4 -4
  72. package/src/cli/commands/eval.ts +24 -30
  73. package/src/cli/commands/init.ts +9 -9
  74. package/src/cli/commands/mcp-stdio.ts +3 -0
  75. package/src/cli/commands/translate.ts +14 -3
  76. package/src/cli/commands/version.ts +85 -0
  77. package/src/cli/dev-lock.ts +31 -10
  78. package/src/cli/eject-scripts.ts +17 -2
  79. package/src/cli/env.ts +13 -30
  80. package/src/cli/index.ts +2 -0
  81. package/src/cli/init/questions.ts +1 -1
  82. package/src/cli/init/scaffold.ts +41 -13
  83. package/src/cli/internal-error.ts +1 -0
  84. package/src/cli/report-format.ts +22 -0
  85. package/src/components/content/AccordionItem.astro +2 -9
  86. package/src/components/content/ColorItem.astro +5 -13
  87. package/src/components/content/Component.astro +12 -8
  88. package/src/components/content/Frame.astro +2 -12
  89. package/src/components/content/Prompt.astro +12 -31
  90. package/src/components/content/Tab.astro +2 -9
  91. package/src/components/content/Tooltip.astro +1 -9
  92. package/src/components/content/Update.astro +2 -9
  93. package/src/components/content/auto-type-table.ts +3 -0
  94. package/src/components/content/diff.ts +9 -5
  95. package/src/components/content/github-info.ts +2 -0
  96. package/src/components/content/inline-markdown.ts +28 -0
  97. package/src/components/copy-feedback.ts +96 -0
  98. package/src/components/islands/ask-ai.tsx +111 -34
  99. package/src/components/islands/hooks.ts +5 -1
  100. package/src/components/islands/webmcp.ts +49 -12
  101. package/src/components/layout/Header.astro +25 -1
  102. package/src/components/layout/NavSelector.astro +11 -2
  103. package/src/components/layout/NavTree.astro +4 -2
  104. package/src/components/layout/PageActions.astro +20 -32
  105. package/src/components/layout/PageLayout.astro +8 -28
  106. package/src/components/layout/RootLayout.astro +24 -48
  107. package/src/components/layout/Search.astro +133 -22
  108. package/src/components/layout/VersionBanner.astro +39 -0
  109. package/src/components/layout/analytics-client.ts +8 -5
  110. package/src/components/layout/drawer-inert.ts +31 -0
  111. package/src/components/layout/hydration-hint.ts +1 -1
  112. package/src/components/layout/nav-utils.ts +1 -4
  113. package/src/components/layout/overrides.ts +25 -12
  114. package/src/components/layout/search/algolia.ts +18 -5
  115. package/src/components/layout/search/endpoint.ts +3 -0
  116. package/src/components/layout/search/flexsearch.ts +23 -7
  117. package/src/components/layout/search/orama-cloud.ts +1 -1
  118. package/src/components/layout/search/orama.ts +4 -1
  119. package/src/components/layout/search/pagefind.ts +8 -5
  120. package/src/components/layout/search/types.ts +45 -1
  121. package/src/components/layout/search/typesense.ts +19 -3
  122. package/src/components/openapi/ApiOverview.astro +32 -6
  123. package/src/components/openapi/AsyncApiOperation.astro +237 -0
  124. package/src/components/openapi/Bindings.astro +89 -0
  125. package/src/components/openapi/MethodBadge.astro +3 -0
  126. package/src/components/openapi/Operation.astro +7 -2
  127. package/src/components/openapi/PanelTabs.astro +131 -0
  128. package/src/components/openapi/ParametersTable.astro +2 -0
  129. package/src/components/openapi/RequestPanel.astro +12 -119
  130. package/src/components/openapi/async-snippets.ts +174 -0
  131. package/src/components/openapi/async.ts +348 -0
  132. package/src/components/openapi/helpers.ts +52 -20
  133. package/src/components/openapi/panel.ts +11 -8
  134. package/src/components/openapi/security.ts +102 -29
  135. package/src/components/openapi/snippets.ts +11 -11
  136. package/src/components/raf-throttle.ts +21 -0
  137. package/src/components/slug.ts +14 -0
  138. package/src/core/base-path.ts +18 -1
  139. package/src/core/component-overrides.ts +28 -23
  140. package/src/core/config-input.ts +96 -27
  141. package/src/core/config.ts +20 -7
  142. package/src/core/content.ts +3 -1
  143. package/src/core/data.ts +16 -1
  144. package/src/core/define-components.ts +5 -0
  145. package/src/core/diagnostics.ts +46 -38
  146. package/src/core/frontmatter.ts +74 -4
  147. package/src/core/graph.ts +137 -53
  148. package/src/core/i18n-ui.ts +15 -0
  149. package/src/core/i18n.ts +16 -8
  150. package/src/core/load-module.ts +1 -0
  151. package/src/core/manifest.ts +92 -3
  152. package/src/core/meta.ts +44 -14
  153. package/src/core/nav-diagnostics.ts +3 -3
  154. package/src/core/navigation.ts +247 -67
  155. package/src/core/probe.ts +7 -19
  156. package/src/core/project-graph.ts +27 -4
  157. package/src/core/schema.ts +219 -67
  158. package/src/core/site-url.ts +27 -0
  159. package/src/core/sources/assets.ts +2 -0
  160. package/src/core/sources/cache.ts +16 -8
  161. package/src/core/sources/github-releases.ts +39 -11
  162. package/src/core/sources/mdx-remote.ts +4 -0
  163. package/src/core/sources/normalize.ts +93 -22
  164. package/src/core/sources/notion.ts +76 -22
  165. package/src/core/sources/portable-text.ts +48 -12
  166. package/src/core/sources/resolve.ts +1 -0
  167. package/src/core/sources/sanity.ts +68 -14
  168. package/src/core/sources/types.ts +17 -1
  169. package/src/core/sources/watch.ts +1 -1
  170. package/src/core/standard-schema.ts +9 -3
  171. package/src/core/text-width.ts +26 -0
  172. package/src/core/tsconfig-aliases.ts +9 -5
  173. package/src/core/types.ts +45 -2
  174. package/src/core/ui-packs/index.ts +9 -1
  175. package/src/core/version-cut.ts +301 -0
  176. package/src/core/version.ts +2 -0
  177. package/src/core/versions.ts +170 -0
  178. package/src/deploy/adapter-output.ts +5 -2
  179. package/src/deploy/cloudflare-negotiation.ts +40 -11
  180. package/src/deploy/robots.ts +2 -1
  181. package/src/deploy/rss.ts +2 -1
  182. package/src/deploy/sitemap.ts +89 -8
  183. package/src/deploy/vercel-negotiation.ts +11 -4
  184. package/src/eval/agents.ts +13 -10
  185. package/src/eval/report.ts +5 -18
  186. package/src/eval/run.ts +2 -2
  187. package/src/eval/schema.ts +1 -1
  188. package/src/markdown/base-links.ts +6 -6
  189. package/src/markdown/directives.ts +7 -1
  190. package/src/markdown/heading-anchors.ts +17 -6
  191. package/src/markdown/index.ts +73 -24
  192. package/src/markdown/inline-code.ts +14 -2
  193. package/src/markdown/language-icon.ts +6 -2
  194. package/src/markdown/mdast.ts +18 -4
  195. package/src/markdown/package-commands.ts +63 -58
  196. package/src/markdown/table-wrap.ts +4 -1
  197. package/src/markdown/twoslash.ts +2 -0
  198. package/src/og/card.ts +50 -33
  199. package/src/og/derive.ts +43 -27
  200. package/src/openapi/asyncapi.ts +366 -0
  201. package/src/openapi/model.ts +135 -66
  202. package/src/openapi/parse.ts +166 -33
  203. package/src/openapi/references.ts +47 -22
  204. package/src/openapi/render-mdx.ts +137 -59
  205. package/src/openapi/scalar.ts +8 -10
  206. package/src/openapi/source.ts +126 -29
  207. package/src/registry/eject.ts +7 -2
  208. package/src/search/documents.ts +103 -39
  209. package/src/search/facets.ts +7 -5
  210. package/src/search/orama-index.ts +117 -32
  211. package/src/search/popular.ts +10 -5
  212. package/src/search/providers.ts +2 -2
  213. package/src/search/sync/index.ts +2 -0
  214. package/src/search/sync/typesense.ts +4 -2
  215. package/src/seo/jsonld.ts +24 -6
  216. package/src/seo/x-handle.ts +8 -3
  217. package/src/theme/chrome-icons.ts +7 -2
  218. package/src/theme/fonts.ts +8 -4
  219. package/src/theme/icons.ts +4 -2
  220. package/src/theme/palette.ts +27 -15
  221. package/src/translate/ledger.ts +4 -2
  222. package/src/translate/meta.ts +15 -6
  223. package/src/translate/report.ts +10 -19
  224. package/src/translate/run.ts +29 -38
  225. package/src/translate/validate.ts +52 -17
  226. package/src/translate/work-list.ts +0 -0
  227. package/src/cli/coalesce.ts +0 -43
@@ -1,8 +1,16 @@
1
1
  import type { ExampleLookup } from "../core/types.ts";
2
+ /**
3
+ * A statically-recovered data value. Parsed front matter and evaluated
4
+ * attribute literals are both plain data — scalars, dates, arrays, and
5
+ * nested maps — never functions or class instances.
6
+ */
7
+ export type EvaluatedValue = string | number | boolean | null | undefined | Date | EvaluatedValue[] | {
8
+ [key: string]: EvaluatedValue;
9
+ };
2
10
  /** Evaluated props plus whether any attribute resisted static evaluation. */
3
11
  interface EvaluatedProps {
4
12
  lossy: boolean;
5
- props: Record<string, unknown>;
13
+ props: Record<string, EvaluatedValue>;
6
14
  }
7
15
  /** A child component extracted by name (e.g. each `<Step>` under `<Steps>`). */
8
16
  export interface ComponentMarkdownChild extends EvaluatedProps {
@@ -20,7 +28,7 @@ export interface ComponentMarkdownContext extends EvaluatedProps {
20
28
  * serializer read page metadata directly, even when a prop expression is
21
29
  * not statically evaluable.
22
30
  */
23
- frontmatter: Record<string, unknown>;
31
+ frontmatter: Record<string, EvaluatedValue>;
24
32
  }
25
33
  /**
26
34
  * A component's Markdown serializer. Return the replacement Markdown, or
@@ -36,7 +44,9 @@ export type ComponentMarkdown = (context: ComponentMarkdownContext) => string |
36
44
  * the JSX verbatim, mirroring the "no example found" note the component renders
37
45
  * on the page.
38
46
  */
39
- export declare const exampleComponentSerializers: (examples: ExampleLookup) => Record<string, ComponentMarkdown>;
47
+ export declare const exampleComponentSerializers: (examples: ExampleLookup) => {
48
+ Component: ({ props }: ComponentMarkdownContext) => string | null;
49
+ };
40
50
  /**
41
51
  * Downlevel supported components in an MDX source to plain Markdown. Sources
42
52
  * with no supported components — and sources Satteri can't parse as MDX, e.g.
@@ -51,5 +61,5 @@ export declare const exampleComponentSerializers: (examples: ExampleLookup) => R
51
61
  * resolves the way it does when Astro renders the page — and handed to
52
62
  * serializers on their context.
53
63
  */
54
- export declare const downlevelComponents: (source: string, components?: Record<string, ComponentMarkdown>, frontmatter?: Record<string, unknown>) => string;
64
+ export declare const downlevelComponents: (source: string, components?: Record<string, ComponentMarkdown>, frontmatter?: Record<string, EvaluatedValue>) => string;
55
65
  export {};
@@ -22,6 +22,14 @@ export declare const normalizeBasePath: (input?: string) => string;
22
22
  * `/docs` and `/docs/` as the same page) and collapse an empty path to `/`.
23
23
  */
24
24
  export declare const normalizePath: (path: string) => string;
25
+ /**
26
+ * Canonicalize a route-ish string (a configured route, a page path, an agent-
27
+ * supplied route) to `/` or `/seg[/seg…]`: trimmed, exactly one leading slash,
28
+ * no trailing slash. The shared spelling of what openapi/references,
29
+ * ai/ask-context, and ai/mcp/server each hand-rolled with slightly different
30
+ * regexes.
31
+ */
32
+ export declare const normalizeRoute: (input: string) => string;
25
33
  /**
26
34
  * Whether a link target is a root-relative internal path (`/x`) — the only
27
35
  * shape a base path applies to. Protocol-relative (`//host`), absolute URLs,
@@ -171,6 +171,8 @@ export interface SanitySource {
171
171
  /** A Notion database; pages become entries, blocks become MDX. */
172
172
  export interface NotionSource {
173
173
  type: "notion";
174
+ /** Max concurrent Notion API requests; default 3 (Notion's per-integration pace). */
175
+ concurrency?: number;
174
176
  /** Notion database id. */
175
177
  database: string;
176
178
  /** Opt-in dev polling interval (seconds); omit to freeze for the session. */
@@ -565,6 +567,12 @@ export interface AskConfig {
565
567
  * limiting, and streaming. Accepts an absolute URL or root-relative path.
566
568
  */
567
569
  endpoint?: string;
570
+ /**
571
+ * Extra system-prompt text appended to the built-in instructions — use it
572
+ * for identity, language, or tone. The built-in grounding behavior (answer
573
+ * from the retrieved excerpts, cite pages as Markdown links) is preserved.
574
+ */
575
+ instructions?: string;
568
576
  /** Model id to use. Defaults to `openai/gpt-5.5`. */
569
577
  model?: string;
570
578
  /** Which backend routes the request. Defaults to `gateway`. */
@@ -725,6 +733,53 @@ export interface I18nConfig {
725
733
  */
726
734
  ui?: Record<string, Record<string, Record<string, string>>>;
727
735
  }
736
+ /** A frozen documentation snapshot: a directory under the content root. */
737
+ export interface ArchivedVersionInput {
738
+ /**
739
+ * The "you're viewing an old version" notice: `true` (default) for the
740
+ * built-in message, a string for custom copy, `false` to hide it.
741
+ */
742
+ banner?: boolean | string;
743
+ /**
744
+ * Where this version's pages point their canonical URL. `latest` (default)
745
+ * targets the same page in the current docs when it still exists (self
746
+ * otherwise); `self` keeps every page authoritative.
747
+ */
748
+ canonical?: "latest" | "self";
749
+ /**
750
+ * Directory name under the content root, and the URL segment. Must start
751
+ * with a letter (e.g. `v1.0`).
752
+ */
753
+ id: string;
754
+ /** Switcher label; defaults to the id. */
755
+ label?: string;
756
+ /** Emit `noindex` on every page of this version. Defaults to `false`. */
757
+ noindex?: boolean;
758
+ }
759
+ /**
760
+ * Docs versioning. Opt-in: the latest docs live at the content root with
761
+ * unprefixed URLs, and each archived version is a frozen snapshot directory
762
+ * (`content/docs/<id>/`) cut with `blume version <id>`. Archived means frozen:
763
+ * snapshots carry their own translations and are never retranslated.
764
+ */
765
+ export interface VersionsConfig {
766
+ /** Frozen snapshots, newest first — this order is the switcher order. */
767
+ archived?: ArchivedVersionInput[];
768
+ /** Labels the unprefixed tree (the latest docs) in the switcher. */
769
+ current: {
770
+ /** Small tag rendered next to the label (e.g. `Latest`). */
771
+ badge?: string;
772
+ label: string;
773
+ };
774
+ switcher?: {
775
+ /**
776
+ * Where switching lands when the page has no equivalent in the target
777
+ * version: `same-page` (default) goes to the equivalent when it exists
778
+ * (version root otherwise); `root` always goes to the version root.
779
+ */
780
+ redirect?: "same-page" | "root";
781
+ };
782
+ }
728
783
  /**
729
784
  * Where and how the site deploys. `site` (and `adapter`) are auto-detected from
730
785
  * the platform env on Vercel, Netlify, and Cloudflare.
@@ -958,22 +1013,17 @@ export interface ReactConfig {
958
1013
  compiler?: boolean;
959
1014
  }
960
1015
  /**
961
- * OpenAPI reference. By default (`renderer: "blume"`) Blume renders its own UI:
962
- * one real page per operation, grouped by tag in the sidebar and included in
963
- * search, llms.txt, and OG. Set `renderer: "scalar"` for the embedded Scalar
964
- * SPA (a single self-contained route).
1016
+ * The shared shape of both API-reference blocks (`openapi`, `asyncapi`). Only
1017
+ * the per-block defaults differ; those are documented on the extending
1018
+ * interfaces.
965
1019
  */
966
- export interface OpenApiConfig {
967
- /** Code-sample languages shown per operation (Blume renderer). */
968
- codeSamples?: string[];
1020
+ interface ReferenceConfig {
969
1021
  /** Turn the reference on. Defaults to `false`. */
970
1022
  enabled?: boolean;
971
1023
  /** Start nested schema rows expanded (Blume renderer). Defaults to `false`. */
972
1024
  expandSchemas?: boolean;
973
1025
  /** Who renders the reference. Defaults to `blume`. */
974
1026
  renderer?: "blume" | "scalar";
975
- /** Where the reference mounts. Defaults to `/reference`. */
976
- route?: string;
977
1027
  /**
978
1028
  * Extra Scalar options forwarded verbatim to the embedded `<ScalarComponent>`
979
1029
  * (Scalar renderer only) — e.g. `localization`, `agent`,
@@ -989,27 +1039,35 @@ export interface OpenApiConfig {
989
1039
  theme?: string;
990
1040
  }
991
1041
  /**
992
- * AsyncAPI reference, rendered via the embedded Scalar SPA (which auto-detects
993
- * the document type). Same shape as {@link OpenApiConfig}; only the default
994
- * `route` differs.
1042
+ * OpenAPI reference. By default (`renderer: "blume"`) Blume renders its own UI:
1043
+ * one real page per operation, grouped by tag in the sidebar and included in
1044
+ * search, llms.txt, and OG. Set `renderer: "scalar"` for the embedded Scalar
1045
+ * SPA (a single self-contained route).
995
1046
  */
996
- export interface AsyncApiConfig {
997
- /** Turn the reference on. Defaults to `false`. */
998
- enabled?: boolean;
999
- /** Where the reference mounts. Defaults to `/events`. */
1047
+ export interface OpenApiConfig extends ReferenceConfig {
1048
+ /**
1049
+ * Code-sample languages shown per operation (Blume renderer). Defaults to
1050
+ * `["curl", "js", "python"]`.
1051
+ */
1052
+ codeSamples?: string[];
1053
+ /** Where the reference mounts. Defaults to `/reference`. */
1000
1054
  route?: string;
1055
+ }
1056
+ /**
1057
+ * AsyncAPI reference. Same shape as {@link OpenApiConfig}: by default
1058
+ * (`renderer: "blume"`) Blume normalizes the spec to AsyncAPI 3.x and renders
1059
+ * its own UI — one real page per operation, grouped by tag (or channel) in the
1060
+ * sidebar and included in search, llms.txt, and OG. Set `renderer: "scalar"`
1061
+ * for the embedded Scalar SPA (a single self-contained route).
1062
+ */
1063
+ export interface AsyncApiConfig extends ReferenceConfig {
1001
1064
  /**
1002
- * Extra Scalar options forwarded verbatim to the embedded `<ScalarComponent>`.
1003
- * These win over Blume's derived spec/theme config — a full escape hatch to
1004
- * Scalar's API.
1065
+ * Code-sample tools shown per operation (Blume renderer). Defaults to every
1066
+ * tool appropriate to the operation's protocol binding.
1005
1067
  */
1006
- scalar?: Record<string, unknown>;
1007
- /** One or more specs. */
1008
- sources?: OpenApiSource[];
1009
- /** Shorthand for a single source. */
1010
- spec?: string;
1011
- /** Scalar theme name. */
1012
- theme?: string;
1068
+ codeSamples?: string[];
1069
+ /** Where the reference mounts. Defaults to `/events`. */
1070
+ route?: string;
1013
1071
  }
1014
1072
  /** `<Component />` example previews (the object form of `examples`). */
1015
1073
  export interface ExamplesConfig {
@@ -1126,7 +1184,7 @@ export interface BlumeConfig {
1126
1184
  ai?: AiConfig;
1127
1185
  /** Analytics providers (PostHog, Vercel, or arbitrary scripts). */
1128
1186
  analytics?: AnalyticsConfig;
1129
- /** AsyncAPI reference (embedded Scalar renderer). */
1187
+ /** AsyncAPI reference (native renderer by default, Scalar opt-out). */
1130
1188
  asyncapi?: AsyncApiConfig;
1131
1189
  /** Site-wide announcement banner shown above the header. */
1132
1190
  banner?: BannerConfig;
@@ -1199,5 +1257,7 @@ export interface BlumeConfig {
1199
1257
  title?: string;
1200
1258
  /** On-page table of contents. Defaults to on (H2–H3). */
1201
1259
  toc?: TocConfig;
1260
+ /** Docs versioning (opt-in frozen snapshots with a version switcher). */
1261
+ versions?: VersionsConfig;
1202
1262
  }
1203
1263
  export {};
@@ -60,7 +60,8 @@ import type { Diagnostic } from "./types.ts";
60
60
  * **Reference docs**
61
61
  * - `openapi` — native OpenAPI reference: one real page per operation, woven
62
62
  * into the sidebar and search. Point `sources`/`spec` at your spec.
63
- * - `asyncapi` — AsyncAPI reference via the embedded Scalar renderer.
63
+ * - `asyncapi` — native AsyncAPI reference with the same treatment; 2.x specs
64
+ * are normalized to 3.x automatically.
64
65
  *
65
66
  * **Search & AI**
66
67
  * - `search` — search backend `provider` (`orama` by default; `pagefind`,
@@ -1,6 +1,6 @@
1
1
  import type { UIStrings } from "./i18n-ui.ts";
2
2
  import type { ResolvedConfig, SearchProvider } from "./schema.ts";
3
- import type { Navigation, RouteAlternate } from "./types.ts";
3
+ import type { Navigation, RouteAlternate, VersionAlternate } from "./types.ts";
4
4
  /**
5
5
  * The shape of the `blume:data` virtual module — the resolved, serializable
6
6
  * snapshot of a site that custom `.astro` pages read with
@@ -86,6 +86,14 @@ export interface BlumeRoute {
86
86
  locale: string;
87
87
  path: string;
88
88
  title: string;
89
+ /** Resolved docs version (`""` for the current docs). */
90
+ version: string;
91
+ /**
92
+ * Versions this logical page exists in within this route's locale — the
93
+ * current version first, then archived versions in configured order. Empty
94
+ * when versioning is off.
95
+ */
96
+ versionAlternates: VersionAlternate[];
89
97
  }
90
98
  /** Site-wide settings derived from `blume.config` — the `config` field of {@link BlumeData}. */
91
99
  export interface BlumeDataConfig {
@@ -171,6 +179,8 @@ export interface BlumeDataConfig {
171
179
  * WebMCP in-page tools (`ai.webmcp`), plus whether llms.txt exists for the
172
180
  * list tool to fetch (`ai.llmsTxt.enabled`).
173
181
  */
182
+ /** Docs versioning config; `null` when the site is unversioned. */
183
+ versions: NonNullable<ResolvedConfig["versions"]> | null;
174
184
  webmcp: {
175
185
  enabled: boolean;
176
186
  llms: boolean;
@@ -205,6 +215,11 @@ export interface BlumeData {
205
215
  navigation: Navigation;
206
216
  /** Per-locale navigation trees, keyed by locale code (empty without i18n). */
207
217
  navigationByLocale: Record<string, Navigation>;
218
+ /**
219
+ * Per-archived-version navigation trees, keyed by version id and then locale
220
+ * code (`""` on a single-locale site). Empty when versioning is off.
221
+ */
222
+ navigationByVersion: Record<string, Record<string, Navigation>>;
208
223
  routes: BlumeRoute[];
209
224
  /** Resolved UI strings for the default locale. */
210
225
  ui: UIStrings;
@@ -42,4 +42,8 @@ export declare const diagnosticsFromZod: (error: ZodError, options: {
42
42
  /** Format a single diagnostic for terminal output. */
43
43
  export declare const formatDiagnostic: (diagnostic: Diagnostic, root?: string) => string;
44
44
  export declare const hasErrors: (diagnostics: Diagnostic[]) => boolean;
45
- export declare const countBySeverity: (diagnostics: Diagnostic[]) => Record<Diagnostic["severity"], number>;
45
+ export declare const countBySeverity: (diagnostics: Diagnostic[]) => {
46
+ error: number;
47
+ info: number;
48
+ warning: number;
49
+ };
@@ -91,6 +91,7 @@ declare const uiStringsObject: z.ZodObject<{
91
91
  search: z.ZodPrefault<z.ZodObject<{
92
92
  all: z.ZodDefault<z.ZodString>;
93
93
  allLanguages: z.ZodDefault<z.ZodString>;
94
+ allVersions: z.ZodDefault<z.ZodString>;
94
95
  askAi: z.ZodDefault<z.ZodString>;
95
96
  askAiHint: z.ZodDefault<z.ZodString>;
96
97
  button: z.ZodDefault<z.ZodString>;
@@ -108,6 +109,11 @@ declare const uiStringsObject: z.ZodObject<{
108
109
  toc: z.ZodPrefault<z.ZodObject<{
109
110
  title: z.ZodDefault<z.ZodString>;
110
111
  }, z.core.$strip>>;
112
+ versions: z.ZodPrefault<z.ZodObject<{
113
+ latest: z.ZodDefault<z.ZodString>;
114
+ notice: z.ZodDefault<z.ZodString>;
115
+ switcher: z.ZodDefault<z.ZodString>;
116
+ }, z.core.$strip>>;
111
117
  }, z.core.$strip>;
112
118
  export declare const uiStringsSchema: z.ZodPrefault<z.ZodObject<{
113
119
  actions: z.ZodPrefault<z.ZodObject<{
@@ -193,6 +199,7 @@ export declare const uiStringsSchema: z.ZodPrefault<z.ZodObject<{
193
199
  search: z.ZodPrefault<z.ZodObject<{
194
200
  all: z.ZodDefault<z.ZodString>;
195
201
  allLanguages: z.ZodDefault<z.ZodString>;
202
+ allVersions: z.ZodDefault<z.ZodString>;
196
203
  askAi: z.ZodDefault<z.ZodString>;
197
204
  askAiHint: z.ZodDefault<z.ZodString>;
198
205
  button: z.ZodDefault<z.ZodString>;
@@ -210,6 +217,11 @@ export declare const uiStringsSchema: z.ZodPrefault<z.ZodObject<{
210
217
  toc: z.ZodPrefault<z.ZodObject<{
211
218
  title: z.ZodDefault<z.ZodString>;
212
219
  }, z.core.$strip>>;
220
+ versions: z.ZodPrefault<z.ZodObject<{
221
+ latest: z.ZodDefault<z.ZodString>;
222
+ notice: z.ZodDefault<z.ZodString>;
223
+ switcher: z.ZodDefault<z.ZodString>;
224
+ }, z.core.$strip>>;
213
225
  }, z.core.$strip>>;
214
226
  /** A fully-resolved dictionary; every key present. */
215
227
  export type UIStrings = z.infer<typeof uiStringsObject>;
@@ -12,6 +12,19 @@ declare const hydrationMode: z.ZodEnum<{
12
12
  only: "only";
13
13
  }>;
14
14
  export type HydrationMode = z.infer<typeof hydrationMode>;
15
+ /**
16
+ * How a sidebar group renders:
17
+ * - `flat`: a non-collapsible header with its items listed beneath (default).
18
+ * - `group`: a collapsible `<details>` disclosure.
19
+ * - `page`: a single row that drills into a sub-panel showing only this group's
20
+ * items, with a back arrow at the top.
21
+ */
22
+ declare const sidebarDisplaySchema: z.ZodEnum<{
23
+ flat: "flat";
24
+ group: "group";
25
+ page: "page";
26
+ }>;
27
+ export type SidebarDisplay = z.infer<typeof sidebarDisplaySchema>;
15
28
  /** Frontmatter accepted on any content page. */
16
29
  declare const pageMetaBaseSchema: z.ZodObject<{
17
30
  ai: z.ZodPrefault<z.ZodObject<{
@@ -58,6 +71,11 @@ declare const pageMetaBaseSchema: z.ZodObject<{
58
71
  }, z.core.$strict>>;
59
72
  sidebar: z.ZodPrefault<z.ZodObject<{
60
73
  badge: z.ZodOptional<z.ZodString>;
74
+ display: z.ZodOptional<z.ZodEnum<{
75
+ flat: "flat";
76
+ group: "group";
77
+ page: "page";
78
+ }>>;
61
79
  hidden: z.ZodDefault<z.ZodBoolean>;
62
80
  icon: z.ZodOptional<z.ZodString>;
63
81
  label: z.ZodOptional<z.ZodString>;
@@ -112,6 +130,11 @@ export declare const pageMetaSchema: z.ZodObject<{
112
130
  }, z.core.$strict>>;
113
131
  sidebar: z.ZodPrefault<z.ZodObject<{
114
132
  badge: z.ZodOptional<z.ZodString>;
133
+ display: z.ZodOptional<z.ZodEnum<{
134
+ flat: "flat";
135
+ group: "group";
136
+ page: "page";
137
+ }>>;
115
138
  hidden: z.ZodDefault<z.ZodBoolean>;
116
139
  icon: z.ZodOptional<z.ZodString>;
117
140
  label: z.ZodOptional<z.ZodString>;
@@ -123,21 +146,13 @@ export declare const pageMetaSchema: z.ZodObject<{
123
146
  }, z.core.$strict>;
124
147
  export type PageMeta = z.infer<typeof pageMetaBaseSchema>;
125
148
  export type PageMetaInput = z.input<typeof pageMetaBaseSchema>;
126
- /**
127
- * How a sidebar group renders:
128
- * - `flat`: a non-collapsible header with its items listed beneath (default).
129
- * - `group`: a collapsible `<details>` disclosure.
130
- * - `page`: a single row that drills into a sub-panel showing only this group's
131
- * items, with a back arrow at the top.
132
- */
133
- declare const sidebarDisplaySchema: z.ZodEnum<{
134
- flat: "flat";
135
- group: "group";
136
- page: "page";
137
- }>;
138
- export type SidebarDisplay = z.infer<typeof sidebarDisplaySchema>;
139
149
  export declare const folderMetaSchema: z.ZodObject<{
140
150
  collapsed: z.ZodOptional<z.ZodBoolean>;
151
+ display: z.ZodOptional<z.ZodEnum<{
152
+ flat: "flat";
153
+ group: "group";
154
+ page: "page";
155
+ }>>;
141
156
  icon: z.ZodOptional<z.ZodString>;
142
157
  order: z.ZodOptional<z.ZodNumber>;
143
158
  pages: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -189,6 +204,7 @@ declare const contentSourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
189
204
  query: z.ZodString;
190
205
  type: z.ZodLiteral<"sanity">;
191
206
  }, z.core.$strip>, z.ZodObject<{
207
+ concurrency: z.ZodOptional<z.ZodNumber>;
192
208
  database: z.ZodString;
193
209
  pollInterval: z.ZodOptional<z.ZodNumber>;
194
210
  prefix: z.ZodOptional<z.ZodString>;
@@ -244,6 +260,7 @@ declare const aiConfigSchema: z.ZodObject<{
244
260
  baseUrl: z.ZodOptional<z.ZodURL>;
245
261
  enabled: z.ZodDefault<z.ZodBoolean>;
246
262
  endpoint: z.ZodOptional<z.ZodString>;
263
+ instructions: z.ZodOptional<z.ZodString>;
247
264
  model: z.ZodDefault<z.ZodString>;
248
265
  provider: z.ZodDefault<z.ZodEnum<{
249
266
  gateway: "gateway";
@@ -316,6 +333,52 @@ declare const i18nConfigSchema: z.ZodObject<{
316
333
  }>>;
317
334
  ui: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>>>;
318
335
  }, z.core.$strict>;
336
+ /**
337
+ * Version ids must start with a letter (`v1.0`, not `1.0`): the id doubles as
338
+ * the snapshot directory name, and a leading digit would collide with the
339
+ * numeric-prefix ordering convention (`01-intro.mdx`), which strips `1.0/` to
340
+ * `0/`. The rest allows word characters, dots, and hyphens — URL-safe as-is.
341
+ */
342
+ export declare const VERSION_ID: RegExp;
343
+ /** A frozen documentation snapshot: a directory under the content root. */
344
+ declare const archivedVersionSchema: z.ZodObject<{
345
+ banner: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
346
+ canonical: z.ZodDefault<z.ZodEnum<{
347
+ latest: "latest";
348
+ self: "self";
349
+ }>>;
350
+ id: z.ZodString;
351
+ label: z.ZodOptional<z.ZodString>;
352
+ noindex: z.ZodDefault<z.ZodBoolean>;
353
+ }, z.core.$strict>;
354
+ /**
355
+ * Docs versioning. Opt-in: the latest docs live at the content root with
356
+ * unprefixed URLs, and each archived version is a frozen snapshot directory
357
+ * (`content/docs/<id>/`) cut with `blume version <id>`. Archived means frozen:
358
+ * snapshots carry their own translations and are never retranslated.
359
+ */
360
+ declare const versionsConfigSchema: z.ZodObject<{
361
+ archived: z.ZodDefault<z.ZodArray<z.ZodObject<{
362
+ banner: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
363
+ canonical: z.ZodDefault<z.ZodEnum<{
364
+ latest: "latest";
365
+ self: "self";
366
+ }>>;
367
+ id: z.ZodString;
368
+ label: z.ZodOptional<z.ZodString>;
369
+ noindex: z.ZodDefault<z.ZodBoolean>;
370
+ }, z.core.$strict>>>;
371
+ current: z.ZodObject<{
372
+ badge: z.ZodOptional<z.ZodString>;
373
+ label: z.ZodString;
374
+ }, z.core.$strict>;
375
+ switcher: z.ZodPrefault<z.ZodObject<{
376
+ redirect: z.ZodDefault<z.ZodEnum<{
377
+ root: "root";
378
+ "same-page": "same-page";
379
+ }>>;
380
+ }, z.core.$strict>>;
381
+ }, z.core.$strict>;
319
382
  /**
320
383
  * Content signals accept a boolean shorthand or a per-signal object, and
321
384
  * normalize to `{ search, aiInput, aiTrain }` — or `null` when disabled, so
@@ -382,7 +445,8 @@ declare const dateFormatConfigSchema: z.ZodObject<{
382
445
  }, z.core.$strict>;
383
446
  /**
384
447
  * A single spec rendered by the API reference. `spec` is a local path or an
385
- * `http(s)` URL (OpenAPI for the Blume renderer; OpenAPI or AsyncAPI for Scalar).
448
+ * `http(s)` URL (an OpenAPI document under `openapi`, an AsyncAPI document
449
+ * under `asyncapi`).
386
450
  */
387
451
  declare const openapiSourceSchema: z.ZodObject<{
388
452
  includeInLlms: z.ZodDefault<z.ZodBoolean>;
@@ -400,6 +464,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
400
464
  baseUrl: z.ZodOptional<z.ZodURL>;
401
465
  enabled: z.ZodDefault<z.ZodBoolean>;
402
466
  endpoint: z.ZodOptional<z.ZodString>;
467
+ instructions: z.ZodOptional<z.ZodString>;
403
468
  model: z.ZodDefault<z.ZodString>;
404
469
  provider: z.ZodDefault<z.ZodEnum<{
405
470
  gateway: "gateway";
@@ -453,7 +518,13 @@ export declare const blumeConfigSchema: z.ZodObject<{
453
518
  vercel: z.ZodOptional<z.ZodBoolean>;
454
519
  }, z.core.$strict>>;
455
520
  asyncapi: z.ZodPrefault<z.ZodObject<{
521
+ codeSamples: z.ZodDefault<z.ZodArray<z.ZodString>>;
456
522
  enabled: z.ZodDefault<z.ZodBoolean>;
523
+ expandSchemas: z.ZodDefault<z.ZodBoolean>;
524
+ renderer: z.ZodDefault<z.ZodEnum<{
525
+ blume: "blume";
526
+ scalar: "scalar";
527
+ }>>;
457
528
  route: z.ZodDefault<z.ZodString>;
458
529
  scalar: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
459
530
  sources: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -527,6 +598,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
527
598
  query: z.ZodString;
528
599
  type: z.ZodLiteral<"sanity">;
529
600
  }, z.core.$strip>, z.ZodObject<{
601
+ concurrency: z.ZodOptional<z.ZodNumber>;
530
602
  database: z.ZodString;
531
603
  pollInterval: z.ZodOptional<z.ZodNumber>;
532
604
  prefix: z.ZodOptional<z.ZodString>;
@@ -1018,13 +1090,38 @@ export declare const blumeConfigSchema: z.ZodObject<{
1018
1090
  maxHeadingLevel: z.ZodOptional<z.ZodNumber>;
1019
1091
  minHeadingLevel: z.ZodOptional<z.ZodNumber>;
1020
1092
  }, z.core.$strict>]>>, z.ZodTransform<{
1021
- enabled: boolean;
1093
+ enabled: (boolean | {
1094
+ maxHeadingLevel?: number | undefined;
1095
+ minHeadingLevel?: number | undefined;
1096
+ }) & boolean;
1022
1097
  maxLevel: number;
1023
1098
  minLevel: number;
1024
1099
  }, boolean | {
1025
1100
  maxHeadingLevel?: number | undefined;
1026
1101
  minHeadingLevel?: number | undefined;
1027
1102
  }>>;
1103
+ versions: z.ZodOptional<z.ZodObject<{
1104
+ archived: z.ZodDefault<z.ZodArray<z.ZodObject<{
1105
+ banner: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
1106
+ canonical: z.ZodDefault<z.ZodEnum<{
1107
+ latest: "latest";
1108
+ self: "self";
1109
+ }>>;
1110
+ id: z.ZodString;
1111
+ label: z.ZodOptional<z.ZodString>;
1112
+ noindex: z.ZodDefault<z.ZodBoolean>;
1113
+ }, z.core.$strict>>>;
1114
+ current: z.ZodObject<{
1115
+ badge: z.ZodOptional<z.ZodString>;
1116
+ label: z.ZodString;
1117
+ }, z.core.$strict>;
1118
+ switcher: z.ZodPrefault<z.ZodObject<{
1119
+ redirect: z.ZodDefault<z.ZodEnum<{
1120
+ root: "root";
1121
+ "same-page": "same-page";
1122
+ }>>;
1123
+ }, z.core.$strict>>;
1124
+ }, z.core.$strict>>;
1028
1125
  }, z.core.$strict>;
1029
1126
  /** Resolved config: every field present after defaults are applied. */
1030
1127
  export type ResolvedConfig = z.infer<typeof blumeConfigSchema>;
@@ -1036,6 +1133,10 @@ export type FrontmatterExtend = Record<string, StandardSchema>;
1036
1133
  export type ResolvedI18nConfig = z.infer<typeof i18nConfigSchema>;
1037
1134
  /** A configured locale with display metadata. */
1038
1135
  export type LocaleConfig = z.infer<typeof localeSchema>;
1136
+ /** Resolved versions block (present only when the project opts into versioning). */
1137
+ export type ResolvedVersionsConfig = z.infer<typeof versionsConfigSchema>;
1138
+ /** A configured archived (frozen) version. */
1139
+ export type ArchivedVersionConfig = z.infer<typeof archivedVersionSchema>;
1039
1140
  /**
1040
1141
  * User-authored config, straight off the schema. The public, hand-documented
1041
1142
  * authoring type is `BlumeConfig` in `./config-input.ts`, which a compile-time
@@ -1,4 +1,4 @@
1
- import type { FrontmatterExtend, ResolvedI18nConfig } from "../schema.ts";
1
+ import type { FolderMeta, FrontmatterExtend, ResolvedI18nConfig, ResolvedVersionsConfig } from "../schema.ts";
2
2
  import type { Diagnostic } from "../types.ts";
3
3
  /**
4
4
  * A single content item, normalized by a source adapter. Adapters lower their
@@ -41,6 +41,14 @@ export interface SourceLoadResult {
41
41
  entries: SourceEntry[];
42
42
  /** Source-level diagnostics (e.g. an offline cache fallback warning). */
43
43
  diagnostics: Diagnostic[];
44
+ /**
45
+ * Folder meta the source derives for the sidebar groups its entries create,
46
+ * keyed by locale-stripped group path (the `meta.ts` key space). The OpenAPI
47
+ * source labels each tag directory with the spec's own tag name, so the
48
+ * sidebar shows `OAuth2`/`Größe` instead of a re-humanized slug. Merged
49
+ * beneath user-authored meta files, which always win.
50
+ */
51
+ folderMeta?: Record<string, FolderMeta>;
44
52
  }
45
53
  /**
46
54
  * Per-source runtime context, handed to an adapter factory at construction so
@@ -119,4 +127,6 @@ export interface NormalizeContext {
119
127
  * applied to a page only when its resolved `type` matches.
120
128
  */
121
129
  typeFrontmatter?: Record<string, FrontmatterExtend>;
130
+ /** Docs versioning config; a leading archived-version dir becomes the page's version. */
131
+ versions?: ResolvedVersionsConfig;
122
132
  }
@@ -30,12 +30,16 @@ export interface StandardSchema<Input = unknown, Output = Input> {
30
30
  readonly "~standard": {
31
31
  readonly version: 1;
32
32
  readonly vendor: string;
33
- readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
33
+ readonly validate: (value: Input) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
34
34
  readonly types?: {
35
35
  readonly input: Input;
36
36
  readonly output: Output;
37
37
  } | undefined;
38
38
  };
39
39
  }
40
- /** Whether a config-supplied value implements the `~standard` contract. */
41
- export declare const isStandardSchema: (value: unknown) => value is StandardSchema;
40
+ /**
41
+ * Whether a config-supplied value implements the `~standard` contract. Generic
42
+ * so it can decode any input at the config boundary (`z.custom` hands it the
43
+ * raw config value) while narrowing whatever type the caller holds.
44
+ */
45
+ export declare const isStandardSchema: <T>(value: T) => value is T & StandardSchema;