blume 2.0.3 → 2.1.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 (455) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
  12. package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
  13. package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
  14. package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
  15. package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
  16. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  17. package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
  18. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
  19. package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
  20. package/dist/cli/{chunk-2z928egk.js → chunk-5wj2qwn2.js} +5 -5
  21. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  22. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  23. package/dist/cli/{chunk-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
  24. package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
  25. package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
  26. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
  27. package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
  28. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  29. package/dist/cli/{chunk-b07cmahc.js → chunk-amrb6hgh.js} +2 -2
  30. package/dist/cli/chunk-cm89scdv.js +443 -0
  31. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  32. package/dist/cli/{chunk-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
  33. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  34. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  35. package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
  36. package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
  37. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  38. package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
  39. package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
  40. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  41. package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
  42. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
  43. package/dist/cli/chunk-kwrg63tm.js +31 -0
  44. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  45. package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
  46. package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
  47. package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
  48. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
  49. package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
  50. package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
  51. package/dist/cli/{chunk-ey84smr6.js.map → chunk-py62fewh.js.map} +1 -1
  52. package/dist/cli/chunk-q3wxxq39.js +126 -0
  53. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  54. package/dist/cli/{chunk-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
  55. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  56. package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
  57. package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
  58. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  59. package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
  60. package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
  61. package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
  62. package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
  63. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
  64. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  65. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  66. package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
  67. package/dist/cli/chunk-zf748avv.js.map +37 -0
  68. package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
  69. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
  70. package/dist/cli/index.js +26 -20
  71. package/dist/cli/index.js.map +5 -5
  72. package/dist/types/ai/agent-surface.d.ts +8 -7
  73. package/dist/types/ai/ai-catalog.d.ts +7 -0
  74. package/dist/types/ai/ask-context.d.ts +22 -0
  75. package/dist/types/ai/ask.d.ts +271 -58
  76. package/dist/types/ai/index.d.ts +2 -2
  77. package/dist/types/ai/site-skill.d.ts +26 -0
  78. package/dist/types/ai/skills.d.ts +16 -0
  79. package/dist/types/astro/integration.d.ts +8 -0
  80. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  81. package/dist/types/captcha/index.d.ts +28 -0
  82. package/dist/types/captcha/schema.d.ts +29 -0
  83. package/dist/types/captcha/turnstile.d.ts +28 -0
  84. package/dist/types/components/content/api-field.d.ts +59 -0
  85. package/dist/types/components/content/api-page.d.ts +95 -0
  86. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  87. package/dist/types/components/openapi/helpers.d.ts +137 -0
  88. package/dist/types/components/openapi/security.d.ts +85 -0
  89. package/dist/types/consent/ethyca.d.ts +48 -0
  90. package/dist/types/consent/index.d.ts +37 -0
  91. package/dist/types/consent/native.d.ts +28 -0
  92. package/dist/types/consent/osano.d.ts +42 -0
  93. package/dist/types/consent/schema.d.ts +110 -0
  94. package/dist/types/core/config-input.d.ts +187 -20
  95. package/dist/types/core/data.d.ts +49 -4
  96. package/dist/types/core/footer.d.ts +8 -0
  97. package/dist/types/core/i18n-ui.d.ts +86 -0
  98. package/dist/types/core/includes.d.ts +8 -2
  99. package/dist/types/core/localizable.d.ts +7 -0
  100. package/dist/types/core/page-modes.d.ts +29 -0
  101. package/dist/types/core/redirect-patterns.d.ts +144 -0
  102. package/dist/types/core/related.d.ts +40 -0
  103. package/dist/types/core/schema.d.ts +551 -56
  104. package/dist/types/core/sources/read.d.ts +5 -0
  105. package/dist/types/core/types.d.ts +1 -1
  106. package/dist/types/core/variables.d.ts +42 -0
  107. package/dist/types/deploy/node-headers.d.ts +13 -2
  108. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  109. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  110. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  111. package/dist/types/deploy/redirects.d.ts +39 -11
  112. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  113. package/dist/types/markdown/code-title.d.ts +17 -0
  114. package/dist/types/markdown/features.d.ts +3 -2
  115. package/dist/types/narration/build.d.ts +70 -0
  116. package/dist/types/narration/provider.d.ts +26 -0
  117. package/dist/types/narration/script.d.ts +154 -0
  118. package/dist/types/openapi/model.d.ts +12 -6
  119. package/dist/types/openapi/overlay.d.ts +54 -0
  120. package/dist/types/openapi/parse.d.ts +13 -0
  121. package/dist/types/openapi/references.d.ts +14 -4
  122. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  123. package/dist/types/ratelimit/index.d.ts +27 -0
  124. package/dist/types/ratelimit/memory.d.ts +36 -0
  125. package/dist/types/ratelimit/schema.d.ts +111 -0
  126. package/dist/types/ratelimit/upstash.d.ts +21 -0
  127. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  128. package/dist/types/reference/asyncapi.d.ts +10 -10
  129. package/dist/types/reference/graphql.d.ts +13 -10
  130. package/dist/types/reference/openapi.d.ts +33 -12
  131. package/dist/types/reference/options.d.ts +45 -5
  132. package/dist/types/reference/scalar.d.ts +13 -0
  133. package/dist/types/reference/schema.d.ts +60 -36
  134. package/dist/types/search/documents.d.ts +12 -1
  135. package/dist/types/search/orama-index.d.ts +4 -0
  136. package/dist/types/seo/metatags.d.ts +20 -0
  137. package/docs/02-deployment.mdx +41 -5
  138. package/docs/03-upgrading.mdx +4 -4
  139. package/docs/04-migrating.mdx +1 -1
  140. package/docs/advanced/changelog.mdx +2 -2
  141. package/docs/advanced/skills.mdx +14 -0
  142. package/docs/cli/audit.mdx +21 -1
  143. package/docs/cli/index.mdx +1 -0
  144. package/docs/configuration/analytics.mdx +3 -1
  145. package/docs/configuration/assistant.mdx +188 -36
  146. package/docs/configuration/consent.mdx +100 -0
  147. package/docs/configuration/customization.mdx +4 -4
  148. package/docs/configuration/index.mdx +63 -5
  149. package/docs/configuration/meta.ts +3 -0
  150. package/docs/configuration/narration.mdx +119 -0
  151. package/docs/configuration/rate-limiting.mdx +69 -0
  152. package/docs/configuration/search.mdx +29 -1
  153. package/docs/configuration/theming.mdx +3 -1
  154. package/docs/content/components.mdx +106 -0
  155. package/docs/content/frontmatter.mdx +76 -0
  156. package/docs/content/i18n.mdx +38 -3
  157. package/docs/content/includes.mdx +16 -0
  158. package/docs/content/meta.mdx +1 -0
  159. package/docs/content/meta.ts +1 -0
  160. package/docs/content/navigation.mdx +28 -6
  161. package/docs/content/sources/contentful.mdx +32 -0
  162. package/docs/content/sources/custom.mdx +39 -0
  163. package/docs/content/sources/github-releases.mdx +31 -0
  164. package/docs/content/sources/index.mdx +91 -0
  165. package/docs/content/sources/meta.ts +16 -0
  166. package/docs/content/sources/notion.mdx +32 -0
  167. package/docs/content/sources/obsidian.mdx +37 -0
  168. package/docs/content/sources/payload.mdx +30 -0
  169. package/docs/content/sources/remote-mdx.mdx +40 -0
  170. package/docs/content/sources/sanity.mdx +29 -0
  171. package/docs/content/sources/strapi.mdx +31 -0
  172. package/docs/content/syntax.mdx +67 -2
  173. package/docs/content/variables.mdx +41 -0
  174. package/docs/discoverability/agent-discovery.mdx +29 -2
  175. package/docs/discoverability/index.mdx +1 -1
  176. package/docs/discoverability/llms-txt.mdx +1 -1
  177. package/docs/discoverability/metadata.mdx +17 -0
  178. package/docs/references/api-pages.mdx +74 -0
  179. package/docs/references/asyncapi.mdx +1 -1
  180. package/docs/references/graphql.mdx +2 -2
  181. package/docs/references/meta.ts +1 -1
  182. package/docs/references/openapi.mdx +102 -3
  183. package/docs/references/scalar.mdx +3 -1
  184. package/package.json +36 -1
  185. package/skills/blume/SKILL.md +3 -0
  186. package/skills/blume-migrate/SKILL.md +5 -5
  187. package/skills/blume-migrate/references/docusaurus.md +3 -3
  188. package/skills/blume-migrate/references/mintlify.md +37 -19
  189. package/skills/blume-migrate/references/nextra.md +2 -2
  190. package/skills/blume-migrate/references/starlight.md +2 -1
  191. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  192. package/skills/blume-write-skill/SKILL.md +82 -0
  193. package/src/ai/agent-readability.ts +2 -1
  194. package/src/ai/agent-surface.ts +9 -7
  195. package/src/ai/ai-catalog.ts +6 -3
  196. package/src/ai/ask-context.ts +73 -20
  197. package/src/ai/ask-data.ts +6 -0
  198. package/src/ai/ask-tools.ts +142 -0
  199. package/src/ai/ask.ts +354 -76
  200. package/src/ai/component-markdown.ts +75 -0
  201. package/src/ai/cors.ts +2 -0
  202. package/src/ai/index.ts +12 -0
  203. package/src/ai/llms.ts +6 -2
  204. package/src/ai/markdown.ts +2 -1
  205. package/src/ai/mcp/data.ts +6 -0
  206. package/src/ai/openapi-components.ts +9 -3
  207. package/src/ai/site-skill.ts +274 -0
  208. package/src/ai/skills.ts +35 -2
  209. package/src/ai/write-skill.ts +97 -0
  210. package/src/astro/generate.ts +132 -18
  211. package/src/astro/index.ts +1 -0
  212. package/src/astro/integration.ts +35 -0
  213. package/src/astro/module-types.ts +6 -0
  214. package/src/astro/templates.ts +283 -30
  215. package/src/astro/variables.ts +31 -0
  216. package/src/audit/catalog.ts +19 -0
  217. package/src/audit/checks/contrast.ts +183 -0
  218. package/src/audit/checks/i18n.ts +2 -3
  219. package/src/audit/checks/indexability.ts +2 -3
  220. package/src/audit/checks/links.ts +5 -12
  221. package/src/audit/checks/sitemap.ts +2 -3
  222. package/src/audit/redirects.ts +46 -15
  223. package/src/audit/run.ts +2 -0
  224. package/src/audit/types.ts +2 -1
  225. package/src/blume-modules.d.ts +7 -0
  226. package/src/captcha/client.ts +152 -0
  227. package/src/captcha/hcaptcha.ts +36 -0
  228. package/src/captcha/index.ts +28 -0
  229. package/src/captcha/schema.ts +32 -0
  230. package/src/captcha/turnstile.ts +38 -0
  231. package/src/captcha/verify.ts +71 -0
  232. package/src/cli/command-meta.ts +5 -0
  233. package/src/cli/commands/doctor.ts +22 -16
  234. package/src/cli/commands/skill.ts +96 -0
  235. package/src/cli/index.ts +5 -0
  236. package/src/cli/required-secrets.ts +71 -29
  237. package/src/components/BlumePage.astro +2 -0
  238. package/src/components/content/ApiEndpoint.astro +18 -0
  239. package/src/components/content/ApiField.astro +79 -0
  240. package/src/components/content/ApiPlayground.astro +62 -0
  241. package/src/components/content/ApiRail.astro +19 -0
  242. package/src/components/content/Color.astro +1 -0
  243. package/src/components/content/FileTree.astro +1 -0
  244. package/src/components/content/GithubInfo.astro +1 -0
  245. package/src/components/content/Panel.astro +1 -0
  246. package/src/components/content/ParamField.astro +25 -0
  247. package/src/components/content/RequestExample.astro +17 -0
  248. package/src/components/content/ResponseExample.astro +17 -0
  249. package/src/components/content/ResponseField.astro +32 -0
  250. package/src/components/content/Step.astro +1 -0
  251. package/src/components/content/Tile.astro +1 -0
  252. package/src/components/content/Tooltip.astro +1 -0
  253. package/src/components/content/Tree.astro +1 -0
  254. package/src/components/content/TypeTable.astro +4 -1
  255. package/src/components/content/View.astro +21 -0
  256. package/src/components/content/ViewSwitcher.astro +168 -0
  257. package/src/components/content/api-field.ts +100 -0
  258. package/src/components/content/api-page.ts +224 -0
  259. package/src/components/content/view-switcher.ts +71 -0
  260. package/src/components/index.ts +2 -0
  261. package/src/components/islands/Assistant.astro +10 -1
  262. package/src/components/islands/assistant.tsx +104 -14
  263. package/src/components/islands/code-question.ts +62 -0
  264. package/src/components/islands/hooks.ts +110 -21
  265. package/src/components/islands/support-link.ts +68 -0
  266. package/src/components/layout/Analytics.astro +11 -2
  267. package/src/components/layout/Banner.astro +14 -10
  268. package/src/components/layout/Breadcrumbs.astro +6 -2
  269. package/src/components/layout/ConsentBanner.astro +62 -0
  270. package/src/components/layout/ConsentHead.astro +38 -0
  271. package/src/components/layout/Directory.astro +94 -0
  272. package/src/components/layout/DrawerSwitchers.astro +51 -0
  273. package/src/components/layout/Empty.astro +2 -2
  274. package/src/components/layout/Header.astro +122 -80
  275. package/src/components/layout/LanguageSwitcher.astro +48 -15
  276. package/src/components/layout/NarrationPlayer.astro +1085 -0
  277. package/src/components/layout/NavSelector.astro +33 -7
  278. package/src/components/layout/PageFeedback.astro +54 -49
  279. package/src/components/layout/PageLayout.astro +71 -3
  280. package/src/components/layout/ReferenceLayout.astro +17 -6
  281. package/src/components/layout/RelatedPages.astro +42 -0
  282. package/src/components/layout/RootLayout.astro +308 -45
  283. package/src/components/layout/Search.astro +31 -17
  284. package/src/components/layout/SiteFooter.astro +124 -0
  285. package/src/components/layout/SiteMetatags.astro +16 -0
  286. package/src/components/layout/consent/native.ts +79 -0
  287. package/src/components/layout/consent/types.ts +37 -0
  288. package/src/components/layout/footer-links.ts +53 -0
  289. package/src/components/layout/footer-socials.ts +87 -0
  290. package/src/components/layout/locale-redirect.ts +107 -0
  291. package/src/components/layout/nav-utils.ts +83 -0
  292. package/src/components/layout/page-feedback.ts +96 -0
  293. package/src/components/layout/search/analytics.ts +98 -0
  294. package/src/components/layout/search/flexsearch.ts +17 -5
  295. package/src/components/layout/search/orama-cloud.ts +26 -14
  296. package/src/components/layout/search/types.ts +20 -0
  297. package/src/components/layout/search/typesense.ts +5 -1
  298. package/src/components/openapi/Operation.astro +161 -13
  299. package/src/components/openapi/RequestBody.astro +6 -4
  300. package/src/components/openapi/RequestPanel.astro +59 -7
  301. package/src/components/openapi/Responses.astro +6 -4
  302. package/src/components/openapi/async-snippets.ts +4 -4
  303. package/src/components/openapi/code-samples.ts +97 -0
  304. package/src/components/openapi/security.ts +6 -3
  305. package/src/components/openapi/snippets.ts +869 -8
  306. package/src/components/props.ts +6 -0
  307. package/src/consent/client.ts +95 -0
  308. package/src/consent/clients.ts +19 -0
  309. package/src/consent/ethyca.ts +71 -0
  310. package/src/consent/head.ts +24 -0
  311. package/src/consent/index.ts +37 -0
  312. package/src/consent/init.ts +29 -0
  313. package/src/consent/native.ts +36 -0
  314. package/src/consent/osano.ts +59 -0
  315. package/src/consent/schema.ts +30 -0
  316. package/src/core/builtin-tags.ts +5 -0
  317. package/src/core/client-address.ts +19 -0
  318. package/src/core/config-input.ts +194 -20
  319. package/src/core/data.ts +44 -4
  320. package/src/core/diagnostics.ts +3 -2
  321. package/src/core/footer.ts +26 -0
  322. package/src/core/graph.ts +39 -36
  323. package/src/core/i18n-ui.ts +82 -2
  324. package/src/core/includes.ts +20 -3
  325. package/src/core/links.ts +16 -2
  326. package/src/core/localizable.ts +26 -0
  327. package/src/core/navigation.ts +43 -12
  328. package/src/core/page-modes.ts +56 -0
  329. package/src/core/project-graph.ts +111 -5
  330. package/src/core/redirect-patterns.ts +491 -0
  331. package/src/core/related.ts +131 -0
  332. package/src/core/schema.ts +318 -64
  333. package/src/core/sources/normalize.ts +2 -1
  334. package/src/core/sources/read.ts +8 -3
  335. package/src/core/types.ts +1 -1
  336. package/src/core/ui-packs/ar.ts +43 -0
  337. package/src/core/ui-packs/bg.ts +45 -0
  338. package/src/core/ui-packs/bn.ts +43 -0
  339. package/src/core/ui-packs/ca.ts +46 -0
  340. package/src/core/ui-packs/cs.ts +44 -0
  341. package/src/core/ui-packs/da.ts +45 -0
  342. package/src/core/ui-packs/de.ts +46 -0
  343. package/src/core/ui-packs/el.ts +45 -0
  344. package/src/core/ui-packs/es.ts +46 -0
  345. package/src/core/ui-packs/fa.ts +45 -0
  346. package/src/core/ui-packs/fi.ts +45 -0
  347. package/src/core/ui-packs/fr.ts +46 -0
  348. package/src/core/ui-packs/he.ts +43 -0
  349. package/src/core/ui-packs/hi.ts +44 -0
  350. package/src/core/ui-packs/hr.ts +45 -0
  351. package/src/core/ui-packs/hu.ts +44 -0
  352. package/src/core/ui-packs/id.ts +45 -0
  353. package/src/core/ui-packs/it.ts +45 -0
  354. package/src/core/ui-packs/ja.ts +45 -0
  355. package/src/core/ui-packs/ko.ts +44 -0
  356. package/src/core/ui-packs/nl.ts +46 -0
  357. package/src/core/ui-packs/no.ts +44 -0
  358. package/src/core/ui-packs/pl.ts +45 -0
  359. package/src/core/ui-packs/pt-br.ts +46 -0
  360. package/src/core/ui-packs/pt.ts +45 -0
  361. package/src/core/ui-packs/ro.ts +45 -0
  362. package/src/core/ui-packs/ru.ts +45 -0
  363. package/src/core/ui-packs/sk.ts +44 -0
  364. package/src/core/ui-packs/sr.ts +45 -0
  365. package/src/core/ui-packs/sv.ts +44 -0
  366. package/src/core/ui-packs/th.ts +43 -0
  367. package/src/core/ui-packs/tr.ts +45 -0
  368. package/src/core/ui-packs/uk.ts +45 -0
  369. package/src/core/ui-packs/vi.ts +45 -0
  370. package/src/core/ui-packs/zh-tw.ts +43 -0
  371. package/src/core/ui-packs/zh.ts +43 -0
  372. package/src/core/variables.ts +103 -0
  373. package/src/deploy/artifacts.ts +77 -8
  374. package/src/deploy/cloudflare-negotiation.ts +35 -5
  375. package/src/deploy/headers.ts +2 -1
  376. package/src/deploy/node-headers.ts +63 -9
  377. package/src/deploy/platforms/cloudflare.ts +32 -0
  378. package/src/deploy/platforms/netlify.ts +33 -1
  379. package/src/deploy/platforms/vercel.ts +37 -2
  380. package/src/deploy/redirects.ts +91 -24
  381. package/src/deploy/vercel-negotiation.ts +36 -0
  382. package/src/markdown/api-rail.ts +259 -0
  383. package/src/markdown/code-title.ts +35 -9
  384. package/src/markdown/features.ts +3 -2
  385. package/src/markdown/fence-meta.ts +2 -0
  386. package/src/markdown/include.ts +11 -1
  387. package/src/markdown/index.ts +28 -5
  388. package/src/markdown/math.ts +4 -4
  389. package/src/markdown/variables.ts +56 -0
  390. package/src/markdown/views.ts +82 -0
  391. package/src/narration/build.ts +412 -0
  392. package/src/narration/player.ts +249 -0
  393. package/src/narration/provider.ts +29 -0
  394. package/src/narration/script.ts +592 -0
  395. package/src/og/card.ts +7 -6
  396. package/src/openapi/model.ts +57 -24
  397. package/src/openapi/overlay.ts +256 -0
  398. package/src/openapi/parse.ts +55 -2
  399. package/src/openapi/references.ts +21 -5
  400. package/src/openapi/render-mdx.ts +5 -1
  401. package/src/openapi/scalar.ts +40 -6
  402. package/src/openapi/source.ts +1 -1
  403. package/src/ratelimit/cloudflare.ts +54 -0
  404. package/src/ratelimit/index.ts +30 -0
  405. package/src/ratelimit/memory.ts +45 -0
  406. package/src/ratelimit/runtime.ts +229 -0
  407. package/src/ratelimit/schema.ts +28 -0
  408. package/src/ratelimit/upstash.ts +30 -0
  409. package/src/ratelimit/wrangler.ts +70 -0
  410. package/src/reference/asyncapi.ts +2 -2
  411. package/src/reference/graphql.ts +5 -2
  412. package/src/reference/openapi.ts +21 -7
  413. package/src/reference/options.ts +51 -7
  414. package/src/reference/scalar.ts +33 -8
  415. package/src/registry/eject.ts +20 -4
  416. package/src/registry/registry.ts +7 -0
  417. package/src/search/documents.ts +26 -2
  418. package/src/search/orama-index.ts +28 -3
  419. package/src/search/pagefind-ranking.ts +39 -0
  420. package/src/search/sync/algolia.ts +15 -4
  421. package/src/search/sync/orama-cloud.ts +3 -0
  422. package/src/search/sync/typesense.ts +5 -0
  423. package/src/seo/metatags.ts +67 -0
  424. package/src/theme/chrome-icons.ts +1 -0
  425. package/src/theme/color.ts +500 -0
  426. package/src/theme/entry.ts +56 -3
  427. package/src/theme/palette.ts +121 -45
  428. package/src/translate/prompts.ts +1 -0
  429. package/src/translate/validate.ts +8 -2
  430. package/src/upgrade/upgrade.ts +4 -171
  431. package/dist/cli/chunk-364znk6q.js +0 -75
  432. package/dist/cli/chunk-364znk6q.js.map +0 -11
  433. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  434. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  435. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  436. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  437. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  438. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  439. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  440. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  441. package/docs/content/sources.mdx +0 -334
  442. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
  443. /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
  444. /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
  445. /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
  446. /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
  447. /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
  448. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
  449. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
  450. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
  451. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
  452. /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
  453. /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
  454. /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
  455. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/search/documents.ts", "../src/markdown/fence-meta.ts", "../src/markdown/code-title.ts", "../src/search/facets.ts", "../src/ai/mcp/data.ts"],
4
+ "sourcesContent": [
5
+ "import type { Nodes } from \"mdast\";\nimport { markdownToMdast, mdxToMdast } from \"satteri\";\n\nimport {\n componentRegistry,\n downlevelComponentNode,\n downlevelComponents,\n} from \"../ai/component-markdown.ts\";\nimport type {\n ComponentMarkdown,\n DownlevelWalk,\n MdastNode as DownlevelNode,\n} from \"../ai/component-markdown.ts\";\nimport { projectComponentSerializers } from \"../ai/serializers.ts\";\nimport { applyAudienceVisibility } from \"../ai/visibility.ts\";\nimport type { VisibilityAudience } from \"../ai/visibility.ts\";\nimport matter from \"../core/frontmatter.ts\";\nimport { parseHeadingMarkers } from \"../core/heading-markers.ts\";\nimport { EN_UI, resolveUIStrings } from \"../core/i18n-ui.ts\";\nimport { contentIndexable } from \"../core/manifest.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport { HTML_COMMENT } from \"../core/sources/normalize.ts\";\nimport { readExpandedEntryText } from \"../core/sources/read.ts\";\nimport type { NavNode, PageRecord } from \"../core/types.ts\";\nimport { parseCodeTitle } from \"../markdown/code-title.ts\";\nimport { directiveSource } from \"../markdown/directives.ts\";\nimport type { DirectiveNode } from \"../markdown/directives.ts\";\nimport {\n MARKDOWN_BODY_FEATURES,\n MDX_BODY_FEATURES,\n} from \"../markdown/features.ts\";\nimport { pageFacets } from \"./facets.ts\";\n\n/** A document indexed by the client-side search providers (Orama, FlexSearch). */\nexport interface SearchDocument {\n route: string;\n title: string;\n description: string;\n content: string;\n /** Ancestor section labels for the result breadcrumb, e.g. `[\"Guides\"]`. */\n breadcrumb: string[];\n /** Top-level section label, used by the search filter pills. */\n section: string;\n /** Locale code, so the dialog can filter results to the active language. */\n locale: string;\n /** Resolved page `type` (`doc`, `blog`, a custom `rfc`…), for type filters. */\n contentType: string;\n /** Docs version (`\"\"` for the current docs), so results scope to the viewed version. */\n version: string;\n /** Frontmatter `search.tags`, surfaced for hosted-provider faceting. */\n tags?: string[];\n /**\n * Declared facet values (`content.types.<type>.facets`), key → value.\n * Filterable through the MCP tools' `filters` input.\n */\n facets?: Record<string, string>;\n /** Frontmatter `search.boost`: the page's relevance multiplier, when set. */\n boost?: number;\n /** Frontmatter `search.keywords`: extra terms the page is found by. */\n keywords?: string[];\n}\n\n/**\n * A record uploaded to a hosted search backend (Algolia, Orama Cloud,\n * Typesense, Mixedbread). `_id` is the stable per-page key; each sync adapts it\n * to the backend's own id field (`objectID`, `id`, …).\n */\nexport interface SearchRecord {\n _id: string;\n url: string;\n title: string;\n description: string;\n content: string;\n /** Locale code, carried as a facet for per-language filtering. */\n locale: string;\n /**\n * Docs version, carried as a facet for per-version filtering. The current\n * docs upload as `\"current\"` — hosted backends treat an empty facet value\n * unreliably, so the sentinel stands in for the empty version id.\n */\n version: string;\n /** Single faceting tag (the first frontmatter tag, when present). */\n tag?: string;\n /**\n * The page's relevance multiplier, 1 unless `search.boost` sets it: a\n * ranking attribute every record carries, so the backend can sort by it.\n */\n boost: number;\n /** Extra terms the page is found by (`search.keywords`). */\n keywords?: string[];\n}\n\n// Tag-shaped only: a name (or closing slash/fragment) right after `<`, and no\n// newline inside. Applied *within* html/JSX nodes so their inner prose is\n// kept; the surrounding Markdown is walked as a tree, so a bare `<` in prose\n// (\"costs < 5 credits\") is ordinary text and never at risk.\nconst HTML_OR_JSX = /<\\/?[a-zA-Z][^\\n<>]*>|<\\/?>/gu;\nconst WHITESPACE = /\\s+/gu;\n\n// Parents whose children are inline: no separator is inserted after them, or\n// `re*ally*` would index as `re ally`. Every other parent is block-shaped and\n// ends with a space so adjacent paragraphs/headings/cells don't fuse.\nconst INLINE_PARENTS = new Set([\n \"delete\",\n \"emphasis\",\n \"footnoteReference\",\n \"link\",\n \"linkReference\",\n \"mdxJsxTextElement\",\n \"strong\",\n \"subscript\",\n \"superscript\",\n]);\n\n// Nodes that never render as prose: image alt text was never indexed, MDX\n// expressions (`{props.x}`) and ESM (`export const meta`) are code, and math\n// is LaTeX source rendered by KaTeX, not searchable words.\nconst NON_PROSE = new Set<Nodes[\"type\"]>([\n \"image\",\n \"imageReference\",\n \"inlineMath\",\n \"math\",\n \"mdxFlowExpression\",\n \"mdxTextExpression\",\n \"mdxjsEsm\",\n]);\n\ntype PageFormat = PageRecord[\"format\"];\n\n/** What the plain-text walk carries: the code-block policy and the downlevel context. */\ninterface Walk {\n includeCodeBlocks: boolean;\n downlevel: DownlevelWalk;\n}\n\n// Front matter is already off (core/frontmatter.ts) by the time a body gets\n// here, so a body that opens with a `---` divider must read as a thematic\n// break rather than a second front matter block — as the renderer reads it.\nconst MD_PARSE = { features: MARKDOWN_BODY_FEATURES };\nconst MDX_PARSE = { features: MDX_BODY_FEATURES };\n\nconst parseMdx = (markdown: string): Nodes | undefined => {\n try {\n return mdxToMdast(markdown, MDX_PARSE);\n } catch {\n return undefined;\n }\n};\n\n/**\n * Parse a page body the way the renderer reads it. `.mdx` pages go through\n * the MDX grammar with the renderer's feature set, so components become\n * `mdxJsx*` nodes whose children — prose and fences, however they're indented\n * — walk like top-level content, and `:::` directives and `$$` math parse as\n * such; Markdown would instead fold a tight component into one html node and\n * read its indented children as an indented code block. A page the MDX\n * grammar rejects (an HTML comment, an unclosed tag) fails to render too, so\n * it has no body to index: reading it as Markdown instead would put its ESM,\n * expressions, and raw tags in the index as prose.\n */\nconst parseMarkdown = (\n markdown: string,\n format: PageFormat\n): Nodes | undefined =>\n format === \"mdx\" ? parseMdx(markdown) : markdownToMdast(markdown, MD_PARSE);\n\n// Fenced code is excluded from the plain index by default (ranking noise) —\n// the \"markdown\" extraction keeps it for assistant grounding. Code-heavy docs opt\n// in via `search.indexing.includeCodeBlocks`, which indexes the fence body and\n// its rendered title (```ts blume.config.ts) but never the fence markers,\n// language, or other meta keywords.\nconst collectCode = (\n node: Extract<Nodes, { type: \"code\" }>,\n out: string[],\n walk: Walk\n): void => {\n if (!walk.includeCodeBlocks) {\n return;\n }\n const title = parseCodeTitle(node.meta ?? undefined);\n if (title) {\n out.push(title, \" \");\n }\n // The trailing space keeps the fence apart from what follows it.\n out.push(node.value, \" \");\n};\n\n/**\n * A text or leaf directive as the page shows it. Blume renders neither (see\n * `markdown/directives.ts`), and prose is full of text that parses as one —\n * `16:9`, `og:image` — so the page shows the source the author wrote, and the\n * index reads the same text rather than dropping the `:name` part.\n */\nconst directiveText = (\n node: Extract<Nodes, { type: \"leafDirective\" | \"textDirective\" }>,\n walk: Walk\n): string =>\n directiveSource(\n // SAFETY: DirectiveNode is a structural subset of Satteri's directive\n // nodes — a `name`, optional attributes and children, and a position.\n node as DirectiveNode,\n node.type === \"textDirective\" ? \":\" : \"::\",\n // The walked tree was parsed from this text, so its offsets index into it.\n walk.downlevel.source\n );\n\n// Block boundaries separate words; so does an empty inline element (`<br />`,\n// a self-closing icon), which otherwise fuses its neighbors.\nconst separatesWords = (node: Extract<Nodes, { children: unknown }>): boolean =>\n !INLINE_PARENTS.has(node.type) || node.children.length === 0;\n\n/** Fold one mdast node into the plain-text accumulator. */\nconst collectText = (node: Nodes, out: string[], walk: Walk): void => {\n if (NON_PROSE.has(node.type)) {\n return;\n }\n switch (node.type) {\n case \"code\": {\n collectCode(node, out, walk);\n return;\n }\n // A component with a serializer indexes as the text the page shows —\n // a Card's title and call to action, a TypeTable's descriptions — read\n // from the Markdown the serializer emits (its contract is Markdown, not\n // MDX). One without a serializer walks its children like any block.\n case \"mdxJsxFlowElement\": {\n // SAFETY: DownlevelNode is a structural subset of Satteri's mdast\n // output — the walk checks for offsets before reading them.\n const text = downlevelComponentNode(\n node as DownlevelNode,\n walk.downlevel\n );\n if (text !== null) {\n collectText(markdownToMdast(text, MD_PARSE), out, walk);\n return;\n }\n break;\n }\n // Inline code is kept verbatim — `Array<T>` is a type parameter, not a\n // tag, and its tokens must stay searchable.\n case \"inlineCode\": {\n out.push(node.value);\n return;\n }\n // A raw-HTML run. In a `.md` page (or an `.mdx` page MDX couldn't parse)\n // CommonMark folds a block-level `<Callout>` with no blank lines into ONE\n // html node holding all its inner prose, so the node can't just be\n // dropped — strip the tag-shaped runs and keep the text.\n case \"html\": {\n out.push(\n node.value.replaceAll(HTML_COMMENT, \" \").replaceAll(HTML_OR_JSX, \" \"),\n \" \"\n );\n return;\n }\n case \"break\": {\n out.push(\" \");\n return;\n }\n case \"textDirective\": {\n out.push(directiveText(node, walk));\n return;\n }\n case \"leafDirective\": {\n out.push(directiveText(node, walk), \" \");\n return;\n }\n case \"heading\": {\n // oxlint-disable-next-line no-use-before-define -- mutual recursion: a heading's children are walked like any others\n collectHeading(node, out, walk);\n return;\n }\n default: {\n break;\n }\n }\n if (\"value\" in node) {\n out.push(node.value);\n return;\n }\n if (\"children\" in node) {\n for (const child of node.children) {\n collectText(child, out, walk);\n }\n if (separatesWords(node)) {\n out.push(\" \");\n }\n }\n};\n\n/**\n * Fold a heading into the accumulator. Trailing heading markers\n * (`[#custom-id]`, `{#custom-id}`, `[!toc]`, `[toc]`) are anchor metadata,\n * not prose — strip them so they never pollute the index. Only a marker that\n * ends the heading's final plain-text child counts, mirroring the renderer: a\n * heading ending in inline code or an image keeps its bracketed text on the\n * page (so it stays searchable), and a heading that is nothing but markers\n * renders them literally.\n */\nconst collectHeading = (\n node: Extract<Nodes, { type: \"heading\" }>,\n out: string[],\n walk: Walk\n): void => {\n const last = node.children.at(-1);\n const trailing = last?.type === \"text\" ? last : undefined;\n const inner: string[] = [];\n const kept = trailing ? node.children.slice(0, -1) : node.children;\n for (const child of kept) {\n collectText(child, inner, walk);\n }\n if (trailing) {\n const stripped = parseHeadingMarkers(trailing.value).text;\n const literal = stripped === \"\" && node.children.length === 1;\n inner.push(literal ? trailing.value : stripped);\n }\n out.push(inner.join(\"\").trimEnd(), \" \");\n};\n\n/**\n * Reduce Markdown/MDX to plain, searchable text: parse (GFM included) and walk\n * the tree instead of regex-stripping the source, so reference-style links,\n * autolinks, setext headings, tables, and literal `*`/`~`/`>` in prose all\n * reduce correctly. This feeds the client index *and* every hosted-provider\n * record, so anything lost here is a permanent search-quality loss.\n */\nconst toPlainText = (\n markdown: string,\n format: PageFormat,\n walk: Walk\n): string => {\n const tree = parseMarkdown(markdown, format);\n if (!tree) {\n return \"\";\n }\n const out: string[] = [];\n collectText(tree, out, walk);\n return out.join(\"\").replaceAll(WHITESPACE, \" \").trim();\n};\n\ninterface Crumbs {\n breadcrumb: string[];\n section: string;\n}\n\n/**\n * Map each page route to its ancestor section labels by walking the nav sidebar\n * once. The nearest ancestor group — the sidebar section a page appears under —\n * becomes its `section`, the dimension the search filter pills group by, so the\n * pills mirror the visible sidebar (and honor folder-meta renames).\n */\nconst buildCrumbIndex = (sidebar: NavNode[]): Map<string, Crumbs> => {\n const index = new Map<string, Crumbs>();\n // A config-sidebar section's landing page (the group's `root`) lives on the\n // *group* node, not on any page leaf — record it under the group's own label\n // so the section's landing page carries the same facet as its children. A\n // real page leaf for the route (filesystem sidebars emit index pages as\n // leaves) wins, so group routes are merged in only where no leaf claimed one.\n const groupRoutes = new Map<string, Crumbs>();\n const walk = (nodes: NavNode[], trail: string[]): void => {\n for (const node of nodes) {\n if (node.kind === \"group\") {\n if (node.route && !groupRoutes.has(node.route)) {\n groupRoutes.set(node.route, {\n breadcrumb: [...trail, node.label],\n section: node.label,\n });\n }\n walk(node.children, [...trail, node.label]);\n } else if (node.route) {\n index.set(node.route, {\n breadcrumb: trail,\n section: trail.at(-1) ?? \"\",\n });\n }\n }\n };\n walk(sidebar, []);\n for (const [route, crumbs] of groupRoutes) {\n if (!index.has(route)) {\n index.set(route, crumbs);\n }\n }\n return index;\n};\n\ninterface BuildOptions {\n includeWhenDisabled?: boolean;\n content?: \"markdown\" | \"plain\";\n audience?: VisibilityAudience;\n}\n\n/**\n * Read a page's body and reduce it per the extraction options. The\n * `\"markdown\"` body is agent-facing, so its components are downleveled in the\n * source with the serializers every agent surface uses (front matter in scope\n * for prop expressions). The plain body is parsed as written and walked, with\n * the same serializers applied per component on the way, so the index reads a\n * `<Card>` or `<TypeTable>` as the text it shows.\n */\nconst pageBody = async (\n project: BlumeProject,\n page: PageRecord | undefined,\n options: Pick<BuildOptions, \"audience\" | \"content\"> | undefined,\n components: Record<string, ComponentMarkdown>\n): Promise<string> => {\n if (!page) {\n return \"\";\n }\n const parsed = matter(await readExpandedEntryText(project, page));\n const visible = applyAudienceVisibility(\n parsed.content,\n options?.audience ?? \"web\"\n );\n if (options?.content === \"markdown\") {\n return downlevelComponents(visible.trim(), components, parsed.data);\n }\n return toPlainText(visible, page.format, {\n downlevel: {\n frontmatter: parsed.data,\n registry: componentRegistry(components),\n source: visible,\n },\n includeCodeBlocks: project.config.search.indexing.includeCodeBlocks,\n });\n};\n\n/** The ranking a page asks for: its `search.boost` and `search.keywords`. */\nconst rankingFields = (\n search: PageRecord[\"meta\"][\"search\"] | undefined\n): Pick<SearchDocument, \"boost\" | \"keywords\"> => {\n const { boost, keywords } = search ?? {};\n return {\n ...(boost !== undefined && boost !== 1 && { boost }),\n ...(keywords && keywords.length > 0 && { keywords }),\n };\n};\n\n/**\n * Build search documents from the content graph. Only indexable pages are\n * included (per the route manifest), and content comes from the source files,\n * so the index is identical in dev and build.\n *\n * Pass `includeWhenDisabled` to index pages on their content merits even when\n * the search provider is `none` — used by the MCP server, which is a separate\n * feature from on-page search.\n *\n * `content` selects the extraction: `\"plain\"` (default) strips Markdown to bare\n * searchable text; `\"markdown\"` keeps the body's Markdown — code blocks, lists,\n * headings — for assistant grounding, where fenced examples are often the answer\n * and stripping them makes the model unable to cite content the docs do contain.\n * The `\"markdown\"` body is agent-facing, so components are downleveled with the\n * same serializers the `.md` mirror, llms-full.txt and MCP `get_page` use: a\n * page whose body is a `<CardGroup>` of `<Card>`s is prose to a reader and bare\n * JSX to anything reading the source, and section landing pages are exactly\n * that shape.\n *\n * `audience` resolves `<Visibility>` blocks before extraction: `\"web\"`\n * (default) keeps web-only content and drops agents-only blocks — the site\n * search and hosted syncs must not surface content the page hides — while\n * `\"agents\"` mirrors llms-full.txt/MCP `get_page` (web removed, agents kept).\n *\n * Whether fenced code joins the plain extraction is site-wide policy, read\n * from `search.indexing.includeCodeBlocks` here rather than threaded by each\n * caller, so the client index, hosted syncs, eject, and the MCP `search_docs`\n * index can't drift apart.\n */\nexport const buildSearchDocuments = async (\n project: BlumeProject,\n options?: BuildOptions\n): Promise<SearchDocument[]> => {\n const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));\n\n // Build the crumb index from every locale's sidebar (their nodes carry\n // locale-prefixed routes), so localized pages get the right section/breadcrumb.\n // Falls back to the single default-locale nav when i18n is off.\n const byLocale = Object.values(project.graph.navigationByLocale ?? {});\n // Archived versions' trees contribute too, so snapshot pages get their own\n // section/breadcrumb instead of falling through to the \"Docs\" default.\n const byVersion = Object.values(project.graph.navigationByVersion ?? {})\n .flatMap((locales) => Object.values(locales))\n .map((nav) => nav.sidebar);\n const sidebars = [\n ...(byLocale.length > 0\n ? byLocale.map((nav) => nav.sidebar)\n : [project.graph.navigation?.sidebar ?? []]),\n ...byVersion,\n ];\n const crumbs = new Map<string, Crumbs>();\n for (const sidebar of sidebars) {\n for (const [route, crumb] of buildCrumbIndex(sidebar)) {\n crumbs.set(route, crumb);\n }\n }\n\n const indexable = project.manifest.routes.filter((route) => {\n if (!options?.includeWhenDisabled) {\n return route.indexable;\n }\n // A fallback route renders the fallback locale's page at another locale's\n // URL, so indexing it would only repeat that page as a second hit — the\n // reason site search leaves it out too.\n if (route.fallback) {\n return false;\n }\n const page = pageById.get(route.id);\n return page ? contentIndexable(page, project.config) : false;\n });\n\n // Built once — `downlevelComponents` rebuilds its registry per call\n // otherwise.\n const components = projectComponentSerializers(project);\n\n // A page in no sidebar group falls under the \"Docs\" section, in its own\n // language: the label is a UI string, resolved once per locale.\n const { i18n } = project.config;\n const docsSections = new Map<string, string>();\n const docsSection = (locale: string): string => {\n let label = docsSections.get(locale);\n if (label === undefined) {\n label = (\n i18n\n ? resolveUIStrings(locale, {\n defaultLocale: i18n.defaultLocale,\n overrides: i18n.ui,\n })\n : EN_UI\n ).search.docs;\n docsSections.set(locale, label);\n }\n return label;\n };\n\n return await Promise.all(\n indexable.map(async (route) => {\n const page = pageById.get(route.id);\n const body = await pageBody(project, page, options, components);\n const tags = page?.meta?.search?.tags;\n const crumb = crumbs.get(route.path);\n const facets = page ? pageFacets(page, project.config) : undefined;\n const document: SearchDocument = {\n breadcrumb: crumb?.breadcrumb ?? [],\n content: body,\n contentType: route.contentType,\n description: page?.description ?? \"\",\n locale: route.locale,\n route: route.path,\n section: crumb?.section || docsSection(route.locale),\n tags: tags && tags.length > 0 ? tags : undefined,\n title: route.title,\n version: route.version,\n };\n if (facets) {\n document.facets = facets;\n }\n return { ...document, ...rankingFields(page?.meta?.search) };\n })\n );\n};\n\n/**\n * Map per-page search documents to the flat record shape hosted backends\n * ingest. One record per page, keyed by route; the first tag becomes the\n * faceting `tag`, and every record carries a `boost` (1 by default).\n */\nexport const toSearchRecords = (documents: SearchDocument[]): SearchRecord[] =>\n documents.map((doc) => ({\n _id: doc.route,\n boost: doc.boost ?? 1,\n content: doc.content,\n description: doc.description,\n ...(doc.keywords && { keywords: doc.keywords }),\n locale: doc.locale,\n tag: doc.tags?.[0],\n title: doc.title,\n url: doc.route,\n version: doc.version || \"current\",\n }));\n",
6
+ "/**\n * The shared fence-meta grammar: how the tokens after a code fence's language\n * (```ts title=\"...\" {1,3-5} lineNumbers) split, and which of them carry\n * reserved meaning. Both the Shiki-side meta reader (`code-title.ts`) and the\n * MDAST plugins that rewrite marked fences (`ts2js.ts`) consume this, so the\n * two layers can't drift on token boundaries or keyword lists.\n */\n\n/**\n * Any quoted `key=\"...\"` attr (spaces allowed inside the quotes). The body\n * excludes only the delimiting quote, so `title=\"foo's file.ts\"` (an\n * apostrophe inside double quotes) still matches. The left boundary stops\n * `subtitle=\"...\"` (or any `*title=` attr) from reading as a title.\n */\nexport const QUOTED_ATTR = /[\\w-]+=(?:\"[^\"]*\"|'[^']*')/gu;\n\n/**\n * A Shiki `{1,3-5}` line-range token, braces included. Shiki tolerates\n * whitespace inside the braces (`{1, 3-5}`), so the range is one token even\n * when it contains spaces — split on whitespace alone, `3-5}` would surface\n * as a bare word and be promoted to the block title.\n */\nconst LINE_RANGE = /\\{[^}]*\\}/u;\n\n/**\n * One fence-meta token: a quoted attribute, a line range, or a bare word, so\n * a keyword inside a quoted value (`title=\"enable ts2js later\"`) never reads\n * as a bare token and a spaced line range never splits.\n */\nconst META_TOKEN = new RegExp(\n `${QUOTED_ATTR.source}|${LINE_RANGE.source}|\\\\S+`,\n \"gu\"\n);\n\n/** Split a raw fence meta string into its tokens. */\nexport const metaTokens = (meta: string | null | undefined): string[] =>\n meta?.match(META_TOKEN) ?? [];\n\n/**\n * Bare keywords with reserved meaning after the language; never promoted to\n * a block title.\n */\nexport const RESERVED_META_KEYWORDS: ReadonlySet<string> = new Set([\n \"expandable\",\n \"lineNumbers\",\n \"ts2js\",\n \"twoslash\",\n \"wrap\",\n]);\n\n/** A Shiki `{1,3-5}` line-range token. */\nexport const isLineRange = (token: string): boolean => token.startsWith(\"{\");\n",
7
+ "/**\n * Code-fence meta. A Shiki transformer reads the tokens after the language and\n * promotes them to attributes on the rendered `<pre>`:\n *\n * - a title — the first bare token (```ts blume.config.ts) or `title=\"...\"` —\n * becomes `data-title`; the theme's code header shows it, falling back to the\n * language label.\n * - the `lineNumbers` keyword (```ts file.ts lineNumbers) becomes\n * `data-line-numbers`; the theme renders a counter-driven line-number gutter.\n * - the `wrap` keyword becomes `data-wrap`: this block's long lines wrap\n * instead of scrolling, as `markdown.code.wrap` does for every block.\n * - the `expandable` keyword becomes `data-expandable` on a block of\n * {@link EXPANDABLE_MIN_LINES} lines or more: the theme shows its first\n * {@link EXPANDABLE_VISIBLE_LINES} lines, and the page script adds a toggle\n * that shows the rest in full. A shorter block renders as it is.\n */\n\nimport {\n isLineRange,\n metaTokens,\n QUOTED_ATTR,\n RESERVED_META_KEYWORDS,\n} from \"./fence-meta.ts\";\n\n/** The lines a collapsed `expandable` block shows. */\nexport const EXPANDABLE_VISIBLE_LINES = 10;\n\n/**\n * The lines a block needs before `expandable` collapses it: a few more than it\n * shows, so the toggle never hides a line or two behind a click.\n */\nexport const EXPANDABLE_MIN_LINES = 16;\n\n/** The slice of Shiki's transformer `this` context Blume reads. */\ninterface CodeMetaContext {\n options: { meta?: { __raw?: string } };\n /** The block's code, as highlighted. */\n source?: string;\n}\n\n/** The `<pre>` hast node a Shiki `pre` hook receives. */\ninterface PreNode {\n properties: Record<string, boolean | number | string | undefined>;\n}\n\n/** A Shiki-compatible transformer, typed structurally to avoid a Shiki dep. */\nexport interface CodeTitleTransformer {\n name: string;\n pre: (this: CodeMetaContext, node: PreNode) => void;\n}\n\nconst TITLE_ATTR = /(?:^|\\s)title=(?:\"(?<dq>[^\"]*)\"|'(?<sq>[^']*)')/u;\n\n// The first bare token is the title (```ts blume.config.ts): a non-empty token\n// that isn't a Shiki line range (`{1,3-5}`), a `key=value` attr, or a reserved\n// keyword.\nconst isTitleToken = (token: string): boolean =>\n token.length > 0 &&\n !isLineRange(token) &&\n !token.includes(\"=\") &&\n !RESERVED_META_KEYWORDS.has(token);\n\n/** The title a fence's meta string promotes to `data-title`, if any. */\nexport const parseCodeTitle = (raw: string | undefined): string | undefined => {\n if (!raw) {\n return undefined;\n }\n // Blank every *other* quoted attr first, so a `title=\"…\"` embedded in\n // another attribute's value (`caption='set title=\"X\" here'`) can't be\n // promoted to the block title.\n const scrubbed = raw.replace(QUOTED_ATTR, (attr) =>\n attr.startsWith(\"title=\") ? attr : \" \"\n );\n const explicit = scrubbed.match(TITLE_ATTR);\n const attrTitle = explicit?.groups?.dq ?? explicit?.groups?.sq;\n if (attrTitle) {\n return attrTitle;\n }\n // The shared tokenizer keeps a quoted attr (rejected below by its `=`) and\n // a spaced line range (`{1, 3-5}`) whole, so neither can shed a fragment\n // that reads as a bare title.\n return metaTokens(raw).find(isTitleToken);\n};\n\n// The shared tokenizer keeps a quoted attr whole, so a keyword inside a\n// quoted value (`title=\"enable lineNumbers later\"`) is never a token of its own.\nconst hasKeyword = (raw: string | undefined, keyword: string): boolean =>\n metaTokens(raw).includes(keyword);\n\n/** How many lines a block's code runs to, a trailing newline aside. */\nexport const lineCount = (source = \"\"): number =>\n source === \"\" ? 0 : source.replace(/\\r?\\n$/u, \"\").split(/\\r?\\n/u).length;\n\n/** Build the transformer. Runs after Shiki's built-in `data-language` hook. */\nexport const codeTitleTransformer = (): CodeTitleTransformer => ({\n name: \"blume:code-meta\",\n pre(node) {\n const raw = this.options.meta?.__raw;\n const title = parseCodeTitle(raw);\n if (title) {\n node.properties.dataTitle = title;\n }\n if (hasKeyword(raw, \"lineNumbers\")) {\n node.properties.dataLineNumbers = true;\n }\n if (hasKeyword(raw, \"wrap\")) {\n node.properties.dataWrap = true;\n }\n if (\n hasKeyword(raw, \"expandable\") &&\n lineCount(this.source) >= EXPANDABLE_MIN_LINES\n ) {\n node.properties.dataExpandable = true;\n }\n },\n});\n",
8
+ "import type { ResolvedConfig } from \"../core/schema.ts\";\nimport type { PageRecord } from \"../core/types.ts\";\n\n/**\n * Resolve a page's facet values: the subset of its validated custom\n * frontmatter named by its content type's `facets` declaration. Strings facet\n * as-is; numbers and booleans are stringified so an enum-like `priority: 1`\n * or `enforced: true` still filters; any other shape (objects, arrays,\n * transformed dates) is not a facet value and is skipped. Returns `undefined`\n * when nothing facets, so the field stays absent from serialized documents\n * rather than shipping as `{}` on every page.\n */\n/** Whether a custom frontmatter value stringifies into a usable facet value. */\nconst isFacetValue = <T>(value: T): value is T & (string | number | boolean) =>\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\";\n\nexport const pageFacets = (\n page: Pick<PageRecord, \"contentType\" | \"custom\">,\n config: ResolvedConfig\n): Record<string, string> | undefined => {\n const declared = config.content.types[page.contentType]?.facets;\n if (!declared || declared.length === 0 || !page.custom) {\n return;\n }\n const facets: Record<string, string> = {};\n for (const key of declared) {\n const value = page.custom[key];\n if (isFacetValue(value)) {\n facets[key] = String(value);\n }\n }\n return Object.keys(facets).length > 0 ? facets : undefined;\n};\n",
9
+ "import { normalizeBasePath } from \"../../core/base-path.ts\";\nimport type { BlumeProject } from \"../../core/project-graph.ts\";\nimport type { Navigation } from \"../../core/types.ts\";\nimport { buildSearchDocuments } from \"../../search/documents.ts\";\nimport { pageFacets } from \"../../search/facets.ts\";\nimport type { OramaDoc } from \"../../search/orama-index.ts\";\nimport { agentMarkdown, buildRawMarkdown } from \"../markdown.ts\";\n\n/** A page entry surfaced by the `list_pages` MCP tool. */\nexport interface McpRoute {\n contentType: string;\n description?: string;\n /** Declared facet values (`content.types.<type>.facets`), key → value. */\n facets?: Record<string, string>;\n indexable: boolean;\n lastModified: string | null;\n /** Resolved locale code (the default locale when not under i18n). */\n locale: string;\n route: string;\n title: string;\n /** Docs version (`\"\"` for the current docs). */\n version: string;\n}\n\n/**\n * The self-contained snapshot the generated MCP endpoint serves. Bundles the\n * search documents, raw page Markdown, route list, and navigation so the server\n * works regardless of the configured search provider and needs no filesystem\n * access at request time. Serialized to `generated/mcp-data.json`.\n */\nexport interface McpData {\n /**\n * Normalized `deployment.base` (`\"\"` or `/seg`), layered onto routes when\n * emitting URLs — the site is base-less and routes are base-less manifest\n * paths, matching the sitemap/llms.txt convention.\n */\n base: string;\n /**\n * The site's `i18n.defaultLocale`, when i18n is configured. Selects a\n * word-segmenting Orama tokenizer for every non-Latin script, so\n * `search_docs` can match CJK, Cyrillic, Greek, Hebrew, or Devanagari\n * content.\n */\n defaultLocale?: string;\n /**\n * Archived docs version ids, in configured order. Present only on a\n * versioned site; its presence is what makes `search_docs`/`list_pages`\n * default to the current docs.\n */\n archivedVersions?: string[];\n documents: OramaDoc[];\n instructions?: string;\n name: string;\n navigation: Navigation;\n /** Per-locale trees for a locale-aware `get_navigation` (i18n sites only). */\n navigationByLocale?: Record<string, Navigation>;\n /** Per-archived-version trees, keyed by version id then locale code. */\n navigationByVersion?: Record<string, Record<string, Navigation>>;\n pages: Record<string, string>;\n routes: McpRoute[];\n site: string | null;\n version: string;\n}\n\n/** Build the MCP data snapshot from a resolved project. */\nexport const buildMcpData = async (project: BlumeProject): Promise<McpData> => {\n const { config, graph, manifest } = project;\n const [documents, rawMarkdown] = await Promise.all([\n // The MCP server is independent of on-page search, so index docs even when\n // the search provider is `none`. Documents are agent-facing, so\n // `<Visibility>` resolves like `get_page`/llms-full.txt (web-only content\n // removed, agents-only kept).\n buildSearchDocuments(project, {\n audience: \"agents\",\n includeWhenDisabled: true,\n }),\n buildRawMarkdown(project),\n ]);\n\n // `get_page` serves the agent variant: components downleveled to Markdown.\n const pages = Object.fromEntries(\n Object.entries(rawMarkdown).map(([route, entry]) => [\n route,\n agentMarkdown(entry),\n ])\n );\n\n const pageById = new Map(graph.pages.map((page) => [page.id, page]));\n\n const routes: McpRoute[] = [];\n // Fallback routes render the fallback locale's page at another locale's\n // URL: listing them would pass that page off as a translation, once per\n // locale. `get_page` still resolves them through `pages`, as llms.txt and\n // search leave them out.\n for (const route of manifest.routes) {\n if (route.hidden || route.fallback) {\n continue;\n }\n const page = pageById.get(route.id);\n const facets = page ? pageFacets(page, config) : undefined;\n const entry: McpRoute = {\n contentType: route.contentType,\n description: page?.description,\n indexable: route.indexable,\n lastModified: route.lastModified ?? null,\n locale: route.locale,\n route: route.path,\n title: route.title,\n version: route.version,\n };\n if (facets) {\n entry.facets = facets;\n }\n routes.push(entry);\n }\n\n const data: McpData = {\n base: normalizeBasePath(config.deployment.options.base),\n defaultLocale: config.i18n?.defaultLocale,\n documents: documents.map((doc) => {\n const document: OramaDoc = {\n content: doc.content,\n contentType: doc.contentType,\n description: doc.description,\n locale: doc.locale,\n route: doc.route,\n title: doc.title,\n version: doc.version,\n };\n if (doc.facets) {\n document.facets = doc.facets;\n }\n if (doc.boost !== undefined) {\n document.boost = doc.boost;\n }\n if (doc.keywords) {\n document.keywords = doc.keywords;\n }\n return document;\n }),\n instructions: config.agents.mcp.instructions,\n name: config.agents.mcp.name ?? config.title,\n navigation: graph.navigation,\n pages,\n routes,\n site: config.deployment.options.site ?? null,\n version: manifest.blumeVersion,\n };\n if (config.versions) {\n data.archivedVersions = config.versions.archived.map(\n (version) => version.id\n );\n data.navigationByVersion = graph.navigationByVersion;\n }\n if (config.i18n) {\n data.navigationByLocale = graph.navigationByLocale;\n }\n return data;\n};\n"
10
+ ],
11
+ "mappings": "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",
12
+ "debugId": "8C4562DED97D02A064756E2164756E21",
13
+ "names": []
14
+ }
@@ -211,11 +211,17 @@ var registry = [
211
211
  name: "feedback",
212
212
  slot: "Feedback"
213
213
  }),
214
+ layoutComponent({
215
+ description: "The site footer: a row of links, social icons, the theme toggle, and the Cookie settings link.",
216
+ file: "SiteFooter.astro",
217
+ name: "footer",
218
+ slot: "Footer"
219
+ }),
214
220
  ...CONTENT_COMPONENTS.map(contentComponent)
215
221
  ];
216
222
  var findItem = (name) => registry.find((item) => item.name === name);
217
223
 
218
224
  export { packageSrc, registry, findItem };
219
225
 
220
- //# debugId=2BDCE672B82B453C64756E2164756E21
221
- //# sourceMappingURL=chunk-r20tn01b.js.map
226
+ //# debugId=1311F7BFBB427DD564756E2164756E21
227
+ //# sourceMappingURL=chunk-2f7n4qzt.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/registry/registry.ts"],
4
4
  "sourcesContent": [
5
- "import { join } from \"pathe\";\n\nimport { packageRoot } from \"../core/package-root.ts\";\n\n/** A file copied into the user's project by `blume add`. */\nexport interface RegistryFile {\n /** Path to the source file, relative to the blume package `src` directory. */\n source: string;\n /** Destination path relative to the project root. */\n target: string;\n /** Rewrite the file's relative imports to `blume/*` specifiers on copy. */\n rewrite?: boolean;\n}\n\nexport interface RegistryItem {\n name: string;\n description: string;\n files: RegistryFile[];\n /** Lines printed after install to guide the user. */\n postInstall: string[];\n}\n\n/** Absolute path to the blume package `src` directory (the copy source root). */\nexport const packageSrc = join(packageRoot(), \"src\");\n\n/**\n * A built-in layout component offered as editable source. `blume add` rewrites\n * its relative imports to `blume/*`, so it renders identically to the built-in\n * until the user changes it, then registers under the matching `layout` slot.\n */\nconst layoutComponent = (config: {\n name: string;\n description: string;\n /** Source basename under `src/components/layout`. */\n file: string;\n /** Layout slot key, also the import name in the post-install hint. */\n slot: string;\n}): RegistryItem => {\n const target = `components/blume/${config.file}`;\n return {\n description: config.description,\n files: [\n {\n rewrite: true,\n source: `components/layout/${config.file}`,\n target,\n },\n ],\n name: config.name,\n postInstall: [\n \"Register it in components.ts:\",\n ' import { defineComponents } from \"blume\";',\n ` import ${config.slot} from \"./${target}\";`,\n \"\",\n ` export default defineComponents({ layout: { ${config.slot} } });`,\n \"\",\n \"It imports the rest from `blume/*`, so it matches the built-in until you edit it.\",\n ],\n };\n};\n\n/**\n * A built-in MDX content component offered as editable source. Like\n * {@link layoutComponent}, but registered under the `mdx` map (the tag you write\n * in `.mdx`) rather than a `layout` slot.\n */\nconst contentComponent = (config: {\n name: string;\n description: string;\n /** Source basename under `src/components/content`. */\n file: string;\n /** MDX tag / component name, also the import name in the post-install hint. */\n tag: string;\n}): RegistryItem => {\n const target = `components/blume/${config.file}`;\n return {\n description: config.description,\n files: [\n {\n rewrite: true,\n source: `components/content/${config.file}`,\n target,\n },\n ],\n name: config.name,\n postInstall: [\n \"Register it in components.ts:\",\n ' import { defineComponents } from \"blume\";',\n ` import ${config.tag} from \"./${target}\";`,\n \"\",\n ` export default defineComponents({ mdx: { ${config.tag} } });`,\n \"\",\n \"It imports the rest from `blume/*`, so it matches the built-in until you edit it.\",\n ],\n };\n};\n\n/** Every user-facing content component, by `blume add` name → source basename. */\nconst CONTENT_COMPONENTS: {\n name: string;\n description: string;\n file: string;\n tag: string;\n}[] = [\n {\n description: \"Aside for notes, tips, and warnings.\",\n file: \"Callout.astro\",\n name: \"callout\",\n tag: \"Callout\",\n },\n {\n description: \"A linkable card with icon, title, and body.\",\n file: \"Card.astro\",\n name: \"card\",\n tag: \"Card\",\n },\n {\n description: \"A responsive grid of cards.\",\n file: \"CardGroup.astro\",\n name: \"card-group\",\n tag: \"CardGroup\",\n },\n {\n description: \"Tabbed code blocks for multiple languages.\",\n file: \"CodeGroup.astro\",\n name: \"code-group\",\n tag: \"CodeGroup\",\n },\n {\n description: \"A small status/label badge.\",\n file: \"Badge.astro\",\n name: \"badge\",\n tag: \"Badge\",\n },\n {\n description: \"A numbered list of steps.\",\n file: \"Steps.astro\",\n name: \"steps\",\n tag: \"Steps\",\n },\n {\n description: \"A single step within Steps.\",\n file: \"Step.astro\",\n name: \"step\",\n tag: \"Step\",\n },\n {\n description: \"A tabbed content panel.\",\n file: \"Tabs.astro\",\n name: \"tabs\",\n tag: \"Tabs\",\n },\n {\n description: \"A single tab within Tabs.\",\n file: \"Tab.astro\",\n name: \"tab\",\n tag: \"Tab\",\n },\n {\n description: \"A collapsible accordion group.\",\n file: \"Accordion.astro\",\n name: \"accordion\",\n tag: \"Accordion\",\n },\n {\n description: \"A single item within an Accordion.\",\n file: \"AccordionItem.astro\",\n name: \"accordion-item\",\n tag: \"AccordionItem\",\n },\n {\n description: \"A multi-column layout.\",\n file: \"Columns.astro\",\n name: \"columns\",\n tag: \"Columns\",\n },\n {\n description: \"A single column within Columns.\",\n file: \"Column.astro\",\n name: \"column\",\n tag: \"Column\",\n },\n {\n description: \"A bordered frame around an image or embed.\",\n file: \"Frame.astro\",\n name: \"frame\",\n tag: \"Frame\",\n },\n {\n description: \"An inline expand/collapse disclosure.\",\n file: \"Expandable.astro\",\n name: \"expandable\",\n tag: \"Expandable\",\n },\n {\n description: \"A titled content panel.\",\n file: \"Panel.astro\",\n name: \"panel\",\n tag: \"Panel\",\n },\n {\n description: \"A hover tooltip.\",\n file: \"Tooltip.astro\",\n name: \"tooltip\",\n tag: \"Tooltip\",\n },\n {\n description: \"A compact linkable tile.\",\n file: \"Tile.astro\",\n name: \"tile\",\n tag: \"Tile\",\n },\n {\n description: \"A styled prompt / terminal block.\",\n file: \"Prompt.astro\",\n name: \"prompt\",\n tag: \"Prompt\",\n },\n {\n description: \"A responsive, privacy-friendly YouTube embed.\",\n file: \"YouTube.astro\",\n name: \"youtube\",\n tag: \"YouTube\",\n },\n];\n\n/** The built-in, Blume-owned source registry. */\nexport const registry: RegistryItem[] = [\n layoutComponent({\n description: \"The top navigation bar (logo, search, nav links).\",\n file: \"Header.astro\",\n name: \"header\",\n slot: \"Header\",\n }),\n layoutComponent({\n description: \"The sidebar navigation tree.\",\n file: \"NavTree.astro\",\n name: \"sidebar\",\n slot: \"Sidebar\",\n }),\n layoutComponent({\n description: \"The breadcrumb trail shown above page content.\",\n file: \"Breadcrumbs.astro\",\n name: \"breadcrumbs\",\n slot: \"Breadcrumbs\",\n }),\n layoutComponent({\n description: \"The on-this-page table of contents.\",\n file: \"TableOfContents.astro\",\n name: \"table-of-contents\",\n slot: \"TableOfContents\",\n }),\n layoutComponent({\n description: \"The previous/next pagination footer.\",\n file: \"Pagination.astro\",\n name: \"pagination\",\n slot: \"Pagination\",\n }),\n layoutComponent({\n description: 'The \"Was this page helpful?\" feedback rating.',\n file: \"PageFeedback.astro\",\n name: \"feedback\",\n slot: \"Feedback\",\n }),\n ...CONTENT_COMPONENTS.map(contentComponent),\n];\n\nexport const findItem = (name: string): RegistryItem | undefined =>\n registry.find((item) => item.name === name);\n"
5
+ "import { join } from \"pathe\";\n\nimport { packageRoot } from \"../core/package-root.ts\";\n\n/** A file copied into the user's project by `blume add`. */\nexport interface RegistryFile {\n /** Path to the source file, relative to the blume package `src` directory. */\n source: string;\n /** Destination path relative to the project root. */\n target: string;\n /** Rewrite the file's relative imports to `blume/*` specifiers on copy. */\n rewrite?: boolean;\n}\n\nexport interface RegistryItem {\n name: string;\n description: string;\n files: RegistryFile[];\n /** Lines printed after install to guide the user. */\n postInstall: string[];\n}\n\n/** Absolute path to the blume package `src` directory (the copy source root). */\nexport const packageSrc = join(packageRoot(), \"src\");\n\n/**\n * A built-in layout component offered as editable source. `blume add` rewrites\n * its relative imports to `blume/*`, so it renders identically to the built-in\n * until the user changes it, then registers under the matching `layout` slot.\n */\nconst layoutComponent = (config: {\n name: string;\n description: string;\n /** Source basename under `src/components/layout`. */\n file: string;\n /** Layout slot key, also the import name in the post-install hint. */\n slot: string;\n}): RegistryItem => {\n const target = `components/blume/${config.file}`;\n return {\n description: config.description,\n files: [\n {\n rewrite: true,\n source: `components/layout/${config.file}`,\n target,\n },\n ],\n name: config.name,\n postInstall: [\n \"Register it in components.ts:\",\n ' import { defineComponents } from \"blume\";',\n ` import ${config.slot} from \"./${target}\";`,\n \"\",\n ` export default defineComponents({ layout: { ${config.slot} } });`,\n \"\",\n \"It imports the rest from `blume/*`, so it matches the built-in until you edit it.\",\n ],\n };\n};\n\n/**\n * A built-in MDX content component offered as editable source. Like\n * {@link layoutComponent}, but registered under the `mdx` map (the tag you write\n * in `.mdx`) rather than a `layout` slot.\n */\nconst contentComponent = (config: {\n name: string;\n description: string;\n /** Source basename under `src/components/content`. */\n file: string;\n /** MDX tag / component name, also the import name in the post-install hint. */\n tag: string;\n}): RegistryItem => {\n const target = `components/blume/${config.file}`;\n return {\n description: config.description,\n files: [\n {\n rewrite: true,\n source: `components/content/${config.file}`,\n target,\n },\n ],\n name: config.name,\n postInstall: [\n \"Register it in components.ts:\",\n ' import { defineComponents } from \"blume\";',\n ` import ${config.tag} from \"./${target}\";`,\n \"\",\n ` export default defineComponents({ mdx: { ${config.tag} } });`,\n \"\",\n \"It imports the rest from `blume/*`, so it matches the built-in until you edit it.\",\n ],\n };\n};\n\n/** Every user-facing content component, by `blume add` name → source basename. */\nconst CONTENT_COMPONENTS: {\n name: string;\n description: string;\n file: string;\n tag: string;\n}[] = [\n {\n description: \"Aside for notes, tips, and warnings.\",\n file: \"Callout.astro\",\n name: \"callout\",\n tag: \"Callout\",\n },\n {\n description: \"A linkable card with icon, title, and body.\",\n file: \"Card.astro\",\n name: \"card\",\n tag: \"Card\",\n },\n {\n description: \"A responsive grid of cards.\",\n file: \"CardGroup.astro\",\n name: \"card-group\",\n tag: \"CardGroup\",\n },\n {\n description: \"Tabbed code blocks for multiple languages.\",\n file: \"CodeGroup.astro\",\n name: \"code-group\",\n tag: \"CodeGroup\",\n },\n {\n description: \"A small status/label badge.\",\n file: \"Badge.astro\",\n name: \"badge\",\n tag: \"Badge\",\n },\n {\n description: \"A numbered list of steps.\",\n file: \"Steps.astro\",\n name: \"steps\",\n tag: \"Steps\",\n },\n {\n description: \"A single step within Steps.\",\n file: \"Step.astro\",\n name: \"step\",\n tag: \"Step\",\n },\n {\n description: \"A tabbed content panel.\",\n file: \"Tabs.astro\",\n name: \"tabs\",\n tag: \"Tabs\",\n },\n {\n description: \"A single tab within Tabs.\",\n file: \"Tab.astro\",\n name: \"tab\",\n tag: \"Tab\",\n },\n {\n description: \"A collapsible accordion group.\",\n file: \"Accordion.astro\",\n name: \"accordion\",\n tag: \"Accordion\",\n },\n {\n description: \"A single item within an Accordion.\",\n file: \"AccordionItem.astro\",\n name: \"accordion-item\",\n tag: \"AccordionItem\",\n },\n {\n description: \"A multi-column layout.\",\n file: \"Columns.astro\",\n name: \"columns\",\n tag: \"Columns\",\n },\n {\n description: \"A single column within Columns.\",\n file: \"Column.astro\",\n name: \"column\",\n tag: \"Column\",\n },\n {\n description: \"A bordered frame around an image or embed.\",\n file: \"Frame.astro\",\n name: \"frame\",\n tag: \"Frame\",\n },\n {\n description: \"An inline expand/collapse disclosure.\",\n file: \"Expandable.astro\",\n name: \"expandable\",\n tag: \"Expandable\",\n },\n {\n description: \"A titled content panel.\",\n file: \"Panel.astro\",\n name: \"panel\",\n tag: \"Panel\",\n },\n {\n description: \"A hover tooltip.\",\n file: \"Tooltip.astro\",\n name: \"tooltip\",\n tag: \"Tooltip\",\n },\n {\n description: \"A compact linkable tile.\",\n file: \"Tile.astro\",\n name: \"tile\",\n tag: \"Tile\",\n },\n {\n description: \"A styled prompt / terminal block.\",\n file: \"Prompt.astro\",\n name: \"prompt\",\n tag: \"Prompt\",\n },\n {\n description: \"A responsive, privacy-friendly YouTube embed.\",\n file: \"YouTube.astro\",\n name: \"youtube\",\n tag: \"YouTube\",\n },\n];\n\n/** The built-in, Blume-owned source registry. */\nexport const registry: RegistryItem[] = [\n layoutComponent({\n description: \"The top navigation bar (logo, search, nav links).\",\n file: \"Header.astro\",\n name: \"header\",\n slot: \"Header\",\n }),\n layoutComponent({\n description: \"The sidebar navigation tree.\",\n file: \"NavTree.astro\",\n name: \"sidebar\",\n slot: \"Sidebar\",\n }),\n layoutComponent({\n description: \"The breadcrumb trail shown above page content.\",\n file: \"Breadcrumbs.astro\",\n name: \"breadcrumbs\",\n slot: \"Breadcrumbs\",\n }),\n layoutComponent({\n description: \"The on-this-page table of contents.\",\n file: \"TableOfContents.astro\",\n name: \"table-of-contents\",\n slot: \"TableOfContents\",\n }),\n layoutComponent({\n description: \"The previous/next pagination footer.\",\n file: \"Pagination.astro\",\n name: \"pagination\",\n slot: \"Pagination\",\n }),\n layoutComponent({\n description: 'The \"Was this page helpful?\" feedback rating.',\n file: \"PageFeedback.astro\",\n name: \"feedback\",\n slot: \"Feedback\",\n }),\n layoutComponent({\n description:\n \"The site footer: a row of links, social icons, the theme toggle, and the Cookie settings link.\",\n file: \"SiteFooter.astro\",\n name: \"footer\",\n slot: \"Footer\",\n }),\n ...CONTENT_COMPONENTS.map(contentComponent),\n];\n\nexport const findItem = (name: string): RegistryItem | undefined =>\n registry.find((item) => item.name === name);\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "2BDCE672B82B453C64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "1311F7BFBB427DD564756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -11,17 +11,17 @@ import {
11
11
  import {
12
12
  AGENTS,
13
13
  launchAgent
14
- } from "./chunk-1d7ve1dm.js";
14
+ } from "./chunk-0gz36akr.js";
15
15
  import {
16
16
  YAML_SCHEMA
17
- } from "./chunk-pv29h0wf.js";
18
- import"./chunk-3em5wd2y.js";
17
+ } from "./chunk-xkz5xbtv.js";
18
+ import"./chunk-zccbjcj0.js";
19
19
  import {
20
20
  scanProject
21
- } from "./chunk-j85vccga.js";
21
+ } from "./chunk-43zcf4gz.js";
22
22
  import {
23
23
  buildMcpData
24
- } from "./chunk-yfyb25rh.js";
24
+ } from "./chunk-2f3ndkpw.js";
25
25
  import {
26
26
  seconds,
27
27
  money,
@@ -34,7 +34,7 @@ import {
34
34
  } from "./chunk-ga0pf4aj.js";
35
35
  import {
36
36
  parseTimeoutSeconds
37
- } from "./chunk-e04dxsz1.js";
37
+ } from "./chunk-4ejxbzee.js";
38
38
 
39
39
  // src/cli/commands/eval.ts
40
40
  import { existsSync } from "node:fs";
@@ -680,4 +680,4 @@ export {
680
680
  };
681
681
 
682
682
  //# debugId=C123F57ABFFCB50F64756E2164756E21
683
- //# sourceMappingURL=chunk-35d4wj9f.js.map
683
+ //# sourceMappingURL=chunk-2n4ps1b3.js.map
@@ -22,6 +22,10 @@ import {
22
22
  pagesByRef,
23
23
  buildNavigation,
24
24
  PUBLIC_API_URL,
25
+ pathsUnderPattern,
26
+ hasVariables,
27
+ substituteVariables,
28
+ undefinedVariables,
25
29
  pageMetaSchema,
26
30
  folderMetaSchema,
27
31
  versionizeRoute,
@@ -59,15 +63,17 @@ import {
59
63
  neutralizeUnsafeLinks,
60
64
  columnsPrefix,
61
65
  openApiSource,
62
- BLUME_IGNORE_DIRS,
63
- baselineScanIgnore,
64
- BLUME_WATCH_IGNORE_DIRS,
65
- excludeDirSegments,
66
- ignoringWatchListener,
66
+ isJsonObject,
67
+ getPath,
68
+ isStringValue2,
69
+ asString,
70
+ asObject,
71
+ asNumber,
72
+ objectsIn,
67
73
  createModuleLoader,
68
74
  resolveProjectContext,
69
75
  loadConfig
70
- } from "./chunk-pv29h0wf.js";
76
+ } from "./chunk-xkz5xbtv.js";
71
77
  import {
72
78
  getBlumeVersion,
73
79
  BlumeError,
@@ -78,7 +84,14 @@ import {
78
84
  isInternalPath,
79
85
  withBasePath,
80
86
  mountBasePath
81
- } from "./chunk-3em5wd2y.js";
87
+ } from "./chunk-zccbjcj0.js";
88
+ import {
89
+ BLUME_IGNORE_DIRS,
90
+ baselineScanIgnore,
91
+ BLUME_WATCH_IGNORE_DIRS,
92
+ excludeDirSegments,
93
+ ignoringWatchListener
94
+ } from "./chunk-kwrg63tm.js";
82
95
 
83
96
  // src/core/manifest.ts
84
97
  var versionAlternateKey = (versionKey, locale) => `${versionKey}\x00${locale}`;
@@ -302,6 +315,15 @@ var unknownDirectiveDiagnostics = (entry, sourceName) => {
302
315
  });
303
316
  };
304
317
 
318
+ // src/core/localizable.ts
319
+ var isSingleLabel = (label) => typeof label === "string";
320
+ var resolveLocalizable = (label, locale, defaultLocale) => {
321
+ if (isSingleLabel(label)) {
322
+ return label;
323
+ }
324
+ return (locale === undefined ? undefined : label[locale]) ?? (defaultLocale === undefined ? undefined : label[defaultLocale]) ?? Object.values(label)[0] ?? "";
325
+ };
326
+
305
327
  // src/theme/icon-kind.ts
306
328
  var IMAGE_ICON = /^(?:https?:\/\/|data:image\/|\/|\.{1,2}\/)|\.(?:avif|gif|jpe?g|png|svg|webp)$/iu;
307
329
  var INLINE_SVG = /^\s*<svg[\s\S]*<\/svg>\s*$/u;
@@ -566,13 +588,6 @@ var localePagesFor = (code, real, fallback, fallbackByKey, i18n, basePath) => {
566
588
  }
567
589
  return [...real, ...filled];
568
590
  };
569
- var isSingleLabel = (label) => typeof label === "string";
570
- var resolveLabel = (label, locale, defaultLocale) => {
571
- if (isSingleLabel(label)) {
572
- return label;
573
- }
574
- return label[locale] ?? (defaultLocale === undefined ? undefined : label[defaultLocale]) ?? Object.values(label)[0] ?? "";
575
- };
576
591
  var localizeSidebarHrefs = (items, localize) => items.map((item) => {
577
592
  if (isPageRef(item)) {
578
593
  return item;
@@ -590,10 +605,19 @@ var resolveTabLabels = (tabs, locale, defaultLocale) => (tabs ?? []).map((tab) =
590
605
  ...tab,
591
606
  items: tab.items?.map((item) => ({
592
607
  ...item,
593
- label: resolveLabel(item.label, locale, defaultLocale)
608
+ label: resolveLocalizable(item.label, locale, defaultLocale)
594
609
  })),
595
- label: resolveLabel(tab.label, locale, defaultLocale)
610
+ label: resolveLocalizable(tab.label, locale, defaultLocale)
596
611
  }));
612
+ var withLabel = (link, locale, defaultLocale) => ({
613
+ ...link,
614
+ label: resolveLocalizable(link.label, locale, defaultLocale)
615
+ });
616
+ var resolveHeaderLinks = (navigation, locale, defaultLocale) => ({
617
+ actions: navigation.actions.map((link) => withLabel(link, locale, defaultLocale)),
618
+ cta: navigation.cta ? withLabel(navigation.cta, locale, defaultLocale) : null,
619
+ featured: navigation.featured.map((link) => withLabel(link, locale, defaultLocale))
620
+ });
597
621
  var mountConfiguredLink = (path, basePath, extraRoutes) => extraRoutes.has(path) ? path : withBasePath(basePath, path);
598
622
  var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n, diagnostics, version = "") => {
599
623
  const basePath = options.basePath ?? "";
@@ -637,17 +661,17 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
637
661
  ...item,
638
662
  href: localizeServed(item.href, false)
639
663
  });
640
- const { actions, cta, featured } = options.navigation;
664
+ const { actions, cta, featured } = resolveHeaderLinks(options.navigation, code, i18n.defaultLocale);
641
665
  const sidebarItems = options.navigation.sidebar.items;
642
666
  const navigation = buildNavigation(localePages, {
643
- actions: actions?.map(localizeHref),
667
+ actions: actions.map(localizeHref),
644
668
  basePath,
645
669
  cta: cta ? localizeHref(cta) : null,
646
670
  diagnostics,
647
671
  display: options.navigation.sidebar.display,
648
672
  extraRoutes: options.extraRoutes,
649
673
  fallbackMetaPrefix,
650
- featured: featured?.map(localizeHref),
674
+ featured: featured.map(localizeHref),
651
675
  folderMeta: options.folderMeta,
652
676
  localizedRoot: localizeRoute(versionizeRoute("/", version), code, i18n),
653
677
  metaPrefix: [version, localeDir].filter(Boolean).join("/"),
@@ -709,13 +733,11 @@ var buildVersionNavigation = (id, versionPages, options, diagnostics) => {
709
733
  }
710
734
  return {
711
735
  "": buildNavigation(versionPages, {
712
- actions: options.navigation.actions,
736
+ ...resolveHeaderLinks(options.navigation, ""),
713
737
  basePath: options.basePath ?? "",
714
- cta: options.navigation.cta,
715
738
  diagnostics,
716
739
  display: options.navigation.sidebar.display,
717
740
  extraRoutes: options.extraRoutes,
718
- featured: options.navigation.featured,
719
741
  folderMeta: options.folderMeta,
720
742
  localizedRoot: versionizeRoute("/", id),
721
743
  metaPrefix: id,
@@ -737,13 +759,11 @@ var buildContentGraph = (pages, options) => {
737
759
  ({ navigation, navigationByLocale } = buildI18nNavigation(currentPages, options, i18n, diagnostics));
738
760
  } else {
739
761
  navigation = buildNavigation(currentPages, {
740
- actions: options.navigation.actions,
762
+ ...resolveHeaderLinks(options.navigation, ""),
741
763
  basePath: options.basePath ?? "",
742
- cta: options.navigation.cta,
743
764
  diagnostics,
744
765
  display: options.navigation.sidebar.display,
745
766
  extraRoutes: options.extraRoutes,
746
- featured: options.navigation.featured,
747
767
  folderMeta: options.folderMeta,
748
768
  selectors: options.navigation.selectors,
749
769
  sharedFolderMeta: options.sharedFolderMeta,
@@ -986,30 +1006,6 @@ var discoverFolderMeta = async (sources, options = {}) => {
986
1006
  // src/core/sources/resolve.ts
987
1007
  import { join as join7 } from "pathe";
988
1008
 
989
- // src/core/sources/json.ts
990
- var isJsonObject = (value) => typeof value === "object" && value !== null;
991
- var getPath = (doc, path) => {
992
- let current = doc;
993
- for (const key of path.split(".")) {
994
- if (isJsonObject(current)) {
995
- current = current[key];
996
- } else {
997
- return;
998
- }
999
- }
1000
- return current;
1001
- };
1002
- var isStringValue2 = (value) => typeof value === "string";
1003
- var asString = (value) => isStringValue2(value) ? value : undefined;
1004
- var asArray = (value) => Array.isArray(value) ? value : [];
1005
- var asObject = (value) => isJsonObject(value) && !Array.isArray(value) ? value : undefined;
1006
- var isNumberValue = (value) => typeof value === "number";
1007
- var asNumber = (value) => isNumberValue(value) ? value : undefined;
1008
- var objectsIn = (value) => asArray(value).flatMap((item) => {
1009
- const object = asObject(item);
1010
- return object ? [object] : [];
1011
- });
1012
-
1013
1009
  // src/core/sources/contentful-rich-text.ts
1014
1010
  var HEADING = /^heading-(?<level>[1-6])$/u;
1015
1011
  var CELL_PIPE = /\|/gu;
@@ -3523,6 +3519,46 @@ var gitContentRoots = (sources, gitRoot) => {
3523
3519
  var isLogoShorthand = (logo) => typeof logo === "string";
3524
3520
  var logoHref = (logo) => logo === undefined || isLogoShorthand(logo) ? "/" : logo.href ?? "/";
3525
3521
  var isBannerShorthand = (banner) => typeof banner === "string";
3522
+ var substituteEntryVariables = (entry, variables) => {
3523
+ const { expanded } = entry;
3524
+ const text = expanded?.text ?? entry.body.text;
3525
+ const offset = expanded ? 0 : strippedLineOffset(entry.raw, entry.body.text);
3526
+ const diagnostics = undefinedVariables(text, variables).map(({ line, name }) => {
3527
+ const origin = expanded?.origins[line - 1];
3528
+ return {
3529
+ code: "BLUME_UNDEFINED_VARIABLE",
3530
+ file: origin?.file ?? entry.sourcePath,
3531
+ line: origin?.line ?? line + offset,
3532
+ message: `{{${name}}} isn't a defined variable, so it would show as written.`,
3533
+ severity: "error",
3534
+ suggestion: `Define "${name}" under variables in blume.config.ts, or put it in inline code to show it as written.`
3535
+ };
3536
+ });
3537
+ if (expanded) {
3538
+ expanded.text = substituteVariables(expanded.text, variables);
3539
+ } else {
3540
+ entry.body.text = substituteVariables(entry.body.text, variables);
3541
+ }
3542
+ return diagnostics;
3543
+ };
3544
+ var substituteLoadedVariables = (loaded, variables) => hasVariables(variables) ? loaded.flatMap(({ entries }) => entries.flatMap((entry) => substituteEntryVariables(entry, variables))) : [];
3545
+ var redirectPageDiagnostics = (config, pages, configFile) => config.redirects.flatMap((redirect) => {
3546
+ const matched = pathsUnderPattern(withBasePath(config.basePath, redirect.from), pages);
3547
+ const [first] = matched;
3548
+ if (first === undefined) {
3549
+ return [];
3550
+ }
3551
+ const more = matched.length > 1 ? ` and ${matched.length - 1} more` : "";
3552
+ return [
3553
+ {
3554
+ code: "BLUME_REDIRECT_MATCHES_PAGE",
3555
+ file: configFile ?? undefined,
3556
+ message: `The redirect from ${redirect.from} also matches the page ${first}${more}, which some hosts would redirect and others would serve.`,
3557
+ severity: "error",
3558
+ suggestion: "Narrow the pattern to the paths that moved, or move the pages out from under it."
3559
+ }
3560
+ ];
3561
+ });
3526
3562
  var bannerLinkHref = (banner) => banner === undefined || isBannerShorthand(banner) ? undefined : banner.link?.href;
3527
3563
  var scanProject = async (root, options = {}) => {
3528
3564
  const mode = options.mode ?? "dev";
@@ -3576,6 +3612,7 @@ var scanProject = async (root, options = {}) => {
3576
3612
  includeDiagnostics.push(...expansion.errors);
3577
3613
  });
3578
3614
  }));
3615
+ const variableDiagnostics = substituteLoadedVariables(loaded, config.variables);
3579
3616
  const sharedFolderMeta = new Map([
3580
3617
  ...loaded.flatMap(({ folderMeta: sourceMeta }) => Object.entries(sourceMeta ?? {})),
3581
3618
  ...folderMeta.shared
@@ -3602,14 +3639,15 @@ var scanProject = async (root, options = {}) => {
3602
3639
  lastModifiedWarnings.push(...lastModifiedShallowWarning(context.root, undated));
3603
3640
  }
3604
3641
  const customPages = context.pagesRoot ? await discoverPages(context.pagesRoot) : [];
3642
+ const extraRoutes = new Set([
3643
+ ...customStaticRoutes(customPages),
3644
+ ...hasChangelogIndex(pages, config) ? [CHANGELOG_INDEX_ROUTE] : []
3645
+ ]);
3605
3646
  const graph = buildContentGraph(pages, {
3606
3647
  bannerHref: bannerLinkHref(config.banner),
3607
3648
  basePath: config.basePath,
3608
3649
  brandHref: logoHref(config.logo),
3609
- extraRoutes: new Set([
3610
- ...customStaticRoutes(customPages),
3611
- ...hasChangelogIndex(pages, config) ? [CHANGELOG_INDEX_ROUTE] : []
3612
- ]),
3650
+ extraRoutes,
3613
3651
  folderMeta: folderMeta.meta,
3614
3652
  i18n: config.i18n,
3615
3653
  navigation: config.navigation,
@@ -3625,12 +3663,14 @@ var scanProject = async (root, options = {}) => {
3625
3663
  diagnostics: [
3626
3664
  ...contentDiagnostics,
3627
3665
  ...includeDiagnostics,
3666
+ ...variableDiagnostics,
3628
3667
  ...folderMeta.diagnostics,
3629
3668
  ...entryIdDiagnostics(pages, resolveDocsCollection(config, context.root).base),
3630
3669
  ...graph.diagnostics,
3631
3670
  ...i18nWarnings,
3632
3671
  ...versionWarnings,
3633
- ...lastModifiedWarnings
3672
+ ...lastModifiedWarnings,
3673
+ ...redirectPageDiagnostics(config, [...manifest.routes.map((route) => route.path), ...extraRoutes], context.configFile)
3634
3674
  ],
3635
3675
  droppedPages,
3636
3676
  graph,
@@ -3641,7 +3681,7 @@ var scanProject = async (root, options = {}) => {
3641
3681
  };
3642
3682
  };
3643
3683
 
3644
- export { directiveSource, validateSearchPopularIcons, validateNavTargets, contentIndexable, buildManifest, findFolderMetaFiles, isWithin, scanProject };
3684
+ export { directiveSource, resolveLocalizable, validateSearchPopularIcons, validateNavTargets, contentIndexable, buildManifest, findFolderMetaFiles, isWithin, scanProject };
3645
3685
 
3646
- //# debugId=B7A86608D56AB00864756E2164756E21
3647
- //# sourceMappingURL=chunk-j85vccga.js.map
3686
+ //# debugId=89E5C13A316C75D864756E2164756E21
3687
+ //# sourceMappingURL=chunk-43zcf4gz.js.map