blume 2.0.1 → 2.0.2

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 (410) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cli/{chunk-vtk4a6dg.js → chunk-00gs3wqs.js} +1 -1
  3. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  4. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  5. package/dist/cli/{chunk-yw7dm696.js → chunk-3yce002v.js} +21 -8
  6. package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
  7. package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
  8. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  9. package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
  10. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  11. package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
  12. package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
  13. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
  14. package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
  15. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  16. package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
  17. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  18. package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
  19. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
  20. package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
  21. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  22. package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
  23. package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
  24. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  25. package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
  26. package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
  27. package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
  28. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  29. package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
  30. package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
  31. package/dist/cli/{chunk-hdpx1tax.js → chunk-g698a744.js} +5 -5
  32. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  33. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  34. package/dist/cli/{chunk-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
  35. package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
  36. package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
  37. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  38. package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
  39. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  40. package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
  41. package/dist/cli/chunk-j85scx15.js.map +10 -0
  42. package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
  43. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  44. package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
  45. package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
  46. package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
  47. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  48. package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
  49. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  50. package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
  51. package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
  52. package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
  53. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  54. package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
  55. package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
  56. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  57. package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
  58. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  59. package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
  60. package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
  61. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  62. package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
  63. package/dist/cli/index.js +160 -35
  64. package/dist/cli/index.js.map +4 -4
  65. package/dist/types/ai/agent-surface.d.ts +32 -0
  66. package/dist/types/ai/api-catalog.d.ts +7 -1
  67. package/dist/types/ai/ask-context.d.ts +7 -0
  68. package/dist/types/ai/component-markdown.d.ts +4 -4
  69. package/dist/types/ai/relative-links.d.ts +9 -4
  70. package/dist/types/ai/static-expression.d.ts +28 -0
  71. package/dist/types/analytics/databuddy.d.ts +43 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +14 -0
  74. package/dist/types/cli/env.d.ts +5 -0
  75. package/dist/types/cli/init/scaffold.d.ts +19 -3
  76. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  77. package/dist/types/core/base-path.d.ts +9 -0
  78. package/dist/types/core/config-input.d.ts +4 -4
  79. package/dist/types/core/config.d.ts +2 -2
  80. package/dist/types/core/graph.d.ts +2 -0
  81. package/dist/types/core/i18n-ui.d.ts +31 -0
  82. package/dist/types/core/i18n.d.ts +7 -1
  83. package/dist/types/core/links.d.ts +3 -1
  84. package/dist/types/core/load-module.d.ts +10 -0
  85. package/dist/types/core/locale-links.d.ts +12 -2
  86. package/dist/types/core/meta.d.ts +5 -1
  87. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  88. package/dist/types/core/navigation.d.ts +38 -0
  89. package/dist/types/core/ordering-prefix.d.ts +4 -0
  90. package/dist/types/core/safe-links.d.ts +3 -1
  91. package/dist/types/core/schema.d.ts +52 -13
  92. package/dist/types/core/sources/github-releases.d.ts +5 -0
  93. package/dist/types/core/sources/lower.d.ts +14 -2
  94. package/dist/types/core/sources/normalize.d.ts +10 -1
  95. package/dist/types/core/sources/remote.d.ts +11 -1
  96. package/dist/types/core/sources/resolve.d.ts +12 -0
  97. package/dist/types/core/sources/types.d.ts +31 -0
  98. package/dist/types/core/types.d.ts +9 -0
  99. package/dist/types/deploy/adapters/node.d.ts +5 -2
  100. package/dist/types/deploy/adapters/types.d.ts +7 -0
  101. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  102. package/dist/types/deploy/headers.d.ts +31 -7
  103. package/dist/types/deploy/node-headers.d.ts +43 -8
  104. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  105. package/dist/types/deploy/platforms/node.d.ts +6 -5
  106. package/dist/types/deploy/platforms/types.d.ts +7 -0
  107. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  108. package/dist/types/deploy/redirects.d.ts +7 -2
  109. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  110. package/dist/types/openapi/model.d.ts +20 -6
  111. package/dist/types/search/sync/algolia.d.ts +3 -1
  112. package/docs/01-quickstart.mdx +3 -2
  113. package/docs/02-deployment.mdx +17 -8
  114. package/docs/08-faq.mdx +9 -2
  115. package/docs/advanced/changelog.mdx +1 -1
  116. package/docs/advanced/custom-pages.mdx +4 -2
  117. package/docs/cli/audit.mdx +2 -2
  118. package/docs/cli/doctor.mdx +2 -2
  119. package/docs/cli/evals.mdx +2 -2
  120. package/docs/cli/index.mdx +4 -1
  121. package/docs/cli/translate.mdx +1 -1
  122. package/docs/configuration/analytics.mdx +23 -2
  123. package/docs/configuration/assistant.mdx +1 -1
  124. package/docs/configuration/customization.mdx +4 -3
  125. package/docs/configuration/index.mdx +5 -3
  126. package/docs/configuration/search.mdx +1 -1
  127. package/docs/content/components.mdx +1 -1
  128. package/docs/content/frontmatter.mdx +5 -1
  129. package/docs/content/i18n.mdx +1 -1
  130. package/docs/content/index.mdx +4 -2
  131. package/docs/content/meta.mdx +5 -3
  132. package/docs/content/navigation.mdx +29 -4
  133. package/docs/content/sources.mdx +18 -12
  134. package/docs/content/versioning.mdx +1 -0
  135. package/docs/discoverability/agent-discovery.mdx +21 -7
  136. package/docs/discoverability/index.mdx +2 -2
  137. package/docs/discoverability/llms-txt.mdx +2 -5
  138. package/docs/discoverability/markdown.mdx +5 -3
  139. package/docs/discoverability/mcp.mdx +4 -0
  140. package/docs/discoverability/metadata.mdx +3 -2
  141. package/docs/discoverability/open-graph.mdx +6 -4
  142. package/docs/discoverability/rss.mdx +3 -1
  143. package/docs/references/asyncapi.mdx +1 -1
  144. package/docs/references/graphql.mdx +2 -2
  145. package/docs/references/openapi.mdx +3 -3
  146. package/package.json +1 -1
  147. package/skills/blume-migrate/SKILL.md +6 -6
  148. package/skills/blume-migrate/references/docusaurus.md +6 -6
  149. package/skills/blume-migrate/references/mintlify.md +3 -3
  150. package/skills/blume-migrate/references/monorepo.md +1 -1
  151. package/skills/blume-migrate/references/nextra.md +1 -1
  152. package/skills/blume-migrate/references/starlight.md +5 -5
  153. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  154. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  155. package/src/ai/agent-surface.ts +56 -0
  156. package/src/ai/api/spec.ts +15 -2
  157. package/src/ai/api-catalog.ts +7 -1
  158. package/src/ai/ask-context.ts +14 -2
  159. package/src/ai/ask-data.ts +26 -12
  160. package/src/ai/component-markdown.ts +43 -37
  161. package/src/ai/link-headers.ts +8 -3
  162. package/src/ai/llms.ts +4 -3
  163. package/src/ai/markdown.ts +23 -11
  164. package/src/ai/mcp/server.ts +78 -7
  165. package/src/ai/relative-links.ts +78 -10
  166. package/src/ai/static-expression.ts +416 -0
  167. package/src/ai/visibility.ts +45 -14
  168. package/src/analytics/databuddy.ts +67 -0
  169. package/src/analytics/head.ts +4 -0
  170. package/src/analytics/index.ts +2 -0
  171. package/src/analytics/posthog.ts +21 -3
  172. package/src/analytics/schema.ts +2 -0
  173. package/src/astro/generate.ts +71 -27
  174. package/src/astro/templates.ts +61 -23
  175. package/src/audit/catalog.ts +2 -2
  176. package/src/audit/checks/assets.ts +25 -5
  177. package/src/audit/checks/content.ts +20 -2
  178. package/src/audit/checks/i18n.ts +46 -8
  179. package/src/audit/checks/indexability.ts +39 -19
  180. package/src/audit/checks/links.ts +14 -0
  181. package/src/audit/checks/llms.ts +6 -3
  182. package/src/audit/checks/network.ts +3 -1
  183. package/src/audit/checks/og-image.ts +10 -0
  184. package/src/audit/checks/robots.ts +6 -1
  185. package/src/audit/checks/sitemap.ts +56 -31
  186. package/src/audit/checks/social.ts +21 -2
  187. package/src/audit/crawl.ts +88 -15
  188. package/src/audit/report.ts +54 -17
  189. package/src/audit/run.ts +1 -0
  190. package/src/audit/types.ts +18 -0
  191. package/src/audit/url.ts +37 -6
  192. package/src/cli/build-failure.ts +50 -0
  193. package/src/cli/commands/audit.ts +7 -0
  194. package/src/cli/commands/build.ts +19 -5
  195. package/src/cli/commands/check.ts +2 -0
  196. package/src/cli/commands/eval.ts +1 -7
  197. package/src/cli/commands/init.ts +15 -5
  198. package/src/cli/commands/preview.ts +15 -0
  199. package/src/cli/commands/sync.ts +2 -0
  200. package/src/cli/commands/translate.ts +9 -6
  201. package/src/cli/commands/upgrade.ts +11 -0
  202. package/src/cli/eject-scripts.ts +32 -7
  203. package/src/cli/env.ts +12 -1
  204. package/src/cli/init/scaffold.ts +117 -11
  205. package/src/cli/init/starter-spec.ts +235 -0
  206. package/src/components/content/AccordionItem.astro +26 -22
  207. package/src/components/content/Badge.astro +2 -9
  208. package/src/components/content/Card.astro +2 -2
  209. package/src/components/content/Component.astro +9 -3
  210. package/src/components/content/Expandable.astro +5 -1
  211. package/src/components/content/Frame.astro +2 -7
  212. package/src/components/content/GithubInfo.astro +12 -2
  213. package/src/components/content/Prompt.astro +2 -7
  214. package/src/components/content/Tabs.astro +54 -5
  215. package/src/components/content/Tile.astro +1 -1
  216. package/src/components/content/Tooltip.astro +69 -7
  217. package/src/components/content/Tree.astro +7 -2
  218. package/src/components/content/TypeTable.astro +10 -5
  219. package/src/components/content/Update.astro +8 -2
  220. package/src/components/content/auto-type-table.ts +4 -1
  221. package/src/components/content/badge-color.ts +17 -0
  222. package/src/components/content/base-href.ts +18 -3
  223. package/src/components/content/inline-markdown.ts +27 -7
  224. package/src/components/copy-feedback.ts +35 -8
  225. package/src/components/islands/assistant.tsx +16 -2
  226. package/src/components/islands/hooks.ts +7 -2
  227. package/src/components/islands/webmcp.ts +12 -8
  228. package/src/components/layout/Banner.astro +23 -4
  229. package/src/components/layout/Header.astro +22 -9
  230. package/src/components/layout/Logo.astro +5 -0
  231. package/src/components/layout/NavSelector.astro +6 -1
  232. package/src/components/layout/NavTabMenu.astro +133 -0
  233. package/src/components/layout/NavTree.astro +15 -6
  234. package/src/components/layout/NavTreeCache.astro +5 -2
  235. package/src/components/layout/NavTreeScript.astro +45 -5
  236. package/src/components/layout/PageLayout.astro +22 -8
  237. package/src/components/layout/ReferenceLayout.astro +4 -1
  238. package/src/components/layout/RootLayout.astro +21 -8
  239. package/src/components/layout/Search.astro +5 -1
  240. package/src/components/layout/analytics-client.ts +2 -0
  241. package/src/components/openapi/GraphqlType.astro +11 -3
  242. package/src/components/openapi/MessageComposer.astro +1 -1
  243. package/src/components/openapi/Operation.astro +21 -5
  244. package/src/components/openapi/PanelTabs.astro +4 -1
  245. package/src/components/openapi/Playground.astro +8 -2
  246. package/src/components/openapi/RequestPanel.astro +9 -5
  247. package/src/components/openapi/SchemaProperty.astro +11 -33
  248. package/src/components/openapi/SchemaTable.astro +27 -52
  249. package/src/components/openapi/helpers.ts +90 -12
  250. package/src/components/openapi/message-composer.ts +8 -0
  251. package/src/components/openapi/message-model.ts +12 -2
  252. package/src/components/openapi/message.ts +4 -1
  253. package/src/components/openapi/operation-model.ts +40 -8
  254. package/src/components/openapi/panel.ts +29 -5
  255. package/src/components/openapi/playground-client.ts +37 -7
  256. package/src/components/openapi/playground-schema.ts +25 -6
  257. package/src/components/openapi/request.ts +2 -0
  258. package/src/components/openapi/schema-tree.ts +203 -0
  259. package/src/components/openapi/snippets.ts +30 -15
  260. package/src/components/openapi/validate-json.ts +1 -1
  261. package/src/core/base-path.ts +15 -0
  262. package/src/core/config-input.ts +4 -4
  263. package/src/core/config.ts +18 -4
  264. package/src/core/diagnostics.ts +217 -30
  265. package/src/core/graph.ts +120 -11
  266. package/src/core/i18n-ui.ts +39 -2
  267. package/src/core/i18n.ts +13 -2
  268. package/src/core/links.ts +7 -4
  269. package/src/core/load-module.ts +20 -0
  270. package/src/core/locale-links.ts +17 -17
  271. package/src/core/manifest.ts +3 -2
  272. package/src/core/meta.ts +69 -6
  273. package/src/core/nav-diagnostics.ts +56 -1
  274. package/src/core/navigation.ts +245 -76
  275. package/src/core/ordering-prefix.ts +27 -0
  276. package/src/core/project-graph.ts +23 -1
  277. package/src/core/safe-href.ts +53 -1
  278. package/src/core/safe-links.ts +11 -2
  279. package/src/core/schema.ts +70 -14
  280. package/src/core/sources/assets.ts +83 -41
  281. package/src/core/sources/contentful-rich-text.ts +3 -2
  282. package/src/core/sources/contentful.ts +25 -12
  283. package/src/core/sources/filesystem.ts +5 -1
  284. package/src/core/sources/github-releases.ts +106 -5
  285. package/src/core/sources/lexical.ts +9 -4
  286. package/src/core/sources/lower.ts +79 -26
  287. package/src/core/sources/mdx-remote.ts +1 -0
  288. package/src/core/sources/normalize.ts +132 -30
  289. package/src/core/sources/notion.ts +26 -10
  290. package/src/core/sources/obsidian.ts +17 -3
  291. package/src/core/sources/payload.ts +1 -0
  292. package/src/core/sources/portable-text.ts +63 -44
  293. package/src/core/sources/remote.ts +18 -2
  294. package/src/core/sources/resolve.ts +52 -33
  295. package/src/core/sources/sanity.ts +1 -0
  296. package/src/core/sources/strapi-blocks.ts +9 -4
  297. package/src/core/sources/strapi.ts +1 -0
  298. package/src/core/sources/types.ts +31 -0
  299. package/src/core/types.ts +9 -0
  300. package/src/core/ui-packs/ar.ts +13 -0
  301. package/src/core/ui-packs/bg.ts +13 -0
  302. package/src/core/ui-packs/bn.ts +13 -0
  303. package/src/core/ui-packs/ca.ts +13 -0
  304. package/src/core/ui-packs/cs.ts +13 -0
  305. package/src/core/ui-packs/da.ts +13 -0
  306. package/src/core/ui-packs/de.ts +13 -0
  307. package/src/core/ui-packs/el.ts +13 -0
  308. package/src/core/ui-packs/es.ts +13 -0
  309. package/src/core/ui-packs/fa.ts +13 -0
  310. package/src/core/ui-packs/fi.ts +13 -0
  311. package/src/core/ui-packs/fr.ts +13 -0
  312. package/src/core/ui-packs/he.ts +13 -0
  313. package/src/core/ui-packs/hi.ts +13 -0
  314. package/src/core/ui-packs/hr.ts +13 -0
  315. package/src/core/ui-packs/hu.ts +13 -0
  316. package/src/core/ui-packs/id.ts +13 -0
  317. package/src/core/ui-packs/it.ts +13 -0
  318. package/src/core/ui-packs/ja.ts +13 -0
  319. package/src/core/ui-packs/ko.ts +13 -0
  320. package/src/core/ui-packs/nl.ts +13 -0
  321. package/src/core/ui-packs/no.ts +13 -0
  322. package/src/core/ui-packs/pl.ts +13 -0
  323. package/src/core/ui-packs/pt-br.ts +13 -0
  324. package/src/core/ui-packs/pt.ts +13 -0
  325. package/src/core/ui-packs/ro.ts +13 -0
  326. package/src/core/ui-packs/ru.ts +13 -0
  327. package/src/core/ui-packs/sk.ts +13 -0
  328. package/src/core/ui-packs/sr.ts +13 -0
  329. package/src/core/ui-packs/sv.ts +13 -0
  330. package/src/core/ui-packs/th.ts +13 -0
  331. package/src/core/ui-packs/tr.ts +13 -0
  332. package/src/core/ui-packs/uk.ts +13 -0
  333. package/src/core/ui-packs/vi.ts +13 -0
  334. package/src/core/ui-packs/zh-tw.ts +13 -0
  335. package/src/core/ui-packs/zh.ts +13 -0
  336. package/src/core/version-cut.ts +17 -2
  337. package/src/core/versions.ts +4 -1
  338. package/src/deploy/adapters/node.ts +5 -2
  339. package/src/deploy/adapters/registry.ts +2 -1
  340. package/src/deploy/adapters/types.ts +13 -1
  341. package/src/deploy/artifacts.ts +25 -5
  342. package/src/deploy/cloudflare-negotiation.ts +23 -4
  343. package/src/deploy/headers.ts +67 -51
  344. package/src/deploy/node-headers.ts +148 -27
  345. package/src/deploy/platforms/cloudflare.ts +1 -0
  346. package/src/deploy/platforms/netlify.ts +82 -5
  347. package/src/deploy/platforms/node.ts +7 -5
  348. package/src/deploy/platforms/static.ts +1 -0
  349. package/src/deploy/platforms/types.ts +7 -0
  350. package/src/deploy/platforms/vercel.ts +9 -3
  351. package/src/deploy/redirects.ts +14 -3
  352. package/src/deploy/vercel-negotiation.ts +35 -3
  353. package/src/eval/agents.ts +10 -2
  354. package/src/eval/run.ts +25 -0
  355. package/src/markdown/base-links.ts +55 -8
  356. package/src/markdown/index.ts +6 -3
  357. package/src/markdown/relative-links.ts +3 -23
  358. package/src/markdown/route-snapshot.ts +37 -0
  359. package/src/og/card.ts +20 -4
  360. package/src/og/derive.ts +145 -4
  361. package/src/openapi/graphql-build.ts +28 -2
  362. package/src/openapi/model.ts +77 -13
  363. package/src/openapi/render-mdx.ts +10 -1
  364. package/src/registry/eject.ts +119 -22
  365. package/src/search/documents.ts +22 -1
  366. package/src/search/sync/algolia.ts +36 -2
  367. package/src/sources/registry.ts +5 -0
  368. package/src/theme/entry.ts +11 -4
  369. package/src/translate/agents.ts +6 -1
  370. package/src/translate/ledger.ts +26 -3
  371. package/src/translate/meta.ts +11 -3
  372. package/src/translate/run.ts +11 -5
  373. package/src/translate/validate.ts +10 -1
  374. package/src/translate/work-list.ts +36 -3
  375. package/src/upgrade/upgrade.ts +36 -4
  376. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  377. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  378. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  379. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  380. package/dist/cli/chunk-82bbrxdn.js +0 -51
  381. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  382. package/dist/cli/chunk-abh8yjkn.js +0 -31
  383. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  384. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  385. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  386. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  387. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  388. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  389. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  390. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  391. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  392. package/dist/cli/chunk-q5163e60.js.map +0 -11
  393. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  394. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  395. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  396. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  397. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  398. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
  399. /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
  400. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
  401. /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
  402. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
  403. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
  404. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
  405. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
  406. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
  407. /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
  408. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
  409. /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
  410. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.js.map} +0 -0
@@ -16,6 +16,10 @@ import {
16
16
  localizeRoute,
17
17
  localizeInternalPath,
18
18
  i18nDiagnostics,
19
+ normalizeRef,
20
+ isPageRef,
21
+ pagesByRef,
22
+ buildNavigation,
19
23
  PUBLIC_API_URL,
20
24
  pageMetaSchema,
21
25
  folderMetaSchema,
@@ -35,7 +39,9 @@ import {
35
39
  parseYouTubeId,
36
40
  nodeRequire,
37
41
  escapeMarkdownText,
42
+ guardBlockStart,
38
43
  renderInline,
44
+ destination,
39
45
  renderLink,
40
46
  headingPrefix,
41
47
  blockquote,
@@ -51,10 +57,15 @@ import {
51
57
  neutralizeUnsafeLinks,
52
58
  columnsPrefix,
53
59
  openApiSource,
60
+ BLUME_IGNORE_DIRS,
61
+ baselineScanIgnore,
62
+ BLUME_WATCH_IGNORE_DIRS,
63
+ excludeDirSegments,
64
+ ignoringWatchListener,
54
65
  createModuleLoader,
55
66
  resolveProjectContext,
56
67
  loadConfig
57
- } from "./chunk-qkqwkpte.js";
68
+ } from "./chunk-v6ya5kcb.js";
58
69
  import {
59
70
  getBlumeVersion,
60
71
  BlumeError,
@@ -64,15 +75,8 @@ import {
64
75
  trimChar,
65
76
  isInternalPath,
66
77
  withBasePath,
67
- stripBasePath
68
- } from "./chunk-jts8mvcz.js";
69
- import {
70
- BLUME_IGNORE_DIRS,
71
- baselineScanIgnore,
72
- BLUME_WATCH_IGNORE_DIRS,
73
- excludeDirSegments,
74
- ignoringWatchListener
75
- } from "./chunk-abh8yjkn.js";
78
+ mountBasePath
79
+ } from "./chunk-gs7r695n.js";
76
80
 
77
81
  // src/core/manifest.ts
78
82
  var versionAlternateKey = (versionKey, locale) => `${versionKey}\x00${locale}`;
@@ -105,7 +109,7 @@ var buildFallbackRoutes = (graph, i18n, alternatesByKey, basePath, versionAltern
105
109
  if (present.has(key)) {
106
110
  continue;
107
111
  }
108
- const path = withBasePath(basePath, localizeRoute(key, code, i18n));
112
+ const path = mountBasePath(basePath, localizeRoute(key, code, i18n));
109
113
  const versionAlternates = versionAlternatesByKey ? versionAlternatesFor(versionAlternatesByKey, source.versionKey, code) : [];
110
114
  if (versionAlternatesByKey) {
111
115
  versionAlternates.push({ path, version: source.version });
@@ -155,6 +159,7 @@ var buildManifest = (options) => {
155
159
  }
156
160
  }
157
161
  const routes = graph.pages.map((page) => ({
162
+ aiExclude: page.meta.ai.exclude,
158
163
  alternates: alternatesByKey.get(page.translationKey) ?? [],
159
164
  collection: page.collection ?? "docs",
160
165
  contentType: page.contentType,
@@ -315,7 +320,7 @@ var validateNavTargets = (navigation, routes) => {
315
320
  if (!isInternalPath(path) || path.startsWith("/#") || seen.has(path)) {
316
321
  continue;
317
322
  }
318
- if (!resolvesToPages(routes, path.split("#")[0] ?? path)) {
323
+ if (!resolvesToPages(routes, path.split(/[?#]/u)[0] ?? path)) {
319
324
  seen.add(path);
320
325
  diagnostics.push({
321
326
  code: "BLUME_NAV_MISSING_PAGE",
@@ -327,6 +332,44 @@ var validateNavTargets = (navigation, routes) => {
327
332
  }
328
333
  return diagnostics;
329
334
  };
335
+ var validateSidebarItems = (items, resolves) => {
336
+ const diagnostics = [];
337
+ const walk = (list) => {
338
+ for (const item of list) {
339
+ if (isPageRef(item)) {
340
+ if (!resolves(normalizeRef(item))) {
341
+ diagnostics.push({
342
+ code: "BLUME_NAV_MISSING_PAGE",
343
+ message: `Sidebar entry "${item}" matches no page, so it's left out of the sidebar.`,
344
+ severity: "warning",
345
+ suggestion: "Fix the route, or add a page at it."
346
+ });
347
+ }
348
+ continue;
349
+ }
350
+ if (item.root && !resolves(normalizeRef(item.root))) {
351
+ diagnostics.push({
352
+ code: "BLUME_NAV_MISSING_PAGE",
353
+ message: `Sidebar entry "${item.label}" has root "${item.root}", but no page matches it, so its link leads nowhere.`,
354
+ severity: "warning",
355
+ suggestion: "Fix the root, or add a page at that route."
356
+ });
357
+ }
358
+ if (item.items) {
359
+ walk(item.items);
360
+ } else if (!(item.root || item.href)) {
361
+ diagnostics.push({
362
+ code: "BLUME_NAV_EMPTY_ITEM",
363
+ message: `Sidebar entry "${item.label}" has no page, href, root, or items, so it's left out of the sidebar.`,
364
+ severity: "warning",
365
+ suggestion: "Give it an href or a root to link somewhere, or items to make it a group."
366
+ });
367
+ }
368
+ }
369
+ };
370
+ walk(items);
371
+ return diagnostics;
372
+ };
330
373
  var duplicateLabelDiagnostics = (navigation) => {
331
374
  const diagnostics = [];
332
375
  const checkLevel = (nodes, where) => {
@@ -386,541 +429,6 @@ var validateNavStructure = (navigation, pages) => [
386
429
  ...hiddenInSidebarDiagnostics(navigation, pages)
387
430
  ];
388
431
 
389
- // src/core/navigation.ts
390
- import { extname } from "pathe";
391
- var NUMERIC_PREFIX = /^(?<order>\d+)[-_.]/u;
392
- var GROUP_FOLDER = /^\((?<label>.+)\)$/u;
393
- var WORD_SPLIT = /[-_]/u;
394
- var isUnderPath = (route, base) => base === "/" || route === base || route.startsWith(`${base}/`);
395
- var isRootTab = (tab, root) => isUnderPath(root, tab.path);
396
- var sidebarIcon = (page) => page.meta.sidebar.icon ?? page.meta.icon;
397
- var WORD_FORMS = new Map([
398
- ["ai", "AI"],
399
- ["api", "API"],
400
- ["apis", "APIs"],
401
- ["asyncapi", "AsyncAPI"],
402
- ["cli", "CLI"],
403
- ["css", "CSS"],
404
- ["faq", "FAQ"],
405
- ["faqs", "FAQs"],
406
- ["graphql", "GraphQL"],
407
- ["html", "HTML"],
408
- ["http", "HTTP"],
409
- ["https", "HTTPS"],
410
- ["id", "ID"],
411
- ["ids", "IDs"],
412
- ["ios", "iOS"],
413
- ["js", "JS"],
414
- ["json", "JSON"],
415
- ["jwt", "JWT"],
416
- ["llm", "LLM"],
417
- ["llms", "LLMs"],
418
- ["macos", "macOS"],
419
- ["mcp", "MCP"],
420
- ["oauth", "OAuth"],
421
- ["openapi", "OpenAPI"],
422
- ["rss", "RSS"],
423
- ["sdk", "SDK"],
424
- ["sdks", "SDKs"],
425
- ["seo", "SEO"],
426
- ["sql", "SQL"],
427
- ["sso", "SSO"],
428
- ["ts", "TS"],
429
- ["ui", "UI"],
430
- ["url", "URL"],
431
- ["urls", "URLs"],
432
- ["xml", "XML"],
433
- ["yaml", "YAML"]
434
- ]);
435
- var humanize = (segment) => segment.replace(NUMERIC_PREFIX, "").split(WORD_SPLIT).filter(Boolean).map((word) => WORD_FORMS.get(word) ?? word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
436
- var numericOrder = (segment) => {
437
- const value = segment.match(NUMERIC_PREFIX)?.groups?.order;
438
- return value ? Math.trunc(Number(value)) : Number.POSITIVE_INFINITY;
439
- };
440
- var segmentKey = (raw) => {
441
- const group = raw.match(GROUP_FOLDER)?.groups?.label;
442
- return (group ?? raw).replace(NUMERIC_PREFIX, "");
443
- };
444
- var isIndexStem = (stem) => stem.replace(NUMERIC_PREFIX, "") === "index";
445
- var stemOf = (filename) => filename.replace(extname(filename), "");
446
- var navStem = (navPath) => stemOf(navPath.split("/").at(-1) ?? navPath);
447
- var createGroup = (key, path, label, order) => ({
448
- children: [],
449
- index: new Map,
450
- key,
451
- kind: "group",
452
- label,
453
- order,
454
- path
455
- });
456
- var ensureGroup = (parent, rawSegment) => {
457
- const existing = parent.index.get(rawSegment);
458
- if (existing) {
459
- return existing;
460
- }
461
- const path = parent.path ? `${parent.path}/${rawSegment}` : rawSegment;
462
- const group = createGroup(segmentKey(rawSegment), path, humanize(rawSegment.match(GROUP_FOLDER)?.groups?.label ?? rawSegment), numericOrder(rawSegment));
463
- parent.index.set(rawSegment, group);
464
- parent.children.push(group);
465
- return group;
466
- };
467
- var pageOrder = (page, filename) => {
468
- if (page.meta.sidebar.order !== undefined) {
469
- return { order: page.meta.sidebar.order, orderIsAuthored: true };
470
- }
471
- if (isIndexStem(stemOf(filename))) {
472
- return { order: Number.NEGATIVE_INFINITY, orderIsAuthored: false };
473
- }
474
- if (page.contentType === "changelog") {
475
- const iso = page.meta.date ?? page.meta.changelog?.date;
476
- const time = iso ? Date.parse(iso) : Number.NaN;
477
- if (!Number.isNaN(time)) {
478
- return { order: -time, orderIsAuthored: false };
479
- }
480
- }
481
- const order = numericOrder(filename);
482
- return {
483
- order,
484
- orderIsAuthored: page.contentType !== "changelog" && Number.isFinite(order)
485
- };
486
- };
487
- var metaKey = (path, metaPrefix) => {
488
- if (!metaPrefix) {
489
- return path;
490
- }
491
- return path ? `${metaPrefix}/${path}` : metaPrefix;
492
- };
493
- var applyFolderMeta = (group, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay, indexRoute) => {
494
- group.route = indexRoute.get(group.path);
495
- const meta = folderMeta.get(metaKey(group.path, metaPrefix)) ?? sharedMeta.get(metaKey(group.path, sharedMetaPrefix));
496
- group.display = indexDisplay.get(group.path) ?? meta?.display;
497
- if (meta) {
498
- group.label = meta.title ?? group.label;
499
- group.icon = meta.icon ?? group.icon;
500
- group.order = meta.order ?? group.order;
501
- group.collapsed = meta.collapsed ?? group.collapsed;
502
- if (meta.pages) {
503
- const rank = new Map(meta.pages.map((key, i) => [key, i]));
504
- for (const child of group.children) {
505
- const position = rank.get(child.key);
506
- if (position !== undefined) {
507
- child.order = position;
508
- if (child.kind === "page") {
509
- child.orderIsAuthored = true;
510
- }
511
- }
512
- }
513
- }
514
- }
515
- for (const child of group.children) {
516
- if (child.kind === "group") {
517
- applyFolderMeta(child, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay, indexRoute);
518
- }
519
- }
520
- };
521
- var indexTitleMismatchDiagnostic = (page, folderPath, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix) => {
522
- if (!(page.meta.title && page.meta.sidebar.hidden) || page.fallback || folderPath === "") {
523
- return;
524
- }
525
- const meta = folderMeta.get(metaKey(folderPath, metaPrefix)) ?? sharedMeta.get(metaKey(folderPath, sharedMetaPrefix));
526
- if (!meta?.title || meta.title === page.title) {
527
- return;
528
- }
529
- return {
530
- code: "BLUME_NAV_INDEX_TITLE_MISMATCH",
531
- file: page.sourcePath ?? page.id,
532
- message: `Index page "${page.navPath}" has title "${page.title}", but its folder's meta.title is "${meta.title}" — the sidebar shows the folder title while the page's own <title>/heading still say "${page.title}".`,
533
- severity: "warning",
534
- suggestion: `Update the page's frontmatter title to match ("${meta.title}"), or leave it if the divergence is intentional.`
535
- };
536
- };
537
- var sidebarDisplayIgnoredDiagnostics = (pages, explicitSidebar) => {
538
- const diagnostics = [];
539
- for (const page of pages) {
540
- if (!page.meta.sidebar.display) {
541
- continue;
542
- }
543
- const isIndex = isIndexStem(navStem(page.navPath));
544
- if (explicitSidebar) {
545
- diagnostics.push({
546
- code: "BLUME_SIDEBAR_DISPLAY_IGNORED",
547
- file: page.sourcePath ?? page.id,
548
- message: `"${page.navPath}" sets sidebar.display, but the explicit navigation.sidebar config owns each group's display mode — the value is ignored.`,
549
- severity: "warning",
550
- suggestion: "Set display on the matching group in navigation.sidebar, or remove the frontmatter key."
551
- });
552
- continue;
553
- }
554
- if (isIndex && !page.navPath.includes("/")) {
555
- diagnostics.push({
556
- code: "BLUME_SIDEBAR_DISPLAY_IGNORED",
557
- file: page.sourcePath ?? page.id,
558
- message: `"${page.navPath}" sets sidebar.display, but the content root is not a sidebar group — the value is ignored.`,
559
- severity: "warning",
560
- suggestion: "Set navigation.sidebar.display in blume.config to change the sidebar-wide mode."
561
- });
562
- continue;
563
- }
564
- if (isIndex) {
565
- continue;
566
- }
567
- diagnostics.push({
568
- code: "BLUME_SIDEBAR_DISPLAY_IGNORED",
569
- file: page.sourcePath ?? page.id,
570
- message: `"${page.navPath}" sets sidebar.display, but only a folder's index page can set its group's display mode — the value is ignored.`,
571
- severity: "warning",
572
- suggestion: "Move display to the folder's index page frontmatter, or set it in the folder's meta.ts."
573
- });
574
- }
575
- return diagnostics;
576
- };
577
- var isAuthoredOrder = (node) => node.kind === "group" || node.orderIsAuthored;
578
- var duplicateOrderDiagnostics = (nodes) => {
579
- const byOrder = new Map;
580
- for (const node of nodes) {
581
- if (!Number.isFinite(node.order) || !isAuthoredOrder(node)) {
582
- continue;
583
- }
584
- const tied = byOrder.get(node.order);
585
- if (tied) {
586
- tied.push(node);
587
- } else {
588
- byOrder.set(node.order, [node]);
589
- }
590
- }
591
- const diagnostics = [];
592
- for (const [order, tied] of byOrder) {
593
- if (tied.length > 1) {
594
- const names = tied.map((node) => `"${node.label}"`);
595
- const list = names.length > 2 ? `${names.slice(0, -1).join(", ")}, and ${names.at(-1)}` : names.join(" and ");
596
- const verb = tied.length > 2 ? "all have" : "both have";
597
- const file = tied.find((node) => node.kind === "page")?.file;
598
- diagnostics.push({
599
- code: "BLUME_DUPLICATE_SIDEBAR_ORDER",
600
- file,
601
- message: `${list} ${verb} sidebar order ${order}; falling back to alphabetical order.`,
602
- severity: "warning",
603
- suggestion: "Give each item a distinct sidebar.order (or folder meta order)."
604
- });
605
- }
606
- }
607
- return diagnostics;
608
- };
609
- var sortNodes = (nodes, diagnostics) => {
610
- diagnostics.push(...duplicateOrderDiagnostics(nodes));
611
- nodes.sort((a, b) => {
612
- if (a.order !== b.order) {
613
- return a.order - b.order;
614
- }
615
- return a.label.localeCompare(b.label);
616
- });
617
- for (const node of nodes) {
618
- if (node.kind === "group") {
619
- sortNodes(node.children, diagnostics);
620
- }
621
- }
622
- };
623
- var hoistPages = (nodes, display, hoist) => {
624
- const groups = nodes.filter((node) => node.kind === "group");
625
- if (hoist || groups.some((group) => (group.display ?? display) === "flat")) {
626
- const pages = nodes.filter((node) => node.kind === "page");
627
- nodes.splice(0, nodes.length, ...pages, ...groups);
628
- }
629
- for (const group of groups) {
630
- hoistPages(group.children, display, false);
631
- }
632
- };
633
- var hoistTabSections = (nodes, tabPaths, display) => {
634
- for (const node of nodes) {
635
- if (node.kind !== "group") {
636
- continue;
637
- }
638
- if (node.routePath !== undefined && tabPaths.has(node.routePath)) {
639
- hoistPages(node.children, display, true);
640
- }
641
- hoistTabSections(node.children, tabPaths, display);
642
- }
643
- };
644
- var toNavNode = (node, display) => {
645
- if (node.kind === "page") {
646
- return {
647
- badge: node.badge,
648
- deprecated: node.deprecated || undefined,
649
- description: node.description,
650
- icon: node.icon,
651
- kind: "page",
652
- label: node.label,
653
- pageId: node.pageId,
654
- route: node.route
655
- };
656
- }
657
- return {
658
- children: node.children.map((child) => toNavNode(child, display)),
659
- collapsed: node.collapsed,
660
- display: node.display ?? display,
661
- icon: node.icon,
662
- kind: "group",
663
- label: node.label,
664
- path: node.routePath,
665
- route: node.route
666
- };
667
- };
668
- var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, display, tabPaths, diagnostics = []) => {
669
- const root = createGroup("", "", "", 0);
670
- const indexDisplay = new Map;
671
- const indexRoute = new Map;
672
- for (const page of pages) {
673
- const parts = page.navPath.split("/");
674
- const filename = parts.at(-1) ?? page.navPath;
675
- const stem = stemOf(filename);
676
- const dirs = parts.slice(0, -1);
677
- if (isIndexStem(stem)) {
678
- const diagnostic = indexTitleMismatchDiagnostic(page, dirs.join("/"), folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix);
679
- if (diagnostic) {
680
- diagnostics.push(diagnostic);
681
- }
682
- if (page.meta.sidebar.display && !page.fallback) {
683
- indexDisplay.set(dirs.join("/"), page.meta.sidebar.display);
684
- }
685
- if (dirs.length > 0) {
686
- indexRoute.set(dirs.join("/"), page.route);
687
- }
688
- }
689
- if (page.meta.sidebar.hidden) {
690
- continue;
691
- }
692
- const routeSegments = page.route.split("/").filter(Boolean);
693
- const folderParts = isIndexStem(stem) ? routeSegments : routeSegments.slice(0, -1);
694
- const routeDirCount = dirs.filter((dir) => !GROUP_FOLDER.test(dir)).length;
695
- const offset = Math.max(0, folderParts.length - routeDirCount);
696
- let parent = root;
697
- let consumed = offset;
698
- for (const dir of dirs) {
699
- parent = ensureGroup(parent, dir);
700
- if (!GROUP_FOLDER.test(dir)) {
701
- consumed += 1;
702
- }
703
- parent.routePath ??= `/${folderParts.slice(0, consumed).join("/")}`;
704
- }
705
- const { order, orderIsAuthored } = pageOrder(page, filename);
706
- parent.children.push({
707
- badge: page.meta.sidebar.badge,
708
- deprecated: page.meta.deprecated || undefined,
709
- description: page.description,
710
- file: page.sourcePath,
711
- icon: sidebarIcon(page),
712
- key: segmentKey(stem),
713
- kind: "page",
714
- label: page.meta.sidebar.label ?? page.title,
715
- order,
716
- orderIsAuthored,
717
- pageId: page.id,
718
- route: page.route
719
- });
720
- }
721
- applyFolderMeta(root, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay, indexRoute);
722
- sortNodes(root.children, diagnostics);
723
- hoistPages(root.children, display, true);
724
- hoistTabSections(root.children, tabPaths, display);
725
- return root.children.map((child) => toNavNode(child, display));
726
- };
727
- var normalizeRef = (ref) => {
728
- if (ref === "index") {
729
- return "/";
730
- }
731
- const withSlash = ref.startsWith("/") ? ref : `/${ref}`;
732
- const noTrailing = withSlash.replace(/\/+$/u, "");
733
- const trimmed = noTrailing.endsWith("/index") ? noTrailing.slice(0, -"/index".length) : noTrailing;
734
- return trimmed === "" ? "/" : trimmed;
735
- };
736
- var routeForRef = (ref, byRoute, basePath) => {
737
- if (!ref) {
738
- return;
739
- }
740
- const normalized = normalizeRef(ref);
741
- return byRoute.get(normalized)?.route ?? withBasePath(basePath, normalized);
742
- };
743
- var isPageRef = (item) => typeof item === "string";
744
- var configItemToNode = (item, byRoute, basePath) => {
745
- if (isPageRef(item)) {
746
- const page = byRoute.get(normalizeRef(item));
747
- if (!page) {
748
- return null;
749
- }
750
- return {
751
- badge: page.meta.sidebar.badge,
752
- deprecated: page.meta.deprecated || undefined,
753
- description: page.description,
754
- icon: sidebarIcon(page),
755
- kind: "page",
756
- label: page.meta.sidebar.label ?? page.title,
757
- pageId: page.id,
758
- route: page.route
759
- };
760
- }
761
- if (item.root) {
762
- const page = byRoute.get(normalizeRef(item.root));
763
- return {
764
- badge: item.badge,
765
- deprecated: page?.meta.deprecated || undefined,
766
- icon: item.icon,
767
- kind: "page",
768
- label: item.label,
769
- pageId: page?.id ?? "",
770
- route: page?.route ?? withBasePath(basePath, normalizeRef(item.root))
771
- };
772
- }
773
- if (item.href) {
774
- return {
775
- badge: item.badge,
776
- icon: item.icon,
777
- kind: "page",
778
- label: item.label,
779
- pageId: "",
780
- route: withBasePath(basePath, item.href)
781
- };
782
- }
783
- return null;
784
- };
785
- var buildConfigSidebar = (items, byRoute, display, basePath) => {
786
- const nodes = [];
787
- for (const item of items) {
788
- if (!isPageRef(item) && item.items) {
789
- nodes.push({
790
- badge: item.badge,
791
- children: buildConfigSidebar(item.items, byRoute, display, basePath),
792
- collapsed: item.collapsed,
793
- directory: item.directory,
794
- display: item.display ?? display,
795
- icon: item.icon,
796
- kind: "group",
797
- label: item.label,
798
- route: routeForRef(item.root, byRoute, basePath)
799
- });
800
- continue;
801
- }
802
- const node = configItemToNode(item, byRoute, basePath);
803
- if (node) {
804
- nodes.push(node);
805
- }
806
- }
807
- return nodes;
808
- };
809
- var resolveTabHref = (sidebar, path, extraRoutes, basePath) => {
810
- if (extraRoutes.has(path)) {
811
- return path;
812
- }
813
- let first;
814
- const walk = (nodes) => {
815
- for (const node of nodes) {
816
- const { route } = node;
817
- if (route === path) {
818
- return true;
819
- }
820
- if (first === undefined && route !== undefined && route.startsWith(`${path}/`)) {
821
- first = route;
822
- }
823
- if (node.kind === "group" && walk(node.children)) {
824
- return true;
825
- }
826
- }
827
- return false;
828
- };
829
- if (walk(sidebar)) {
830
- return path;
831
- }
832
- const bare = stripBasePath(basePath, path);
833
- return extraRoutes.has(bare) ? bare : first ?? path;
834
- };
835
- var withTabHrefs = (tabs, sidebar, extraRoutes, basePath) => tabs.map((tab) => {
836
- const href = tab.href ?? resolveTabHref(sidebar, tab.path, extraRoutes, basePath);
837
- return href === tab.path ? tab : { ...tab, href };
838
- });
839
- var rebaseNavChrome = (basePath, options) => {
840
- const actions = options.actions ?? [];
841
- const cta = options.cta ?? null;
842
- const featured = options.featured ?? [];
843
- const selectors = options.selectors ?? [];
844
- const tabs = options.tabs ?? [];
845
- if (!basePath) {
846
- return { actions, cta, featured, selectors, tabs };
847
- }
848
- const rebaseHref = (item) => ({
849
- ...item,
850
- href: withBasePath(basePath, item.href)
851
- });
852
- const rebasePath = (item) => ({
853
- ...item,
854
- path: withBasePath(basePath, item.path)
855
- });
856
- return {
857
- actions: actions.map(rebaseHref),
858
- cta: cta ? rebaseHref(cta) : null,
859
- featured: featured.map(rebaseHref),
860
- selectors: selectors.map((selector) => ({
861
- ...selector,
862
- items: selector.items.map(rebasePath)
863
- })),
864
- tabs: tabs.map((tab) => {
865
- const rebased = {
866
- ...tab,
867
- items: tab.items?.map(rebasePath),
868
- path: withBasePath(basePath, tab.path)
869
- };
870
- if (tab.href) {
871
- rebased.href = withBasePath(basePath, tab.href);
872
- }
873
- return rebased;
874
- })
875
- };
876
- };
877
- var buildNavigation = (pages, options) => {
878
- const basePath = options.basePath ?? "";
879
- const display = options.display ?? "flat";
880
- const metaPrefix = options.metaPrefix ?? "";
881
- const sharedMetaPrefix = options.sharedMetaPrefix ?? "";
882
- const sharedFolderMeta = options.sharedFolderMeta ?? new Map;
883
- const extraRoutes = options.extraRoutes ?? new Set;
884
- const { actions, cta, featured, selectors, tabs } = rebaseNavChrome(basePath, options);
885
- const byRoute = new Map(pages.map((page) => [
886
- options.refByLogical ? page.translationKey : page.route,
887
- page
888
- ]));
889
- if (basePath && !options.refByLogical) {
890
- for (const page of pages) {
891
- const bare = stripBasePath(basePath, page.route);
892
- if (!byRoute.has(bare)) {
893
- byRoute.set(bare, page);
894
- }
895
- }
896
- }
897
- const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
898
- const diagnostics = options.diagnostics ?? [];
899
- diagnostics.push(...sidebarDisplayIgnoredDiagnostics(pages, Boolean(options.sidebar)));
900
- if (options.sidebar) {
901
- const sidebar = buildConfigSidebar(options.sidebar, byRoute, display, basePath);
902
- return {
903
- actions,
904
- cta,
905
- featured,
906
- root: rootTabPath,
907
- selectors,
908
- sidebar,
909
- tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath)
910
- };
911
- }
912
- const sidebar = buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, sharedMetaPrefix, display, new Set(tabs.flatMap((tab) => isRootTab(tab, rootTabPath) ? [] : [tab.path])), diagnostics);
913
- return {
914
- actions,
915
- cta,
916
- featured,
917
- root: rootTabPath,
918
- selectors,
919
- sidebar,
920
- tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath)
921
- };
922
- };
923
-
924
432
  // src/core/graph.ts
925
433
  var collectRoutes = (pages) => {
926
434
  const routes = new Map;
@@ -953,7 +461,7 @@ var localePagesFor = (code, real, fallback, fallbackByKey, i18n, basePath) => {
953
461
  ...source,
954
462
  fallback: true,
955
463
  locale: code,
956
- route: withBasePath(basePath, localizeRoute(key, code, i18n))
464
+ route: mountBasePath(basePath, localizeRoute(key, code, i18n))
957
465
  });
958
466
  }
959
467
  }
@@ -966,6 +474,19 @@ var resolveLabel = (label, locale, defaultLocale) => {
966
474
  }
967
475
  return label[locale] ?? (defaultLocale === undefined ? undefined : label[defaultLocale]) ?? Object.values(label)[0] ?? "";
968
476
  };
477
+ var localizeSidebarHrefs = (items, localize) => items.map((item) => {
478
+ if (isPageRef(item)) {
479
+ return item;
480
+ }
481
+ const localized = { ...item };
482
+ if (item.href !== undefined) {
483
+ localized.href = localize(item.href);
484
+ }
485
+ if (item.items) {
486
+ localized.items = localizeSidebarHrefs(item.items, localize);
487
+ }
488
+ return localized;
489
+ });
969
490
  var resolveTabLabels = (tabs, locale, defaultLocale) => (tabs ?? []).map((tab) => ({
970
491
  ...tab,
971
492
  items: tab.items?.map((item) => ({
@@ -974,6 +495,7 @@ var resolveTabLabels = (tabs, locale, defaultLocale) => (tabs ?? []).map((tab) =
974
495
  })),
975
496
  label: resolveLabel(tab.label, locale, defaultLocale)
976
497
  }));
498
+ var mountConfiguredLink = (path, basePath, extraRoutes) => extraRoutes.has(path) ? path : withBasePath(basePath, path);
977
499
  var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n, diagnostics, version = "") => {
978
500
  const basePath = options.basePath ?? "";
979
501
  const real = pages.filter((page) => page.locale === code);
@@ -1009,12 +531,15 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
1009
531
  }
1010
532
  return localized;
1011
533
  });
1012
- const localeDir = i18n.parser === "dir" && code !== i18n.defaultLocale ? code : "";
534
+ const localeDirOf = (locale) => i18n.parser === "dir" && locale !== i18n.defaultLocale ? locale : "";
535
+ const localeDir = localeDirOf(code);
536
+ const fallbackMetaPrefix = fallback && code !== fallback ? [version, localeDirOf(fallback)].filter(Boolean).join("/") : undefined;
1013
537
  const localizeHref = (item) => ({
1014
538
  ...item,
1015
539
  href: localizeServed(item.href, false)
1016
540
  });
1017
541
  const { actions, cta, featured } = options.navigation;
542
+ const sidebarItems = options.navigation.sidebar.items;
1018
543
  const navigation = buildNavigation(localePages, {
1019
544
  actions: actions?.map(localizeHref),
1020
545
  basePath,
@@ -1022,6 +547,7 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
1022
547
  diagnostics,
1023
548
  display: options.navigation.sidebar.display,
1024
549
  extraRoutes: options.extraRoutes,
550
+ fallbackMetaPrefix,
1025
551
  featured: featured?.map(localizeHref),
1026
552
  folderMeta: options.folderMeta,
1027
553
  localizedRoot: localizeRoute(versionizeRoute("/", version), code, i18n),
@@ -1030,10 +556,19 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
1030
556
  selectors: options.navigation.selectors,
1031
557
  sharedFolderMeta: options.sharedFolderMeta,
1032
558
  sharedMetaPrefix: version,
1033
- sidebar: version ? undefined : options.navigation.sidebar.items,
559
+ sidebar: version || !sidebarItems ? undefined : localizeSidebarHrefs(sidebarItems, (href) => localizeServed(href, false)),
560
+ tabRoot: localizeRoute("/", code, i18n),
1034
561
  tabs
1035
562
  });
1036
- return options.brandHref === undefined ? navigation : { ...navigation, brandHref: localizeServed(options.brandHref, false) };
563
+ const localizedBrand = (href) => {
564
+ const localized = localizeServed(href, false);
565
+ return localized === href ? localized : withBasePath(basePath, localized);
566
+ };
567
+ const branded = options.brandHref === undefined ? navigation : { ...navigation, brandHref: localizedBrand(options.brandHref) };
568
+ return options.bannerHref === undefined ? branded : {
569
+ ...branded,
570
+ bannerHref: mountConfiguredLink(localizeServed(options.bannerHref, false), basePath, extraRoutes)
571
+ };
1037
572
  };
1038
573
  var buildI18nNavigation = (pages, options, i18n, diagnostics, version = "") => {
1039
574
  const fallback = resolveFallbackLocale(i18n);
@@ -1088,6 +623,7 @@ var buildVersionNavigation = (id, versionPages, options, diagnostics) => {
1088
623
  selectors: options.navigation.selectors,
1089
624
  sharedFolderMeta: options.sharedFolderMeta,
1090
625
  sharedMetaPrefix: id,
626
+ tabRoot: "/",
1091
627
  tabs: resolveTabLabels(options.navigation.tabs, "")
1092
628
  })
1093
629
  };
@@ -1115,12 +651,24 @@ var buildContentGraph = (pages, options) => {
1115
651
  sidebar: options.navigation.sidebar.items,
1116
652
  tabs: resolveTabLabels(options.navigation.tabs, "")
1117
653
  });
654
+ if (options.bannerHref !== undefined) {
655
+ navigation = {
656
+ ...navigation,
657
+ bannerHref: mountConfiguredLink(options.bannerHref, options.basePath ?? "", options.extraRoutes ?? new Set)
658
+ };
659
+ }
1118
660
  }
1119
661
  const navigationByVersion = {};
1120
662
  for (const { id } of options.versions?.archived ?? []) {
1121
663
  navigationByVersion[id] = buildVersionNavigation(id, pages.filter((page) => page.version === id), options, diagnostics);
1122
664
  }
1123
665
  diagnostics.push(...validateNavIcons(navigation), ...validateNavStructure(navigation, currentPages));
666
+ const sidebarItems = options.navigation.sidebar.items;
667
+ if (sidebarItems) {
668
+ const byRef = pagesByRef(currentPages, options.basePath ?? "", Boolean(i18n));
669
+ const extraRoutes = options.extraRoutes ?? new Set;
670
+ diagnostics.push(...validateSidebarItems(sidebarItems, (route) => byRef.has(route) || extraRoutes.has(route)));
671
+ }
1124
672
  return {
1125
673
  diagnostics,
1126
674
  navigation,
@@ -1224,7 +772,7 @@ var lastModifiedShallowWarning = (root, undatedCount) => {
1224
772
 
1225
773
  // src/core/meta.ts
1226
774
  import { basename, dirname, relative as relative2 } from "pathe";
1227
- import { glob } from "tinyglobby";
775
+ import { glob, isDynamicPattern } from "tinyglobby";
1228
776
  var META_FILES = [
1229
777
  "**/meta.ts",
1230
778
  "**/meta.js",
@@ -1235,6 +783,20 @@ var META_FILES = [
1235
783
  ];
1236
784
  var isMetaFactory = (mod) => typeof mod === "function";
1237
785
  var resolveMeta = async (mod) => isMetaFactory(mod) ? await mod() : mod;
786
+ var globBase = (pattern) => {
787
+ const literal = [];
788
+ for (const segment of pattern.split("/").slice(0, -1)) {
789
+ if (isDynamicPattern(segment)) {
790
+ break;
791
+ }
792
+ if (segment !== "" && segment !== ".") {
793
+ literal.push(segment);
794
+ }
795
+ }
796
+ return literal.join("/");
797
+ };
798
+ var onPathOf = (dir, base) => dir === base || base === "" || dir === "" || dir.startsWith(`${base}/`) || base.startsWith(`${dir}/`);
799
+ var withinInclude = (dir, include) => include === undefined || include.some((pattern) => !pattern.startsWith("!") && onPathOf(dir, globBase(pattern)));
1238
800
  var metaKeyFor = (prefix, dir) => {
1239
801
  const clean = prefix ? trimChar(prefix, "/") : "";
1240
802
  if (!clean) {
@@ -1244,19 +806,25 @@ var metaKeyFor = (prefix, dir) => {
1244
806
  };
1245
807
  var discoverFolderMeta = async (sources, options = {}) => {
1246
808
  const list = Array.isArray(sources) ? sources : [{ root: sources }];
1247
- const localeDirs = new Set(options.localeDirs);
809
+ const localeDirs = new Map((options.localeDirs ?? []).map((code) => [code.toLowerCase(), code]));
1248
810
  const versionDirs = new Set(options.versionDirs);
1249
811
  const load = createModuleLoader();
1250
812
  const meta = new Map;
1251
813
  const shared = new Map;
1252
814
  const diagnostics = [];
1253
815
  const perSource = await Promise.all(list.map(async (source) => {
1254
- const files = await glob(META_FILES, {
816
+ const found = await glob(META_FILES, {
1255
817
  absolute: true,
1256
818
  cwd: source.root,
1257
- ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
819
+ ignore: [
820
+ ...source.exclude ?? [],
821
+ "**/node_modules/**",
822
+ "**/.blume/**",
823
+ "**/dist/**"
824
+ ],
1258
825
  onlyFiles: true
1259
826
  });
827
+ const files = found.filter((file) => withinInclude(relative2(source.root, dirname(file)), source.include));
1260
828
  const loaded = await Promise.all(files.map(async (file) => {
1261
829
  try {
1262
830
  return {
@@ -1277,7 +845,7 @@ var discoverFolderMeta = async (sources, options = {}) => {
1277
845
  const version = head && versionDirs.has(head) ? head : "";
1278
846
  const afterVersion = version ? tail : [head ?? "", ...tail];
1279
847
  const [localeHead, ...localeTail] = afterVersion;
1280
- const locale = localeHead && localeDirs.has(localeHead) ? localeHead : "";
848
+ const locale = localeHead ? localeDirs.get(localeHead.toLowerCase()) ?? "" : "";
1281
849
  const rest = (locale ? localeTail : afterVersion).filter(Boolean).join("/");
1282
850
  const key = [version, locale, metaKeyFor(source.prefix, rest)].filter(Boolean).join("/");
1283
851
  if (!entry.ok) {
@@ -1439,11 +1007,11 @@ var renderBlock = (node, options) => {
1439
1007
  const type = asString(node.nodeType) ?? "";
1440
1008
  const heading = HEADING.exec(type)?.groups?.level;
1441
1009
  if (heading) {
1442
- return `${headingPrefix(Number(heading))}${renderInlines(children(node), options)}`;
1010
+ return `${headingPrefix(Number(heading))}${guardBlockStart(renderInlines(children(node), options))}`;
1443
1011
  }
1444
1012
  switch (type) {
1445
1013
  case "paragraph": {
1446
- return renderInlines(children(node), options);
1014
+ return guardBlockStart(renderInlines(children(node), options));
1447
1015
  }
1448
1016
  case "blockquote": {
1449
1017
  return blockquote(joinBlocks(children(node).map((child) => renderBlock(child, options))));
@@ -1480,6 +1048,7 @@ var remoteSource = (options, ctx) => {
1480
1048
  const cache = snapshotCache(ctx?.cacheDir ?? join(".blume", "cache", options.name));
1481
1049
  let snapshot = new Map;
1482
1050
  const load = async (refresh = ctx?.refresh ?? true) => {
1051
+ options.assertConfigured?.();
1483
1052
  const result = await loadWithCache(options.name, cache, options.fetchEntries, refresh);
1484
1053
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
1485
1054
  return result;
@@ -1498,7 +1067,8 @@ var remoteSource = (options, ctx) => {
1498
1067
  prefix: options.prefix,
1499
1068
  read,
1500
1069
  staged: true,
1501
- watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined
1070
+ watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined,
1071
+ withContext: options.withContext
1502
1072
  };
1503
1073
  };
1504
1074
  var stagedEntry = (slug, data, markdown, lastModified, format = "md") => {
@@ -1552,8 +1122,8 @@ var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false)
1552
1122
  let markdown = "";
1553
1123
  let format = "md";
1554
1124
  if (isStringValue2(body)) {
1555
- markdown = `${body.trimEnd()}
1556
- `;
1125
+ markdown = neutralizeUnsafeLinks(`${body.trimEnd()}
1126
+ `);
1557
1127
  } else if (body !== undefined) {
1558
1128
  markdown = lower(body);
1559
1129
  format = lowersToMdx ? "mdx" : "md";
@@ -1588,14 +1158,20 @@ var contentfulSource = (options, ctx) => {
1588
1158
  serializers: options.serializers
1589
1159
  }) : "", false, writesMdx(options.serializers));
1590
1160
  };
1591
- const fetchEntries = async () => {
1592
- const preview = ctx?.preview ?? false;
1593
- const deliveryToken = options.token ?? process.env.CONTENTFUL_ACCESS_TOKEN;
1594
- const previewToken = options.previewToken ?? process.env.CONTENTFUL_PREVIEW_TOKEN;
1595
- if (preview && !previewToken) {
1596
- throw new Error("Contentful --preview needs a Preview API token: set previewToken or CONTENTFUL_PREVIEW_TOKEN.");
1161
+ const preview = ctx?.preview ?? false;
1162
+ const previewToken = () => options.previewToken ?? process.env.CONTENTFUL_PREVIEW_TOKEN;
1163
+ const assertConfigured = () => {
1164
+ if (preview && !previewToken()) {
1165
+ throw new BlumeError({
1166
+ code: "BLUME_SOURCE_MISCONFIGURED",
1167
+ message: `Source "${options.name}": Contentful --preview needs a Preview API token.`,
1168
+ severity: "error",
1169
+ suggestion: "Set previewToken on contentfulSource, or the CONTENTFUL_PREVIEW_TOKEN environment variable."
1170
+ });
1597
1171
  }
1598
- const token = preview ? previewToken : deliveryToken;
1172
+ };
1173
+ const fetchEntries = async () => {
1174
+ const token = preview ? previewToken() : options.token ?? process.env.CONTENTFUL_ACCESS_TOKEN;
1599
1175
  const client = {
1600
1176
  fetchImpl: options.fetchImpl,
1601
1177
  headers: token ? { authorization: `Bearer ${token}` } : {}
@@ -1630,17 +1206,19 @@ var contentfulSource = (options, ctx) => {
1630
1206
  return entries;
1631
1207
  };
1632
1208
  return remoteSource({
1209
+ assertConfigured,
1633
1210
  fetchEntries,
1634
1211
  name: options.name,
1635
1212
  pollInterval: options.pollInterval,
1636
- prefix: options.prefix
1213
+ prefix: options.prefix,
1214
+ withContext: (next) => contentfulSource(options, next)
1637
1215
  }, ctx);
1638
1216
  };
1639
1217
 
1640
1218
  // src/core/sources/filesystem.ts
1641
1219
  import { existsSync, watch as fsWatch } from "node:fs";
1642
1220
  import { readFile } from "node:fs/promises";
1643
- import { extname as extname2, isAbsolute, join as join2, relative as relative3, resolve } from "pathe";
1221
+ import { extname, isAbsolute, join as join2, relative as relative3, resolve } from "pathe";
1644
1222
  import { glob as glob2 } from "tinyglobby";
1645
1223
  var filesystemSource = (options) => {
1646
1224
  const contentRoot = isAbsolute(options.root) ? options.root : join2(resolve(options.projectRoot), options.root);
@@ -1654,7 +1232,7 @@ var filesystemSource = (options) => {
1654
1232
  files.sort();
1655
1233
  const entries = await Promise.all(files.map(async (file) => {
1656
1234
  const source = await readFile(file, "utf-8");
1657
- const ext = extname2(file).toLowerCase();
1235
+ const ext = extname(file).toLowerCase();
1658
1236
  const format = ext === ".mdx" ? "mdx" : "md";
1659
1237
  const parsed = frontmatter_default(source);
1660
1238
  return {
@@ -1674,7 +1252,7 @@ var filesystemSource = (options) => {
1674
1252
  file: contentRoot,
1675
1253
  message: `Content root not found: ${options.root}`,
1676
1254
  severity: "error",
1677
- suggestion: `Create a "${options.root}" folder with at least one .md or .mdx file, or set content.root in blume.config.ts.`
1255
+ suggestion: `Create a "${options.root}" folder with at least one .md or .mdx file, or point Blume at your docs folder: content.root in blume.config.ts, or the filesystem() source's root when you list content.sources.`
1678
1256
  });
1679
1257
  }
1680
1258
  };
@@ -1691,6 +1269,8 @@ var filesystemSource = (options) => {
1691
1269
  };
1692
1270
  return {
1693
1271
  contentRoot,
1272
+ exclude: options.exclude,
1273
+ include: options.include,
1694
1274
  load,
1695
1275
  name: options.name,
1696
1276
  prefix: options.prefix,
@@ -1763,6 +1343,62 @@ var liftHeadings = (body) => {
1763
1343
  }
1764
1344
  return lifted;
1765
1345
  };
1346
+ var WEB_PROTOCOL = /^https?:$/u;
1347
+ var TITLE_SPECIALS = /["\\]/gu;
1348
+ var webOrigin = (site) => {
1349
+ const parsed = site ? URL.parse(site) : null;
1350
+ return parsed && WEB_PROTOCOL.test(parsed.protocol) ? parsed.origin : null;
1351
+ };
1352
+ var titleSuffix = (title) => title ? ` "${title.replaceAll(TITLE_SPECIALS, String.raw`\$&`)}"` : "";
1353
+ var rewrittenLink = (markdown, node, path) => {
1354
+ const target = `${destination(path)}${titleSuffix(node.title)}`;
1355
+ if (node.type === "definition") {
1356
+ return `[${node.label ?? node.identifier}]: ${target}`;
1357
+ }
1358
+ const start = node.position?.start.offset ?? 0;
1359
+ if (markdown[start] !== "[") {
1360
+ return `[${escapeMarkdownText(mdastToString(node))}](${target})`;
1361
+ }
1362
+ const first = node.children.at(0)?.position?.start.offset ?? start + 1;
1363
+ const last = node.children.at(-1)?.position?.end.offset ?? first;
1364
+ return `[${markdown.slice(first, last)}](${target})`;
1365
+ };
1366
+ var relativizeOwnLinks = (markdown, site) => {
1367
+ const origin = webOrigin(site);
1368
+ if (!origin) {
1369
+ return markdown;
1370
+ }
1371
+ const spans = [];
1372
+ const visit = (node) => {
1373
+ if (node.type === "link" || node.type === "definition") {
1374
+ const parsed = URL.parse(node.url);
1375
+ if (parsed?.origin === origin) {
1376
+ spans.push({
1377
+ end: node.position?.end.offset ?? 0,
1378
+ start: node.position?.start.offset ?? 0,
1379
+ text: rewrittenLink(markdown, node, `${parsed.pathname}${parsed.search}${parsed.hash}`)
1380
+ });
1381
+ return;
1382
+ }
1383
+ }
1384
+ if ("children" in node) {
1385
+ for (const child of node.children) {
1386
+ visit(child);
1387
+ }
1388
+ }
1389
+ };
1390
+ visit(fromMarkdown(markdown, {
1391
+ extensions: [gfm()],
1392
+ mdastExtensions: [gfmFromMarkdown()]
1393
+ }));
1394
+ let out = "";
1395
+ let cursor = 0;
1396
+ for (const span of spans) {
1397
+ out += markdown.slice(cursor, span.start) + span.text;
1398
+ cursor = span.end;
1399
+ }
1400
+ return out + markdown.slice(cursor);
1401
+ };
1766
1402
  var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG, "-").replaceAll(EDGE_DASHES, "");
1767
1403
  var githubHeaders = () => {
1768
1404
  const headers = new Headers({ Accept: "application/vnd.github+json" });
@@ -1772,14 +1408,14 @@ var githubHeaders = () => {
1772
1408
  }
1773
1409
  return headers;
1774
1410
  };
1775
- var releaseToEntry = (release) => {
1411
+ var releaseToEntry = (release, site) => {
1776
1412
  const version = release.tag_name.replace(LEADING_V, "");
1777
1413
  const title = release.name?.trim() || release.tag_name;
1778
1414
  const date = release.published_at ?? release.created_at;
1779
1415
  const category = release.prerelease ? "Prerelease" : "Release";
1780
- const body = liftHeadings(neutralizeUnsafeLinks((release.body ?? "").replaceAll(`\r
1416
+ const body = liftHeadings(relativizeOwnLinks(neutralizeUnsafeLinks((release.body ?? "").replaceAll(`\r
1781
1417
  `, `
1782
- `)).trim());
1418
+ `)).trim(), site));
1783
1419
  const description = releaseDescription(body);
1784
1420
  const data = {
1785
1421
  changelog: { category, version },
@@ -1836,7 +1472,7 @@ var githubReleasesSource = (options, ctx) => {
1836
1472
  try {
1837
1473
  const result = await loadWithCache(options.name, cache, async () => {
1838
1474
  const releases = await fetchReleases();
1839
- return releases.map(releaseToEntry);
1475
+ return releases.map((release) => releaseToEntry(release, options.site));
1840
1476
  }, refresh);
1841
1477
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
1842
1478
  return result;
@@ -1870,7 +1506,8 @@ var githubReleasesSource = (options, ctx) => {
1870
1506
  prefix: options.prefix,
1871
1507
  read,
1872
1508
  staged: true,
1873
- watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined
1509
+ watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined,
1510
+ withContext: (next) => githubReleasesSource(options, next)
1874
1511
  };
1875
1512
  };
1876
1513
 
@@ -2013,7 +1650,8 @@ var mdxRemoteSource = (options, ctx) => {
2013
1650
  prefix: options.prefix,
2014
1651
  read,
2015
1652
  staged: true,
2016
- watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined
1653
+ watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined,
1654
+ withContext: (next) => mdxRemoteSource(options, next)
2017
1655
  };
2018
1656
  };
2019
1657
 
@@ -2032,34 +1670,45 @@ import {
2032
1670
  rm,
2033
1671
  writeFile
2034
1672
  } from "node:fs/promises";
2035
- import { extname as extname3, join as join3 } from "pathe";
2036
- var MD_IMAGE = /!\[(?<alt>[^\]]*)\]\((?<url>[^)\s]+)\)/gu;
1673
+ import { extname as extname2, join as join3 } from "pathe";
1674
+ var MD_IMAGE = /!\[(?<alt>(?:\\.|[^\\\]])*)\]\((?:<(?<pointy>(?:\\.|[^\\<>\n])*)>|(?<bare>(?:\\.|[^\\\s)])+))\)/gu;
1675
+ var DESTINATION_ESCAPE = /\\(?<char>[!-/:-@[-`{-~])/gu;
2037
1676
  var HTML_VIDEO_SRC = /(?<open><video\b[^>]*?\ssrc=")(?<url>[^"]+)(?<close>")/gu;
2038
1677
  var REMOTE = /^https?:\/\//u;
2039
- var SAFE_EXT = /^\.[a-z0-9]+$/iu;
2040
1678
  var CODE_FENCE_BLOCK = /^(?<fence>`{3,}|~{3,})[^\n]*\n[\s\S]*?^\k<fence>[^\n]*(?=\n|$)/gmu;
2041
1679
  var FENCE_TOKEN = /\u0000blume-fence-(?<index>\d+)\u0000/gu;
2042
1680
  var EXT_BY_MIME = new Map([
1681
+ ["image/apng", ".apng"],
2043
1682
  ["image/avif", ".avif"],
1683
+ ["image/bmp", ".bmp"],
2044
1684
  ["image/gif", ".gif"],
2045
1685
  ["image/jpeg", ".jpg"],
1686
+ ["image/jpg", ".jpg"],
2046
1687
  ["image/png", ".png"],
2047
1688
  ["image/svg+xml", ".svg"],
1689
+ ["image/tiff", ".tiff"],
1690
+ ["image/vnd.microsoft.icon", ".ico"],
2048
1691
  ["image/webp", ".webp"],
1692
+ ["image/x-icon", ".ico"],
2049
1693
  ["video/mp4", ".mp4"],
2050
1694
  ["video/ogg", ".ogv"],
2051
1695
  ["video/quicktime", ".mov"],
2052
1696
  ["video/webm", ".webm"]
2053
1697
  ]);
2054
- var UNKNOWN_EXT = ".bin";
2055
- var NON_MEDIA_TYPE = /^(?:text\/|application\/(?:[\w.-]+\+)?(?:json|xml|javascript))/u;
1698
+ var MEDIA_EXTS = new Set([...EXT_BY_MIME.values(), ".jpeg"]);
1699
+ var OPAQUE_TYPES = new Set([
1700
+ "",
1701
+ "application/octet-stream",
1702
+ "binary/octet-stream"
1703
+ ]);
2056
1704
  var PART_SUFFIX = ".part";
2057
1705
  var DEFAULT_TIMEOUT_MS = 120000;
2058
1706
  var extFromUrl = (url) => {
2059
1707
  const clean = url.split("?")[0] ?? url;
2060
- const ext = extname3(clean);
2061
- return SAFE_EXT.test(ext) ? ext.toLowerCase() : null;
1708
+ const ext = extname2(clean).toLowerCase();
1709
+ return MEDIA_EXTS.has(ext) ? ext : null;
2062
1710
  };
1711
+ var imageUrl = (pointy, bare) => (pointy ?? bare ?? "").replaceAll(DESTINATION_ESCAPE, "$<char>");
2063
1712
  var mediaType = (res) => res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() ?? "";
2064
1713
  var exists = async (path) => {
2065
1714
  try {
@@ -2076,9 +1725,16 @@ var completedFor = async (dir, stem) => {
2076
1725
  } catch {
2077
1726
  return null;
2078
1727
  }
2079
- const candidates = names.filter((name) => name.startsWith(`${stem}.`) && !name.endsWith(PART_SUFFIX));
1728
+ const candidates = names.filter((name) => name.startsWith(stem) && MEDIA_EXTS.has(name.slice(stem.length)));
2080
1729
  return candidates.length === 1 ? candidates[0] ?? null : null;
2081
1730
  };
1731
+ var extFor = (type, urlExt) => {
1732
+ const ext = EXT_BY_MIME.get(type) ?? (OPAQUE_TYPES.has(type) ? urlExt : null);
1733
+ if (ext) {
1734
+ return ext;
1735
+ }
1736
+ throw new Error(OPAQUE_TYPES.has(type) ? `responded with ${type || "no content type"} and its URL names no image or video file` : `responded with ${type}, not an image or video`);
1737
+ };
2082
1738
  var materializeAssets = async (markdown, ctx) => {
2083
1739
  const doFetch = ctx.fetchImpl ?? globalThis.fetch;
2084
1740
  const limit = ctx.limit ?? ((task) => task());
@@ -2090,21 +1746,17 @@ var materializeAssets = async (markdown, ctx) => {
2090
1746
  fences.push(block);
2091
1747
  return `\x00blume-fence-${fences.length - 1}\x00`;
2092
1748
  });
2093
- const urls = new Set;
2094
- for (const pattern of [MD_IMAGE, HTML_VIDEO_SRC]) {
2095
- for (const match of masked.matchAll(pattern)) {
2096
- const url = match.groups?.url;
2097
- if (url && REMOTE.test(url)) {
2098
- urls.add(url);
2099
- }
2100
- }
2101
- }
1749
+ const referenced = [
1750
+ ...[...masked.matchAll(MD_IMAGE)].map((match) => imageUrl(match.groups?.pointy, match.groups?.bare)),
1751
+ ...[...masked.matchAll(HTML_VIDEO_SRC)].map((match) => match.groups?.url ?? "")
1752
+ ];
1753
+ const urls = new Set(referenced.filter((url) => REMOTE.test(url)));
2102
1754
  const download = async (url, stem) => {
2103
1755
  const urlExt = extFromUrl(url);
2104
1756
  if (urlExt && await exists(join3(ctx.assetsDir, `${stem}${urlExt}`))) {
2105
1757
  return `${stem}${urlExt}`;
2106
1758
  }
2107
- const completed = urlExt ? null : await completedFor(ctx.assetsDir, stem);
1759
+ const completed = await completedFor(ctx.assetsDir, stem);
2108
1760
  if (completed) {
2109
1761
  return completed;
2110
1762
  }
@@ -2112,14 +1764,11 @@ var materializeAssets = async (markdown, ctx) => {
2112
1764
  if (!res.ok) {
2113
1765
  throw new Error(`${res.status}`);
2114
1766
  }
2115
- const type = mediaType(res);
2116
- if (NON_MEDIA_TYPE.test(type)) {
2117
- throw new Error(`responded with ${type}, not a media file`);
2118
- }
1767
+ const ext = extFor(mediaType(res), urlExt);
2119
1768
  if (!res.body) {
2120
1769
  throw new Error("empty response body");
2121
1770
  }
2122
- const file = `${stem}${urlExt ?? EXT_BY_MIME.get(type) ?? UNKNOWN_EXT}`;
1771
+ const file = `${stem}${ext}`;
2123
1772
  const target = join3(ctx.assetsDir, file);
2124
1773
  await mkdir(ctx.assetsDir, { recursive: true });
2125
1774
  const part = `${target}.${randomUUID()}${PART_SUFFIX}`;
@@ -2165,8 +1814,8 @@ var materializeAssets = async (markdown, ctx) => {
2165
1814
  });
2166
1815
  }
2167
1816
  }));
2168
- const rewritten = masked.replaceAll(MD_IMAGE, (match, alt, url) => {
2169
- const local = rewrites.get(url);
1817
+ const rewritten = masked.replaceAll(MD_IMAGE, (match, alt, pointy, bare) => {
1818
+ const local = rewrites.get(imageUrl(pointy, bare));
2170
1819
  return local ? `![${alt}](${local})` : match;
2171
1820
  }).replaceAll(HTML_VIDEO_SRC, (match, open, url, close) => {
2172
1821
  const local = rewrites.get(url);
@@ -2202,7 +1851,8 @@ var withNotionRetry = async (call, attempt = 0) => {
2202
1851
  if (status !== RATE_LIMITED || attempt === MAX_RETRIES) {
2203
1852
  throw error;
2204
1853
  }
2205
- const retryAfter = Number(error.headers?.["retry-after"]);
1854
+ const { headers } = error;
1855
+ const retryAfter = Number(headers?.get?.("retry-after"));
2206
1856
  const wait = retryAfter > 0 ? retryAfter * SECOND_MS : BASE_DELAY_MS * 2 ** attempt * (1 + Math.random());
2207
1857
  await sleep(wait);
2208
1858
  return withNotionRetry(call, attempt + 1);
@@ -2221,6 +1871,7 @@ var LIST_BLOCKS = new Set([
2221
1871
  var isListItem = (block) => block !== undefined && LIST_BLOCKS.has(block.type);
2222
1872
  var richToPlain = (rich = []) => rich.map((node) => node.plain_text).join("");
2223
1873
  var jsxString = (value) => `{${JSON.stringify(value)}}`;
1874
+ var FRAME_ESCAPED = /\\(?=[<>])/gu;
2224
1875
  var YOUTUBE_HOST = /(?:^|\.)(?:youtube(?:-nocookie)?\.com|youtu\.be)$/u;
2225
1876
  var isYouTubeUrl = (url) => {
2226
1877
  const host = URL.parse(url)?.hostname ?? "";
@@ -2234,13 +1885,13 @@ var renderVideo = (data) => {
2234
1885
  const caption = richToMarkdown(data.caption);
2235
1886
  const title = caption ? ` title=${jsxString(richToPlain(data.caption))}` : "";
2236
1887
  const media = isYouTubeUrl(url) ? `<YouTube${title} url=${JSON.stringify(url)} />` : `<video controls src=${JSON.stringify(url)} />`;
2237
- return caption ? `<Frame caption=${jsxString(caption)}>
1888
+ return caption ? `<Frame caption=${jsxString(caption.replaceAll(FRAME_ESCAPED, ""))}>
2238
1889
  ${media}
2239
1890
  </Frame>` : media;
2240
1891
  };
2241
1892
  var renderLeaf = (block) => {
2242
1893
  const data = payloadOf(block) ?? {};
2243
- const text = richToMarkdown(blockField(block));
1894
+ const text = guardBlockStart(richToMarkdown(blockField(block)));
2244
1895
  switch (block.type) {
2245
1896
  case "paragraph": {
2246
1897
  return text;
@@ -2320,7 +1971,10 @@ var notionSource = (options, ctx) => {
2320
1971
  return render(client, await childrenOf(client, target.id));
2321
1972
  };
2322
1973
  if (block.type === "callout") {
2323
- const body = [richToMarkdown(blockField(block)), await children(block)].filter(Boolean).join(`
1974
+ const body = [
1975
+ guardBlockStart(richToMarkdown(blockField(block))),
1976
+ await children(block)
1977
+ ].filter(Boolean).join(`
2324
1978
 
2325
1979
  `);
2326
1980
  return `<Callout>
@@ -2494,7 +2148,8 @@ ${nested}`;
2494
2148
  prefix: options.prefix,
2495
2149
  read,
2496
2150
  staged: true,
2497
- watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined
2151
+ watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined,
2152
+ withContext: (next) => notionSource(options, next)
2498
2153
  };
2499
2154
  };
2500
2155
 
@@ -2741,7 +2396,7 @@ var buildLinkIndex = (notes, options) => {
2741
2396
  if (cached) {
2742
2397
  return cached;
2743
2398
  }
2744
- const route = resolveEntryRoute({ ref: note.rel, slug: entrySlugFor(note.placement.navPath) }, ".md", isStringValue(note.data.slug) ? note.data.slug : undefined, options);
2399
+ const route = resolveEntryRoute({ ref: note.rel, slug: entrySlugFor(note.placement.navPath) }, ".md", isStringValue(note.data.slug) ? note.data.slug : undefined, { ...options, orderingPrefixes: true });
2745
2400
  const built = {
2746
2401
  anchors: new Map,
2747
2402
  locales: route.locales,
@@ -2802,11 +2457,15 @@ var noteToEntry = (pair, index, options, unresolved) => {
2802
2457
  const keep = keptKeysFor(note.data, options);
2803
2458
  const data = Object.fromEntries(Object.entries(note.data).filter(([key]) => !OBSIDIAN_NATIVE_KEYS.has(key) && (PAGE_META_KEYS.has(key) || keep.has(key))));
2804
2459
  const merged = title === undefined ? data : { ...data, title };
2805
- const text = transformBody(note.content.trim(), index, pair.self, unresolved);
2460
+ const body = note.content.trim();
2461
+ const text = transformBody(body, index, pair.self, unresolved);
2806
2462
  const raw = frontmatter_default.stringify(`${text}
2807
2463
  `, merged);
2464
+ const leading = note.content.slice(0, note.content.indexOf(body));
2808
2465
  return {
2809
2466
  body: { format: "md", text },
2467
+ bodyLineOffset: note.frontmatterLines + leading.split(`
2468
+ `).length - 1,
2810
2469
  data: merged,
2811
2470
  hash: hashText(raw),
2812
2471
  raw,
@@ -2818,11 +2477,13 @@ var noteToEntry = (pair, index, options, unresolved) => {
2818
2477
  var readNote = async (vaultDir, rel, options) => {
2819
2478
  const absPath = join5(vaultDir, rel);
2820
2479
  try {
2821
- const { content, data } = frontmatter_default(await readFile2(absPath, "utf-8"));
2480
+ const file = await readFile2(absPath, "utf-8");
2481
+ const { content, data } = frontmatter_default(file);
2822
2482
  return {
2823
2483
  absPath,
2824
2484
  content,
2825
2485
  data,
2486
+ frontmatterLines: strippedLineOffset(file, content),
2826
2487
  placement: placeEntryRef(rel, ".md", options),
2827
2488
  rel
2828
2489
  };
@@ -2956,6 +2617,7 @@ var obsidianSource = (options, ctx) => {
2956
2617
  contentRoot: vaultDir,
2957
2618
  load,
2958
2619
  name: options.name,
2620
+ orderedNames: true,
2959
2621
  prefix: options.prefix,
2960
2622
  read,
2961
2623
  staged: true,
@@ -3015,6 +2677,7 @@ var renderInlineNode2 = (node, options) => {
3015
2677
  }
3016
2678
  }
3017
2679
  };
2680
+ var blockText = (node, options) => guardBlockStart(renderInlines2(children2(node), options));
3018
2681
  var renderUpload = (node, options) => {
3019
2682
  const value = asObject(node.value);
3020
2683
  const url = value ? asString(value.url) : undefined;
@@ -3045,7 +2708,7 @@ var renderList2 = (node, options) => {
3045
2708
  if (listType === "check") {
3046
2709
  check = item.checked === true ? "[x] " : "[ ] ";
3047
2710
  }
3048
- const line = `${check}${renderInlines2(inline, options)}`;
2711
+ const line = `${check}${guardBlockStart(renderInlines2(inline, options))}`;
3049
2712
  items.push(listItem(lastMarker, sub ? `${line}
3050
2713
  ${sub}` : line));
3051
2714
  index += 1;
@@ -3056,14 +2719,14 @@ ${sub}` : line));
3056
2719
  var renderBlock2 = (node, options) => {
3057
2720
  switch (typeOf(node)) {
3058
2721
  case "paragraph": {
3059
- return renderInlines2(children2(node), options);
2722
+ return blockText(node, options);
3060
2723
  }
3061
2724
  case "heading": {
3062
2725
  const level = Number((asString(node.tag) ?? "h1").slice(1));
3063
- return `${headingPrefix(level)}${renderInlines2(children2(node), options)}`;
2726
+ return `${headingPrefix(level)}${blockText(node, options)}`;
3064
2727
  }
3065
2728
  case "quote": {
3066
- return blockquote(renderInlines2(children2(node), options));
2729
+ return blockquote(blockText(node, options));
3067
2730
  }
3068
2731
  case "list": {
3069
2732
  return renderList2(node, options);
@@ -3145,7 +2808,8 @@ var payloadSource = (options, ctx) => {
3145
2808
  fetchEntries,
3146
2809
  name: options.name,
3147
2810
  pollInterval: options.pollInterval,
3148
- prefix: options.prefix
2811
+ prefix: options.prefix,
2812
+ withContext: (next) => payloadSource(options, next)
3149
2813
  }, ctx);
3150
2814
  };
3151
2815
 
@@ -3162,46 +2826,36 @@ var HEADING_STYLES = new Map([
3162
2826
  ["h6", "###### "]
3163
2827
  ]);
3164
2828
  var renderSpan = (span, defs) => {
3165
- const isCode = span.marks?.includes("code") ?? false;
3166
- let text = isCode ? span.text ?? "" : escapeMarkdownText(span.text ?? "");
3167
- if (!span.marks || span.marks.length === 0) {
3168
- return text;
3169
- }
3170
- let link;
3171
- for (const mark of span.marks) {
3172
- switch (mark) {
3173
- case "strong": {
3174
- text = `**${text}**`;
3175
- break;
3176
- }
3177
- case "em": {
3178
- text = `*${text}*`;
3179
- break;
3180
- }
3181
- case "code": {
3182
- text = `\`${text}\``;
3183
- break;
3184
- }
3185
- case "strike-through": {
3186
- text = `~~${text}~~`;
3187
- break;
3188
- }
3189
- default: {
3190
- const def = defs.get(mark);
3191
- if (def?._type === "link") {
3192
- link = def;
3193
- }
3194
- }
3195
- }
3196
- }
3197
- return link?.href ? `[${text}](${link.href})` : text;
2829
+ const marks = span.marks ?? [];
2830
+ const link = marks.map((mark) => defs.get(mark)).findLast((def) => def?._type === "link");
2831
+ return renderLink(renderInline(span.text ?? "", {
2832
+ bold: marks.includes("strong"),
2833
+ code: marks.includes("code"),
2834
+ italic: marks.includes("em"),
2835
+ strike: marks.includes("strike-through")
2836
+ }), link?.href);
3198
2837
  };
3199
2838
  var renderChildren = (block) => {
3200
2839
  const defs = new Map((block.markDefs ?? []).map((def) => [def._key, def]));
3201
- return (block.children ?? []).map((span) => renderSpan(span, defs)).join("");
2840
+ return guardBlockStart((block.children ?? []).map((span) => renderSpan(span, defs)).join(""));
3202
2841
  };
3203
2842
  var isAltText = (value) => typeof value === "string";
3204
- var renderBlock3 = (block, options) => {
2843
+ var listMarker = (block) => block.listItem === "number" ? "1." : "-";
2844
+ var listIndents = (blocks) => {
2845
+ const columns = [];
2846
+ return blocks.map((block) => {
2847
+ if (!block.listItem) {
2848
+ columns.length = 0;
2849
+ return 0;
2850
+ }
2851
+ const depth = Math.min(Math.max(0, (block.level ?? 1) - 1), columns.length);
2852
+ const indent = columns[depth - 1] ?? 0;
2853
+ columns.length = depth;
2854
+ columns.push(indent + listMarker(block).length + 1);
2855
+ return indent;
2856
+ });
2857
+ };
2858
+ var renderBlock3 = (block, options, indent) => {
3205
2859
  const custom = options.serializers?.[block._type];
3206
2860
  if (custom) {
3207
2861
  return custom(block);
@@ -3216,9 +2870,7 @@ var renderBlock3 = (block, options) => {
3216
2870
  }
3217
2871
  const inline = renderChildren(block);
3218
2872
  if (block.listItem) {
3219
- const indent = " ".repeat(Math.max(0, (block.level ?? 1) - 1));
3220
- const marker = block.listItem === "number" ? "1." : "-";
3221
- return `${indent}${marker} ${inline}`;
2873
+ return `${" ".repeat(indent)}${listMarker(block)} ${inline}`;
3222
2874
  }
3223
2875
  if (block.style === "blockquote") {
3224
2876
  return `> ${inline}`;
@@ -3226,7 +2878,8 @@ var renderBlock3 = (block, options) => {
3226
2878
  return `${HEADING_STYLES.get(block.style ?? "normal") ?? ""}${inline}`;
3227
2879
  };
3228
2880
  var portableTextToMarkdown = (blocks, options = {}) => {
3229
- const lines = blocks.map((block) => renderBlock3(block, options));
2881
+ const indents = listIndents(blocks);
2882
+ const lines = blocks.map((block, i) => renderBlock3(block, options, indents[i] ?? 0));
3230
2883
  const out = [];
3231
2884
  for (let i = 0;i < lines.length; i += 1) {
3232
2885
  const line = lines[i] ?? "";
@@ -3335,7 +2988,8 @@ var sanitySource = (options, ctx) => {
3335
2988
  prefix: options.prefix,
3336
2989
  read,
3337
2990
  staged: true,
3338
- watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined
2991
+ watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined,
2992
+ withContext: (next) => sanitySource(options, next)
3339
2993
  };
3340
2994
  };
3341
2995
 
@@ -3361,11 +3015,12 @@ var renderInlineNode3 = (node) => {
3361
3015
  }
3362
3016
  }
3363
3017
  };
3018
+ var blockText2 = (nodes) => guardBlockStart(renderInlines3(nodes));
3364
3019
  var renderList3 = (node) => {
3365
3020
  const ordered = asString(node.format) === "ordered";
3366
3021
  return children3(node).map((item, index) => {
3367
3022
  const kids = children3(item);
3368
- const line = renderInlines3(kids.filter((kid) => typeOf2(kid) !== "list"));
3023
+ const line = blockText2(kids.filter((kid) => typeOf2(kid) !== "list"));
3369
3024
  const sub = kids.filter((kid) => typeOf2(kid) === "list").map((list) => renderList3(list)).join(`
3370
3025
  `);
3371
3026
  const body = sub ? `${line}
@@ -3385,13 +3040,13 @@ var renderImage = (node, options) => {
3385
3040
  var renderBlock4 = (node, options) => {
3386
3041
  switch (typeOf2(node)) {
3387
3042
  case "paragraph": {
3388
- return renderInlines3(children3(node));
3043
+ return blockText2(children3(node));
3389
3044
  }
3390
3045
  case "heading": {
3391
- return `${headingPrefix(asNumber(node.level) ?? 1)}${renderInlines3(children3(node))}`;
3046
+ return `${headingPrefix(asNumber(node.level) ?? 1)}${blockText2(children3(node))}`;
3392
3047
  }
3393
3048
  case "quote": {
3394
- return blockquote(renderInlines3(children3(node)));
3049
+ return blockquote(blockText2(children3(node)));
3395
3050
  }
3396
3051
  case "list": {
3397
3052
  return renderList3(node);
@@ -3491,7 +3146,8 @@ var strapiSource = (options, ctx) => {
3491
3146
  fetchEntries,
3492
3147
  name: options.name,
3493
3148
  pollInterval: options.pollInterval,
3494
- prefix: options.prefix
3149
+ prefix: options.prefix,
3150
+ withContext: (next) => strapiSource(options, next)
3495
3151
  }, ctx);
3496
3152
  };
3497
3153
 
@@ -3509,10 +3165,18 @@ var uniqueNamer = () => {
3509
3165
  return name;
3510
3166
  };
3511
3167
  };
3512
- var sourceContext = (context, name, runtime) => ({
3168
+ var snapshotKey = (adapter, runtime) => {
3169
+ const mode = runtime.preview ? "preview" : "published";
3170
+ if (adapter.kind === "custom") {
3171
+ return mode;
3172
+ }
3173
+ const { pollInterval: _paced, ...fetched } = adapter.options;
3174
+ return `${mode}-${hashText(JSON.stringify(fetched))}`;
3175
+ };
3176
+ var sourceContext = (context, name, runtime, snapshot) => ({
3513
3177
  assetsBaseUrl: `/blume-assets/${name}`,
3514
3178
  assetsDir: join7(context.outDir, "public", "blume-assets", name),
3515
- cacheDir: join7(context.outDir, "cache", name),
3179
+ cacheDir: snapshot === undefined ? join7(context.outDir, "cache", name) : join7(context.outDir, "cache", name, snapshot),
3516
3180
  mode: runtime.mode,
3517
3181
  preview: runtime.preview,
3518
3182
  projectRoot: context.root,
@@ -3523,29 +3187,30 @@ var typeFrontmatterKeys = (config) => Object.fromEntries(Object.entries(config.c
3523
3187
  Object.keys(def.frontmatter)
3524
3188
  ]));
3525
3189
  var buildSource = (adapter, name, config, context, runtime) => {
3190
+ const ctx = () => sourceContext(context, name, runtime, snapshotKey(adapter, runtime));
3526
3191
  switch (adapter.kind) {
3527
3192
  case "filesystem": {
3528
3193
  const { pollInterval: _ignored, ...options } = adapter.options;
3529
3194
  return filesystemSource({ ...options, name, projectRoot: context.root });
3530
3195
  }
3531
3196
  case "custom": {
3532
- const source = adapter.options;
3197
+ const source = adapter.options.withContext?.(ctx()) ?? adapter.options;
3533
3198
  return source.name === name ? source : { ...source, name };
3534
3199
  }
3535
3200
  case "sanity": {
3536
- return sanitySource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3201
+ return sanitySource({ ...adapter.options, name }, ctx());
3537
3202
  }
3538
3203
  case "notion": {
3539
- return notionSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3204
+ return notionSource({ ...adapter.options, name }, ctx());
3540
3205
  }
3541
3206
  case "contentful": {
3542
- return contentfulSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3207
+ return contentfulSource({ ...adapter.options, name }, ctx());
3543
3208
  }
3544
3209
  case "payload": {
3545
- return payloadSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3210
+ return payloadSource({ ...adapter.options, name }, ctx());
3546
3211
  }
3547
3212
  case "strapi": {
3548
- return strapiSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3213
+ return strapiSource({ ...adapter.options, name }, ctx());
3549
3214
  }
3550
3215
  case "obsidian": {
3551
3216
  const { pollInterval: _ignored, ...options } = adapter.options;
@@ -3557,22 +3222,22 @@ var buildSource = (adapter, name, config, context, runtime) => {
3557
3222
  name,
3558
3223
  typeFrontmatterKeys: typeFrontmatterKeys(config),
3559
3224
  versions: config.versions
3560
- }, sourceContext(context, name, runtime));
3225
+ }, ctx());
3561
3226
  }
3562
3227
  case "github-releases": {
3563
- return githubReleasesSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3228
+ return githubReleasesSource({ ...adapter.options, name, site: config.deployment.options.site }, ctx());
3564
3229
  }
3565
3230
  default: {
3566
3231
  break;
3567
3232
  }
3568
3233
  }
3569
- return mdxRemoteSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3234
+ return mdxRemoteSource({ ...adapter.options, name }, ctx());
3570
3235
  };
3571
3236
  var baseName = (adapter) => {
3572
3237
  if (adapter.kind === "custom") {
3573
3238
  return adapter.options.name;
3574
3239
  }
3575
- return adapter.options.prefix ?? adapter.kind;
3240
+ return trimChar(adapter.options.prefix ?? "", "/") || adapter.kind;
3576
3241
  };
3577
3242
  var resolveSources = (config, context, runtime) => {
3578
3243
  const nameFor = uniqueNamer();
@@ -3640,6 +3305,7 @@ var normalizeLoadedEntries = (loaded, config) => {
3640
3305
  source: {
3641
3306
  monolingual: source.monolingual,
3642
3307
  name: source.name,
3308
+ orderedNames: source.orderedNames,
3643
3309
  prefix: source.prefix,
3644
3310
  staged: source.staged
3645
3311
  },
@@ -3657,6 +3323,8 @@ var normalizeLoadedEntries = (loaded, config) => {
3657
3323
  };
3658
3324
  var isLogoShorthand = (logo) => typeof logo === "string";
3659
3325
  var logoHref = (logo) => logo === undefined || isLogoShorthand(logo) ? "/" : logo.href ?? "/";
3326
+ var isBannerShorthand = (banner) => typeof banner === "string";
3327
+ var bannerLinkHref = (banner) => banner === undefined || isBannerShorthand(banner) ? undefined : banner.link?.href;
3660
3328
  var scanProject = async (root, options = {}) => {
3661
3329
  const mode = options.mode ?? "dev";
3662
3330
  const preview = options.preview ?? false;
@@ -3675,7 +3343,14 @@ var scanProject = async (root, options = {}) => {
3675
3343
  for (const source of sources) {
3676
3344
  source.validate?.();
3677
3345
  }
3678
- const metaSources = sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [{ prefix: source.prefix, root: source.contentRoot }]);
3346
+ const metaSources = sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [
3347
+ {
3348
+ exclude: source.exclude,
3349
+ include: source.include,
3350
+ prefix: source.prefix,
3351
+ root: source.contentRoot
3352
+ }
3353
+ ]);
3679
3354
  const localeDirs = config.i18n && config.i18n.parser === "dir" ? config.i18n.locales.flatMap((locale) => locale.code === config.i18n?.defaultLocale ? [] : [locale.code]) : undefined;
3680
3355
  const [loaded, folderMeta] = await Promise.all([
3681
3356
  Promise.all(sources.map(async (source) => ({ source, ...await source.load() }))),
@@ -3729,6 +3404,7 @@ var scanProject = async (root, options = {}) => {
3729
3404
  }
3730
3405
  const customPages = context.pagesRoot ? await discoverPages(context.pagesRoot) : [];
3731
3406
  const graph = buildContentGraph(pages, {
3407
+ bannerHref: bannerLinkHref(config.banner),
3732
3408
  basePath: config.basePath,
3733
3409
  brandHref: logoHref(config.logo),
3734
3410
  extraRoutes: new Set([
@@ -3768,5 +3444,5 @@ var scanProject = async (root, options = {}) => {
3768
3444
 
3769
3445
  export { validateSearchPopularIcons, validateNavTargets, contentIndexable, buildManifest, isWithin, scanProject };
3770
3446
 
3771
- //# debugId=EBFFBF92D39DE54464756E2164756E21
3772
- //# sourceMappingURL=chunk-fs23ddbb.js.map
3447
+ //# debugId=B3E8BA7A9A3228DD64756E2164756E21
3448
+ //# sourceMappingURL=chunk-5r8g91qn.js.map