blume 1.4.3 → 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 (194) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cli/index.js +1621 -576
  3. package/dist/cli/index.js.map +109 -104
  4. package/dist/types/ai/component-markdown.d.ts +14 -4
  5. package/dist/types/core/config-input.d.ts +79 -27
  6. package/dist/types/core/config.d.ts +2 -1
  7. package/dist/types/core/data.d.ts +16 -1
  8. package/dist/types/core/diagnostics.d.ts +5 -1
  9. package/dist/types/core/i18n-ui.d.ts +12 -0
  10. package/dist/types/core/schema.d.ts +112 -15
  11. package/dist/types/core/sources/types.d.ts +3 -1
  12. package/dist/types/core/standard-schema.d.ts +7 -3
  13. package/dist/types/core/types.d.ts +43 -2
  14. package/dist/types/core/ui-packs/index.d.ts +9 -1
  15. package/dist/types/openapi/references.d.ts +6 -5
  16. package/dist/types/seo/x-handle.d.ts +3 -2
  17. package/docs/advanced/api-reference.mdx +8 -6
  18. package/docs/configuration/search.mdx +2 -0
  19. package/docs/configuration/seo.mdx +1 -1
  20. package/docs/content/i18n.mdx +1 -1
  21. package/docs/content/meta.mdx +2 -1
  22. package/docs/content/meta.ts +1 -0
  23. package/docs/content/navigation.mdx +35 -1
  24. package/docs/content/versioning.mdx +106 -0
  25. package/docs/reference/cli.mdx +1 -0
  26. package/docs/reference/frontmatter.mdx +3 -0
  27. package/package.json +3 -1
  28. package/skills/blume-migrate/SKILL.md +2 -2
  29. package/skills/blume-migrate/references/docusaurus.md +1 -1
  30. package/skills/blume-migrate/references/fumadocs.md +1 -1
  31. package/skills/blume-migrate/references/mintlify.md +1 -1
  32. package/src/ai/agent-readability.ts +37 -10
  33. package/src/ai/ask-context.ts +5 -1
  34. package/src/ai/ask.ts +10 -1
  35. package/src/ai/component-markdown.ts +80 -43
  36. package/src/ai/llms.ts +40 -16
  37. package/src/ai/mcp/data.ts +48 -12
  38. package/src/ai/mcp/discovery.ts +28 -11
  39. package/src/ai/mcp/server.ts +183 -38
  40. package/src/ai/mcp/tools.ts +3 -3
  41. package/src/ai/skills.ts +32 -9
  42. package/src/ai/visibility.ts +2 -2
  43. package/src/astro/component-slots.ts +2 -0
  44. package/src/astro/examples.ts +6 -2
  45. package/src/astro/generate.ts +54 -29
  46. package/src/astro/integration.ts +13 -2
  47. package/src/astro/islands.ts +16 -9
  48. package/src/astro/templates.ts +152 -33
  49. package/src/audit/agent.ts +2 -2
  50. package/src/audit/checks/content.ts +26 -11
  51. package/src/audit/checks/dns-aid.ts +3 -0
  52. package/src/audit/checks/indexability.ts +24 -6
  53. package/src/audit/checks/llms.ts +9 -4
  54. package/src/audit/checks/network.ts +2 -0
  55. package/src/audit/checks/social.ts +18 -10
  56. package/src/audit/crawl.ts +37 -9
  57. package/src/audit/report.ts +20 -19
  58. package/src/audit/run.ts +5 -2
  59. package/src/audit/snapshot.ts +2 -4
  60. package/src/audit/types.ts +25 -3
  61. package/src/blume-modules.d.ts +5 -1
  62. package/src/cli/commands/audit.ts +9 -4
  63. package/src/cli/commands/build.ts +15 -9
  64. package/src/cli/commands/dev.ts +2 -0
  65. package/src/cli/commands/doctor.ts +2 -0
  66. package/src/cli/commands/eval.ts +7 -3
  67. package/src/cli/commands/init.ts +9 -9
  68. package/src/cli/commands/mcp-stdio.ts +3 -0
  69. package/src/cli/commands/translate.ts +14 -3
  70. package/src/cli/commands/version.ts +85 -0
  71. package/src/cli/dev-lock.ts +31 -10
  72. package/src/cli/eject-scripts.ts +17 -2
  73. package/src/cli/index.ts +2 -0
  74. package/src/cli/init/questions.ts +1 -1
  75. package/src/cli/init/scaffold.ts +22 -15
  76. package/src/cli/internal-error.ts +1 -0
  77. package/src/components/content/auto-type-table.ts +3 -0
  78. package/src/components/content/diff.ts +9 -5
  79. package/src/components/content/github-info.ts +2 -0
  80. package/src/components/islands/ask-ai.tsx +33 -25
  81. package/src/components/islands/hooks.ts +5 -1
  82. package/src/components/islands/webmcp.ts +49 -12
  83. package/src/components/layout/Header.astro +25 -1
  84. package/src/components/layout/NavSelector.astro +11 -2
  85. package/src/components/layout/NavTree.astro +4 -2
  86. package/src/components/layout/RootLayout.astro +18 -0
  87. package/src/components/layout/Search.astro +77 -13
  88. package/src/components/layout/VersionBanner.astro +39 -0
  89. package/src/components/layout/analytics-client.ts +8 -5
  90. package/src/components/layout/hydration-hint.ts +1 -1
  91. package/src/components/layout/nav-utils.ts +1 -4
  92. package/src/components/layout/overrides.ts +25 -12
  93. package/src/components/layout/search/algolia.ts +18 -5
  94. package/src/components/layout/search/endpoint.ts +3 -0
  95. package/src/components/layout/search/flexsearch.ts +23 -7
  96. package/src/components/layout/search/orama-cloud.ts +1 -1
  97. package/src/components/layout/search/orama.ts +4 -1
  98. package/src/components/layout/search/pagefind.ts +2 -0
  99. package/src/components/layout/search/types.ts +13 -1
  100. package/src/components/layout/search/typesense.ts +19 -3
  101. package/src/components/openapi/ApiOverview.astro +32 -6
  102. package/src/components/openapi/AsyncApiOperation.astro +237 -0
  103. package/src/components/openapi/Bindings.astro +89 -0
  104. package/src/components/openapi/MethodBadge.astro +3 -0
  105. package/src/components/openapi/Operation.astro +7 -2
  106. package/src/components/openapi/PanelTabs.astro +131 -0
  107. package/src/components/openapi/ParametersTable.astro +2 -0
  108. package/src/components/openapi/RequestPanel.astro +12 -119
  109. package/src/components/openapi/async-snippets.ts +174 -0
  110. package/src/components/openapi/async.ts +348 -0
  111. package/src/components/openapi/helpers.ts +52 -20
  112. package/src/components/openapi/security.ts +102 -29
  113. package/src/components/openapi/snippets.ts +11 -11
  114. package/src/core/component-overrides.ts +28 -23
  115. package/src/core/config-input.ts +88 -27
  116. package/src/core/config.ts +20 -7
  117. package/src/core/content.ts +3 -1
  118. package/src/core/data.ts +16 -1
  119. package/src/core/define-components.ts +5 -0
  120. package/src/core/diagnostics.ts +46 -38
  121. package/src/core/frontmatter.ts +33 -7
  122. package/src/core/graph.ts +137 -53
  123. package/src/core/i18n-ui.ts +15 -0
  124. package/src/core/i18n.ts +16 -8
  125. package/src/core/load-module.ts +1 -0
  126. package/src/core/manifest.ts +92 -3
  127. package/src/core/meta.ts +44 -14
  128. package/src/core/nav-diagnostics.ts +3 -3
  129. package/src/core/navigation.ts +247 -67
  130. package/src/core/project-graph.ts +15 -3
  131. package/src/core/schema.ts +213 -67
  132. package/src/core/sources/assets.ts +2 -0
  133. package/src/core/sources/cache.ts +6 -0
  134. package/src/core/sources/github-releases.ts +39 -31
  135. package/src/core/sources/mdx-remote.ts +4 -0
  136. package/src/core/sources/normalize.ts +67 -20
  137. package/src/core/sources/notion.ts +49 -17
  138. package/src/core/sources/portable-text.ts +32 -11
  139. package/src/core/sources/sanity.ts +68 -14
  140. package/src/core/sources/types.ts +4 -0
  141. package/src/core/sources/watch.ts +1 -1
  142. package/src/core/standard-schema.ts +9 -3
  143. package/src/core/text-width.ts +26 -0
  144. package/src/core/tsconfig-aliases.ts +9 -5
  145. package/src/core/types.ts +45 -2
  146. package/src/core/ui-packs/index.ts +9 -1
  147. package/src/core/version-cut.ts +301 -0
  148. package/src/core/version.ts +2 -0
  149. package/src/core/versions.ts +170 -0
  150. package/src/deploy/adapter-output.ts +5 -2
  151. package/src/deploy/cloudflare-negotiation.ts +25 -10
  152. package/src/deploy/sitemap.ts +33 -1
  153. package/src/deploy/vercel-negotiation.ts +11 -4
  154. package/src/eval/report.ts +4 -4
  155. package/src/eval/run.ts +2 -2
  156. package/src/eval/schema.ts +1 -1
  157. package/src/markdown/base-links.ts +6 -6
  158. package/src/markdown/directives.ts +7 -1
  159. package/src/markdown/heading-anchors.ts +17 -6
  160. package/src/markdown/index.ts +73 -24
  161. package/src/markdown/inline-code.ts +14 -2
  162. package/src/markdown/language-icon.ts +6 -2
  163. package/src/markdown/mdast.ts +18 -4
  164. package/src/markdown/package-commands.ts +6 -8
  165. package/src/markdown/table-wrap.ts +4 -1
  166. package/src/markdown/twoslash.ts +2 -0
  167. package/src/og/card.ts +30 -11
  168. package/src/og/derive.ts +43 -27
  169. package/src/openapi/asyncapi.ts +366 -0
  170. package/src/openapi/model.ts +126 -57
  171. package/src/openapi/parse.ts +97 -5
  172. package/src/openapi/references.ts +12 -10
  173. package/src/openapi/render-mdx.ts +73 -34
  174. package/src/openapi/scalar.ts +6 -8
  175. package/src/openapi/source.ts +98 -28
  176. package/src/registry/eject.ts +7 -2
  177. package/src/search/documents.ts +25 -5
  178. package/src/search/facets.ts +7 -5
  179. package/src/search/orama-index.ts +66 -20
  180. package/src/search/popular.ts +10 -5
  181. package/src/search/providers.ts +2 -2
  182. package/src/search/sync/index.ts +2 -0
  183. package/src/search/sync/typesense.ts +4 -2
  184. package/src/seo/jsonld.ts +24 -6
  185. package/src/seo/x-handle.ts +8 -3
  186. package/src/theme/chrome-icons.ts +7 -2
  187. package/src/theme/fonts.ts +8 -4
  188. package/src/theme/icons.ts +4 -2
  189. package/src/theme/palette.ts +22 -14
  190. package/src/translate/meta.ts +15 -6
  191. package/src/translate/report.ts +9 -5
  192. package/src/translate/run.ts +10 -4
  193. package/src/translate/validate.ts +52 -17
  194. package/src/translate/work-list.ts +0 -0
@@ -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 {};
@@ -733,6 +733,53 @@ export interface I18nConfig {
733
733
  */
734
734
  ui?: Record<string, Record<string, Record<string, string>>>;
735
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
+ }
736
783
  /**
737
784
  * Where and how the site deploys. `site` (and `adapter`) are auto-detected from
738
785
  * the platform env on Vercel, Netlify, and Cloudflare.
@@ -966,22 +1013,17 @@ export interface ReactConfig {
966
1013
  compiler?: boolean;
967
1014
  }
968
1015
  /**
969
- * OpenAPI reference. By default (`renderer: "blume"`) Blume renders its own UI:
970
- * one real page per operation, grouped by tag in the sidebar and included in
971
- * search, llms.txt, and OG. Set `renderer: "scalar"` for the embedded Scalar
972
- * 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.
973
1019
  */
974
- export interface OpenApiConfig {
975
- /** Code-sample languages shown per operation (Blume renderer). */
976
- codeSamples?: string[];
1020
+ interface ReferenceConfig {
977
1021
  /** Turn the reference on. Defaults to `false`. */
978
1022
  enabled?: boolean;
979
1023
  /** Start nested schema rows expanded (Blume renderer). Defaults to `false`. */
980
1024
  expandSchemas?: boolean;
981
1025
  /** Who renders the reference. Defaults to `blume`. */
982
1026
  renderer?: "blume" | "scalar";
983
- /** Where the reference mounts. Defaults to `/reference`. */
984
- route?: string;
985
1027
  /**
986
1028
  * Extra Scalar options forwarded verbatim to the embedded `<ScalarComponent>`
987
1029
  * (Scalar renderer only) — e.g. `localization`, `agent`,
@@ -997,27 +1039,35 @@ export interface OpenApiConfig {
997
1039
  theme?: string;
998
1040
  }
999
1041
  /**
1000
- * AsyncAPI reference, rendered via the embedded Scalar SPA (which auto-detects
1001
- * the document type). Same shape as {@link OpenApiConfig}; only the default
1002
- * `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).
1003
1046
  */
1004
- export interface AsyncApiConfig {
1005
- /** Turn the reference on. Defaults to `false`. */
1006
- enabled?: boolean;
1007
- /** 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`. */
1008
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 {
1009
1064
  /**
1010
- * Extra Scalar options forwarded verbatim to the embedded `<ScalarComponent>`.
1011
- * These win over Blume's derived spec/theme config — a full escape hatch to
1012
- * Scalar's API.
1065
+ * Code-sample tools shown per operation (Blume renderer). Defaults to every
1066
+ * tool appropriate to the operation's protocol binding.
1013
1067
  */
1014
- scalar?: Record<string, unknown>;
1015
- /** One or more specs. */
1016
- sources?: OpenApiSource[];
1017
- /** Shorthand for a single source. */
1018
- spec?: string;
1019
- /** Scalar theme name. */
1020
- theme?: string;
1068
+ codeSamples?: string[];
1069
+ /** Where the reference mounts. Defaults to `/events`. */
1070
+ route?: string;
1021
1071
  }
1022
1072
  /** `<Component />` example previews (the object form of `examples`). */
1023
1073
  export interface ExamplesConfig {
@@ -1134,7 +1184,7 @@ export interface BlumeConfig {
1134
1184
  ai?: AiConfig;
1135
1185
  /** Analytics providers (PostHog, Vercel, or arbitrary scripts). */
1136
1186
  analytics?: AnalyticsConfig;
1137
- /** AsyncAPI reference (embedded Scalar renderer). */
1187
+ /** AsyncAPI reference (native renderer by default, Scalar opt-out). */
1138
1188
  asyncapi?: AsyncApiConfig;
1139
1189
  /** Site-wide announcement banner shown above the header. */
1140
1190
  banner?: BannerConfig;
@@ -1207,5 +1257,7 @@ export interface BlumeConfig {
1207
1257
  title?: string;
1208
1258
  /** On-page table of contents. Defaults to on (H2–H3). */
1209
1259
  toc?: TocConfig;
1260
+ /** Docs versioning (opt-in frozen snapshots with a version switcher). */
1261
+ versions?: VersionsConfig;
1210
1262
  }
1211
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>>;
@@ -318,6 +333,52 @@ declare const i18nConfigSchema: z.ZodObject<{
318
333
  }>>;
319
334
  ui: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>>>;
320
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>;
321
382
  /**
322
383
  * Content signals accept a boolean shorthand or a per-signal object, and
323
384
  * normalize to `{ search, aiInput, aiTrain }` — or `null` when disabled, so
@@ -384,7 +445,8 @@ declare const dateFormatConfigSchema: z.ZodObject<{
384
445
  }, z.core.$strict>;
385
446
  /**
386
447
  * A single spec rendered by the API reference. `spec` is a local path or an
387
- * `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`).
388
450
  */
389
451
  declare const openapiSourceSchema: z.ZodObject<{
390
452
  includeInLlms: z.ZodDefault<z.ZodBoolean>;
@@ -456,7 +518,13 @@ export declare const blumeConfigSchema: z.ZodObject<{
456
518
  vercel: z.ZodOptional<z.ZodBoolean>;
457
519
  }, z.core.$strict>>;
458
520
  asyncapi: z.ZodPrefault<z.ZodObject<{
521
+ codeSamples: z.ZodDefault<z.ZodArray<z.ZodString>>;
459
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
+ }>>;
460
528
  route: z.ZodDefault<z.ZodString>;
461
529
  scalar: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
462
530
  sources: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -1022,13 +1090,38 @@ export declare const blumeConfigSchema: z.ZodObject<{
1022
1090
  maxHeadingLevel: z.ZodOptional<z.ZodNumber>;
1023
1091
  minHeadingLevel: z.ZodOptional<z.ZodNumber>;
1024
1092
  }, z.core.$strict>]>>, z.ZodTransform<{
1025
- enabled: boolean;
1093
+ enabled: (boolean | {
1094
+ maxHeadingLevel?: number | undefined;
1095
+ minHeadingLevel?: number | undefined;
1096
+ }) & boolean;
1026
1097
  maxLevel: number;
1027
1098
  minLevel: number;
1028
1099
  }, boolean | {
1029
1100
  maxHeadingLevel?: number | undefined;
1030
1101
  minHeadingLevel?: number | undefined;
1031
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>>;
1032
1125
  }, z.core.$strict>;
1033
1126
  /** Resolved config: every field present after defaults are applied. */
1034
1127
  export type ResolvedConfig = z.infer<typeof blumeConfigSchema>;
@@ -1040,6 +1133,10 @@ export type FrontmatterExtend = Record<string, StandardSchema>;
1040
1133
  export type ResolvedI18nConfig = z.infer<typeof i18nConfigSchema>;
1041
1134
  /** A configured locale with display metadata. */
1042
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>;
1043
1140
  /**
1044
1141
  * User-authored config, straight off the schema. The public, hand-documented
1045
1142
  * authoring type is `BlumeConfig` in `./config-input.ts`, which a compile-time
@@ -1,4 +1,4 @@
1
- import type { FolderMeta, 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
@@ -127,4 +127,6 @@ export interface NormalizeContext {
127
127
  * applied to a page only when its resolved `type` matches.
128
128
  */
129
129
  typeFrontmatter?: Record<string, FrontmatterExtend>;
130
+ /** Docs versioning config; a leading archived-version dir becomes the page's version. */
131
+ versions?: ResolvedVersionsConfig;
130
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;
@@ -114,9 +114,24 @@ export interface PageRecord {
114
114
  locale: string;
115
115
  /**
116
116
  * Locale-agnostic logical route shared by every translation (e.g.
117
- * `/guides/x`). Pages with the same key are translations of each other.
117
+ * `/guides/x`, or `/v1.0/guides/x` under versioning the key is
118
+ * version-specific, so translations group within their version).
119
+ * Pages with the same key are translations of each other.
118
120
  */
119
121
  translationKey: string;
122
+ /**
123
+ * Resolved docs version: an archived id (`v1.0`) for snapshot pages, `""`
124
+ * for the current (unprefixed) docs — including every page of an
125
+ * unversioned project.
126
+ */
127
+ version: string;
128
+ /**
129
+ * Version- and locale-agnostic logical route (e.g. `/guides/x` for
130
+ * `/v1.0/guides/x`). Pages with the same key and locale are the same
131
+ * logical page across versions — this drives the switcher's same-page
132
+ * navigation and the canonical-to-latest lookup.
133
+ */
134
+ versionKey: string;
120
135
  /**
121
136
  * True for entries filled in from the fallback locale to pad a locale's
122
137
  * navigation for pages it hasn't translated yet. The record's content —
@@ -170,7 +185,12 @@ export type NavNode = {
170
185
  label: string;
171
186
  badge?: string;
172
187
  directory?: DirectoryMode;
173
- display?: SidebarDisplay;
188
+ /**
189
+ * The group's resolved render mode. Always stamped by both builders
190
+ * (generated and explicit-config sidebars), so the renderer can rely
191
+ * on it per node.
192
+ */
193
+ display: SidebarDisplay;
174
194
  icon?: string;
175
195
  route?: string;
176
196
  /**
@@ -249,6 +269,12 @@ export interface ContentGraph {
249
269
  navigation: Navigation;
250
270
  /** Navigation per locale; one entry per configured locale under i18n. */
251
271
  navigationByLocale: Record<string, Navigation>;
272
+ /**
273
+ * Navigation per archived version, keyed by version id and then locale code
274
+ * (`""` on a single-locale site). The current version's trees are
275
+ * `navigation`/`navigationByLocale`; empty when versioning is off.
276
+ */
277
+ navigationByVersion: Record<string, Record<string, Navigation>>;
252
278
  /** Map of route -> pageId for fast lookup and duplicate detection. */
253
279
  routes: Map<string, string>;
254
280
  diagnostics: Diagnostic[];
@@ -269,6 +295,12 @@ export interface LocaleSwitchOption {
269
295
  /** True when this locale has no real translation (renders fallback content). */
270
296
  untranslated: boolean;
271
297
  }
298
+ /** A docs version a logical page exists in, for the switcher and canonicals. */
299
+ export interface VersionAlternate {
300
+ /** Version id; `""` for the current (unprefixed) docs. */
301
+ version: string;
302
+ path: string;
303
+ }
272
304
  /** A route entry written to `blume.manifest.json`. */
273
305
  export interface RouteManifestEntry {
274
306
  id: string;
@@ -296,6 +328,15 @@ export interface RouteManifestEntry {
296
328
  locale: string;
297
329
  /** Locales this logical page is genuinely translated into (excludes fallbacks). */
298
330
  alternates: RouteAlternate[];
331
+ /** Resolved docs version (`""` for the current docs; see `PageRecord.version`). */
332
+ version: string;
333
+ /**
334
+ * Versions this logical page exists in within this route's locale — the
335
+ * current version first, then archived versions in configured order. Drives
336
+ * the switcher's same-page navigation and the canonical-to-latest lookup.
337
+ * Empty when versioning is off.
338
+ */
339
+ versionAlternates: VersionAlternate[];
299
340
  /** True when this route renders fallback content for a missing translation. */
300
341
  fallback?: boolean;
301
342
  /** Resolved "last updated" ISO date, when the feature is enabled. */
@@ -10,4 +10,12 @@ import type { UIStringsOverride } from "../i18n-ui.ts";
10
10
  * `zh-TW`). A locale's pack is merged onto English by {@link resolveUIStrings},
11
11
  * so a pack only needs the keys it translates.
12
12
  */
13
- export declare const UI_PACKS: Record<string, UIStringsOverride>;
13
+ /**
14
+ * Starter packs keyed by locale code. Open-keyed on purpose: resolvers look
15
+ * packs up by whatever locale a project configures, not only the codes here.
16
+ */
17
+ interface UiPackRegistry {
18
+ [locale: string]: UIStringsOverride;
19
+ }
20
+ export declare const UI_PACKS: UiPackRegistry;
21
+ export {};
@@ -44,9 +44,10 @@ export interface ReferenceSource {
44
44
  theme?: string;
45
45
  /**
46
46
  * Arbitrary Scalar config forwarded to `<ScalarComponent>` (Scalar renderer
47
- * only). Takes precedence over Blume's derived spec/theme config.
47
+ * only). Takes precedence over Blume's derived spec/theme config. Typed off
48
+ * the config schema so the two can never drift.
48
49
  */
49
- scalar?: Record<string, unknown>;
50
+ scalar?: ResolvedConfig["openapi"]["scalar"];
50
51
  /** Display options carried through to the Blume renderer. */
51
52
  display: ReferenceDisplay;
52
53
  /**
@@ -64,8 +65,8 @@ export interface ReferenceSource {
64
65
  */
65
66
  export declare const slugify: (text: string) => string;
66
67
  /**
67
- * Resolve every enabled reference. OpenAPI honors its `renderer` (Blume's own UI
68
- * by default); AsyncAPI is always rendered by Scalar for now.
68
+ * Resolve every enabled reference. Both blocks honor their `renderer`
69
+ * Blume's own UI by default, with the embedded Scalar SPA as the opt-out.
69
70
  */
70
71
  export declare const resolveReferences: (config: ResolvedConfig) => ReferenceSource[];
71
72
  /**
@@ -76,7 +77,7 @@ export declare const resolveReferences: (config: ResolvedConfig) => ReferenceSou
76
77
  * targets so such a tab doesn't read as a broken link.
77
78
  */
78
79
  export declare const referenceRoutes: (config: ResolvedConfig) => string[];
79
- /** Blume-rendered OpenAPI references, deduped by route (first wins). */
80
+ /** Blume-rendered references (both kinds), deduped by route (first wins). */
80
81
  export declare const blumeReferences: (config: ResolvedConfig) => ReferenceSource[];
81
82
  /** Whether any reference is Scalar-rendered (gates the `@scalar/astro` dep + pages). */
82
83
  export declare const hasScalarReferences: (config: ResolvedConfig) => boolean;