blume 2.0.3 → 2.1.1

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 (607) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +142 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-5f86nr5m.js → chunk-073rdvgb.js} +21 -48
  5. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-073rdvgb.js.map} +4 -5
  6. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  7. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  8. package/dist/cli/chunk-150jk7km.js +52 -0
  9. package/dist/cli/chunk-150jk7km.js.map +10 -0
  10. package/dist/cli/{chunk-9t7a85s3.js → chunk-313vnn40.js} +30 -19
  11. package/dist/cli/chunk-313vnn40.js.map +10 -0
  12. package/dist/cli/{chunk-tkacnehg.js → chunk-3v3ffbrj.js} +2 -2
  13. package/dist/cli/{chunk-q58y5e6a.js → chunk-3x6x3pph.js} +46 -24
  14. package/dist/cli/chunk-3x6x3pph.js.map +10 -0
  15. package/dist/cli/{chunk-1d7ve1dm.js → chunk-42t4jxdx.js} +37 -5
  16. package/dist/cli/chunk-42t4jxdx.js.map +12 -0
  17. package/dist/cli/{chunk-8ktnccpt.js → chunk-54ztw1nj.js} +21 -2
  18. package/dist/cli/chunk-54ztw1nj.js.map +10 -0
  19. package/dist/cli/{chunk-hm3vjy5s.js → chunk-5vjtgs6e.js} +28 -10
  20. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-5vjtgs6e.js.map} +4 -4
  21. package/dist/cli/chunk-66w9j7p6.js +322 -0
  22. package/dist/cli/chunk-66w9j7p6.js.map +14 -0
  23. package/dist/cli/{chunk-2hsdwb9n.js → chunk-7ppf1rva.js} +34 -18
  24. package/dist/cli/chunk-7ppf1rva.js.map +10 -0
  25. package/dist/cli/{chunk-r9rcc4w7.js → chunk-8y2kmjv8.js} +7 -7
  26. package/dist/cli/{chunk-r20tn01b.js → chunk-c1y538kd.js} +8 -2
  27. package/dist/cli/{chunk-r20tn01b.js.map → chunk-c1y538kd.js.map} +3 -3
  28. package/dist/cli/{chunk-2eytanqx.js → chunk-c887fj0s.js} +2 -2
  29. package/dist/cli/{chunk-p3v96n38.js → chunk-cfyg47zg.js} +16 -6
  30. package/dist/cli/chunk-cfyg47zg.js.map +10 -0
  31. package/dist/cli/chunk-cm89scdv.js +443 -0
  32. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  33. package/dist/cli/{chunk-akbpwfxc.js → chunk-dzwehpy7.js} +2 -2
  34. package/dist/cli/{chunk-wrr3j9w9.js → chunk-e6atmn4h.js} +299 -72
  35. package/dist/cli/chunk-e6atmn4h.js.map +36 -0
  36. package/dist/cli/{chunk-tzmab476.js → chunk-e8xw9qkh.js} +8 -6
  37. package/dist/cli/{chunk-tzmab476.js.map → chunk-e8xw9qkh.js.map} +3 -3
  38. package/dist/cli/{chunk-6dsbexzp.js → chunk-e9q7a2ak.js} +17 -15
  39. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-e9q7a2ak.js.map} +2 -2
  40. package/dist/cli/{chunk-pv29h0wf.js → chunk-ehweg4fr.js} +5155 -1249
  41. package/dist/cli/chunk-ehweg4fr.js.map +218 -0
  42. package/dist/cli/{chunk-c8chx29p.js → chunk-ep6nd0rn.js} +390 -318
  43. package/dist/cli/chunk-ep6nd0rn.js.map +36 -0
  44. package/dist/cli/{chunk-p73c0m7w.js → chunk-fdrkhpjy.js} +18 -15
  45. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-fdrkhpjy.js.map} +3 -3
  46. package/dist/cli/{chunk-2z928egk.js → chunk-ft9vfys6.js} +5 -5
  47. package/dist/cli/{chunk-5m5nmvyq.js → chunk-gb420vvz.js} +316 -79
  48. package/dist/cli/chunk-gb420vvz.js.map +17 -0
  49. package/dist/cli/{chunk-vg9r4eb9.js → chunk-hebn0tpx.js} +86 -13
  50. package/dist/cli/chunk-hebn0tpx.js.map +14 -0
  51. package/dist/cli/{chunk-35d4wj9f.js → chunk-htrwgabr.js} +41 -23
  52. package/dist/cli/chunk-htrwgabr.js.map +15 -0
  53. package/dist/cli/{chunk-g4hq16wv.js → chunk-hx3xqnk9.js} +16 -14
  54. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-hx3xqnk9.js.map} +2 -2
  55. package/dist/cli/chunk-j134vrp2.js +65 -0
  56. package/dist/cli/chunk-j134vrp2.js.map +10 -0
  57. package/dist/cli/{chunk-a58773jm.js → chunk-kma9f6sf.js} +2 -2
  58. package/dist/cli/chunk-kwrg63tm.js +31 -0
  59. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  60. package/dist/cli/{chunk-ey84smr6.js → chunk-mrcccgnh.js} +6 -6
  61. package/dist/cli/{chunk-ey84smr6.js.map → chunk-mrcccgnh.js.map} +1 -1
  62. package/dist/cli/{chunk-yfyb25rh.js → chunk-n0v7m8ry.js} +24 -8
  63. package/dist/cli/chunk-n0v7m8ry.js.map +14 -0
  64. package/dist/cli/{chunk-j85vccga.js → chunk-ncj7w596.js} +576 -367
  65. package/dist/cli/chunk-ncj7w596.js.map +35 -0
  66. package/dist/cli/{chunk-5xvm6tfj.js → chunk-nq1d20w2.js} +17 -15
  67. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-nq1d20w2.js.map} +2 -2
  68. package/dist/cli/{chunk-pehfxfta.js → chunk-nrns10mj.js} +6 -98
  69. package/dist/cli/chunk-nrns10mj.js.map +10 -0
  70. package/dist/cli/{chunk-crgn1q09.js → chunk-sds923fa.js} +4 -4
  71. package/dist/cli/chunk-t2m5j4pr.js +104 -0
  72. package/dist/cli/chunk-t2m5j4pr.js.map +11 -0
  73. package/dist/cli/{chunk-acanzt5p.js → chunk-tdqhyxfz.js} +6 -6
  74. package/dist/cli/chunk-tphac74f.js +126 -0
  75. package/dist/cli/chunk-tphac74f.js.map +11 -0
  76. package/dist/cli/chunk-w1r4vase.js +18 -0
  77. package/dist/cli/chunk-w1r4vase.js.map +10 -0
  78. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  79. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  80. package/dist/cli/index.js +31 -20
  81. package/dist/cli/index.js.map +5 -5
  82. package/dist/types/ai/agent-surface.d.ts +8 -7
  83. package/dist/types/ai/ai-catalog.d.ts +7 -0
  84. package/dist/types/ai/ask-context.d.ts +26 -0
  85. package/dist/types/ai/ask.d.ts +271 -58
  86. package/dist/types/ai/component-markdown.d.ts +2 -0
  87. package/dist/types/ai/index.d.ts +2 -2
  88. package/dist/types/ai/openapi-components.d.ts +4 -1
  89. package/dist/types/ai/site-skill.d.ts +26 -0
  90. package/dist/types/ai/skills.d.ts +16 -0
  91. package/dist/types/astro/integration.d.ts +9 -0
  92. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  93. package/dist/types/astro/pages.d.ts +10 -0
  94. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  95. package/dist/types/captcha/index.d.ts +28 -0
  96. package/dist/types/captcha/schema.d.ts +29 -0
  97. package/dist/types/captcha/turnstile.d.ts +28 -0
  98. package/dist/types/cli/init/scaffold.d.ts +1 -1
  99. package/dist/types/components/content/api-field.d.ts +62 -0
  100. package/dist/types/components/content/api-page.d.ts +100 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  103. package/dist/types/components/openapi/helpers.d.ts +158 -0
  104. package/dist/types/components/openapi/security.d.ts +95 -0
  105. package/dist/types/components/openapi/snippets.d.ts +60 -0
  106. package/dist/types/consent/ethyca.d.ts +48 -0
  107. package/dist/types/consent/index.d.ts +37 -0
  108. package/dist/types/consent/native.d.ts +28 -0
  109. package/dist/types/consent/osano.d.ts +42 -0
  110. package/dist/types/consent/schema.d.ts +110 -0
  111. package/dist/types/core/changelog-index.d.ts +8 -0
  112. package/dist/types/core/config-input.d.ts +217 -23
  113. package/dist/types/core/data.d.ts +53 -4
  114. package/dist/types/core/footer.d.ts +8 -0
  115. package/dist/types/core/i18n-ui.d.ts +88 -0
  116. package/dist/types/core/includes.d.ts +8 -2
  117. package/dist/types/core/links.d.ts +8 -0
  118. package/dist/types/core/localizable.d.ts +7 -0
  119. package/dist/types/core/page-modes.d.ts +29 -0
  120. package/dist/types/core/probe.d.ts +2 -0
  121. package/dist/types/core/project-graph.d.ts +6 -0
  122. package/dist/types/core/redirect-patterns.d.ts +152 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +670 -56
  125. package/dist/types/core/sources/cache.d.ts +3 -0
  126. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  127. package/dist/types/core/sources/contentful.d.ts +17 -1
  128. package/dist/types/core/sources/lexical.d.ts +5 -0
  129. package/dist/types/core/sources/normalize.d.ts +20 -1
  130. package/dist/types/core/sources/read.d.ts +5 -0
  131. package/dist/types/core/sources/remote.d.ts +37 -10
  132. package/dist/types/core/sources/sanity.d.ts +1 -1
  133. package/dist/types/core/types.d.ts +18 -1
  134. package/dist/types/core/variables.d.ts +42 -0
  135. package/dist/types/deploy/adapter-output.d.ts +2 -1
  136. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  137. package/dist/types/deploy/adapters/node.d.ts +58 -23
  138. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  139. package/dist/types/deploy/adapters/types.d.ts +3 -2
  140. package/dist/types/deploy/node-headers.d.ts +13 -2
  141. package/dist/types/deploy/platforms/cloudflare.d.ts +15 -7
  142. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  143. package/dist/types/deploy/platforms/node.d.ts +3 -1
  144. package/dist/types/deploy/platforms/types.d.ts +18 -7
  145. package/dist/types/deploy/platforms/vercel.d.ts +26 -1
  146. package/dist/types/deploy/redirects.d.ts +55 -11
  147. package/dist/types/deploy/sitemap.d.ts +2 -1
  148. package/dist/types/deploy/vercel-negotiation.d.ts +51 -21
  149. package/dist/types/markdown/code-title.d.ts +17 -0
  150. package/dist/types/markdown/features.d.ts +3 -2
  151. package/dist/types/narration/build.d.ts +70 -0
  152. package/dist/types/narration/provider.d.ts +26 -0
  153. package/dist/types/narration/script.d.ts +154 -0
  154. package/dist/types/openapi/checks.d.ts +27 -0
  155. package/dist/types/openapi/graphql.d.ts +6 -0
  156. package/dist/types/openapi/model.d.ts +27 -10
  157. package/dist/types/openapi/overlay.d.ts +54 -0
  158. package/dist/types/openapi/parse.d.ts +16 -0
  159. package/dist/types/openapi/references.d.ts +30 -4
  160. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  161. package/dist/types/ratelimit/index.d.ts +27 -0
  162. package/dist/types/ratelimit/memory.d.ts +36 -0
  163. package/dist/types/ratelimit/schema.d.ts +119 -0
  164. package/dist/types/ratelimit/upstash.d.ts +46 -0
  165. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  166. package/dist/types/reference/asyncapi.d.ts +10 -10
  167. package/dist/types/reference/graphql.d.ts +89 -11
  168. package/dist/types/reference/index.d.ts +1 -1
  169. package/dist/types/reference/openapi.d.ts +33 -12
  170. package/dist/types/reference/options.d.ts +82 -6
  171. package/dist/types/reference/scalar.d.ts +13 -0
  172. package/dist/types/reference/schema.d.ts +144 -36
  173. package/dist/types/search/adapters/registry.d.ts +3 -0
  174. package/dist/types/search/adapters/typesense.d.ts +14 -4
  175. package/dist/types/search/build.d.ts +3 -1
  176. package/dist/types/search/documents.d.ts +12 -1
  177. package/dist/types/search/orama-index.d.ts +13 -9
  178. package/dist/types/search/sync/algolia.d.ts +5 -2
  179. package/dist/types/search/sync/index.d.ts +3 -1
  180. package/dist/types/search/sync/typesense.d.ts +11 -7
  181. package/dist/types/seo/metatags.d.ts +20 -0
  182. package/dist/types/sources/contentful.d.ts +16 -0
  183. package/dist/types/sources/github-releases.d.ts +8 -1
  184. package/dist/types/sources/payload.d.ts +2 -0
  185. package/dist/types/sources/registry.d.ts +54 -0
  186. package/dist/types/sources/shared.d.ts +3 -0
  187. package/dist/types/sources/strapi.d.ts +2 -0
  188. package/docs/01-quickstart.mdx +4 -2
  189. package/docs/02-deployment.mdx +71 -17
  190. package/docs/03-upgrading.mdx +4 -4
  191. package/docs/04-migrating.mdx +1 -1
  192. package/docs/advanced/changelog.mdx +13 -2
  193. package/docs/advanced/skills.mdx +15 -1
  194. package/docs/cli/audit.mdx +51 -8
  195. package/docs/cli/doctor.mdx +5 -1
  196. package/docs/cli/evals.mdx +4 -4
  197. package/docs/cli/index.mdx +1 -0
  198. package/docs/cli/translate.mdx +1 -0
  199. package/docs/cli/validate.mdx +4 -3
  200. package/docs/configuration/analytics.mdx +3 -1
  201. package/docs/configuration/assistant.mdx +194 -38
  202. package/docs/configuration/consent.mdx +100 -0
  203. package/docs/configuration/customization.mdx +8 -6
  204. package/docs/configuration/export.mdx +5 -3
  205. package/docs/configuration/index.mdx +64 -5
  206. package/docs/configuration/meta.ts +3 -0
  207. package/docs/configuration/narration.mdx +119 -0
  208. package/docs/configuration/rate-limiting.mdx +90 -0
  209. package/docs/configuration/search.mdx +63 -12
  210. package/docs/configuration/theming.mdx +3 -1
  211. package/docs/content/components.mdx +108 -2
  212. package/docs/content/frontmatter.mdx +80 -2
  213. package/docs/content/i18n.mdx +39 -4
  214. package/docs/content/includes.mdx +16 -0
  215. package/docs/content/meta.mdx +1 -0
  216. package/docs/content/meta.ts +1 -0
  217. package/docs/content/navigation.mdx +32 -10
  218. package/docs/content/sources/contentful.mdx +35 -0
  219. package/docs/content/sources/custom.mdx +43 -0
  220. package/docs/content/sources/github-releases.mdx +39 -0
  221. package/docs/content/sources/index.mdx +91 -0
  222. package/docs/content/sources/meta.ts +16 -0
  223. package/docs/content/sources/notion.mdx +32 -0
  224. package/docs/content/sources/obsidian.mdx +37 -0
  225. package/docs/content/sources/payload.mdx +31 -0
  226. package/docs/content/sources/remote-mdx.mdx +44 -0
  227. package/docs/content/sources/sanity.mdx +29 -0
  228. package/docs/content/sources/strapi.mdx +32 -0
  229. package/docs/content/syntax.mdx +69 -4
  230. package/docs/content/variables.mdx +41 -0
  231. package/docs/discoverability/agent-discovery.mdx +29 -2
  232. package/docs/discoverability/index.mdx +2 -2
  233. package/docs/discoverability/json-api.mdx +2 -0
  234. package/docs/discoverability/llms-txt.mdx +2 -2
  235. package/docs/discoverability/markdown.mdx +3 -1
  236. package/docs/discoverability/mcp.mdx +4 -0
  237. package/docs/discoverability/metadata.mdx +17 -0
  238. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  239. package/docs/references/api-pages.mdx +74 -0
  240. package/docs/references/asyncapi.mdx +4 -2
  241. package/docs/references/graphql.mdx +14 -2
  242. package/docs/references/meta.ts +1 -1
  243. package/docs/references/openapi.mdx +148 -7
  244. package/docs/references/scalar.mdx +3 -1
  245. package/package.json +43 -8
  246. package/skills/blume/SKILL.md +3 -0
  247. package/skills/blume-migrate/SKILL.md +6 -6
  248. package/skills/blume-migrate/references/docusaurus.md +7 -7
  249. package/skills/blume-migrate/references/fumadocs.md +17 -13
  250. package/skills/blume-migrate/references/mintlify.md +37 -19
  251. package/skills/blume-migrate/references/nextra.md +10 -7
  252. package/skills/blume-migrate/references/starlight.md +11 -8
  253. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  254. package/skills/blume-write-skill/SKILL.md +82 -0
  255. package/src/ai/agent-readability.ts +2 -1
  256. package/src/ai/agent-surface.ts +9 -7
  257. package/src/ai/ai-catalog.ts +6 -3
  258. package/src/ai/ask-context.ts +103 -20
  259. package/src/ai/ask-data.ts +6 -0
  260. package/src/ai/ask-limits.ts +13 -0
  261. package/src/ai/ask-tools.ts +142 -0
  262. package/src/ai/ask.ts +354 -76
  263. package/src/ai/changelog-markdown.ts +11 -6
  264. package/src/ai/component-markdown.ts +76 -1
  265. package/src/ai/cors.ts +14 -3
  266. package/src/ai/index.ts +12 -0
  267. package/src/ai/llms.ts +6 -2
  268. package/src/ai/markdown.ts +2 -1
  269. package/src/ai/mcp/data.ts +6 -0
  270. package/src/ai/mcp/query.ts +14 -6
  271. package/src/ai/openapi-components.ts +36 -6
  272. package/src/ai/relative-links.ts +4 -38
  273. package/src/ai/site-skill.ts +274 -0
  274. package/src/ai/skills.ts +35 -2
  275. package/src/ai/write-skill.ts +97 -0
  276. package/src/astro/generate.ts +210 -66
  277. package/src/astro/index.ts +1 -0
  278. package/src/astro/integration.ts +42 -4
  279. package/src/astro/markdown-negotiation.ts +3 -0
  280. package/src/astro/module-types.ts +9 -1
  281. package/src/astro/pages.ts +23 -0
  282. package/src/astro/templates.ts +406 -108
  283. package/src/astro/variables.ts +31 -0
  284. package/src/audit/catalog.ts +38 -3
  285. package/src/audit/checks/contrast.ts +183 -0
  286. package/src/audit/checks/i18n.ts +2 -3
  287. package/src/audit/checks/indexability.ts +2 -3
  288. package/src/audit/checks/links.ts +19 -13
  289. package/src/audit/checks/network.ts +110 -6
  290. package/src/audit/checks/redirects.ts +6 -4
  291. package/src/audit/checks/robots.ts +58 -2
  292. package/src/audit/checks/sitemap.ts +2 -3
  293. package/src/audit/redirects.ts +46 -15
  294. package/src/audit/run.ts +5 -0
  295. package/src/audit/types.ts +4 -1
  296. package/src/audit/url.ts +14 -5
  297. package/src/blume-modules.d.ts +12 -1
  298. package/src/captcha/client.ts +152 -0
  299. package/src/captcha/hcaptcha.ts +36 -0
  300. package/src/captcha/index.ts +28 -0
  301. package/src/captcha/schema.ts +32 -0
  302. package/src/captcha/turnstile.ts +38 -0
  303. package/src/captcha/verify.ts +71 -0
  304. package/src/cli/args.ts +51 -0
  305. package/src/cli/build-failure.ts +23 -0
  306. package/src/cli/command-meta.ts +5 -0
  307. package/src/cli/commands/audit.ts +10 -1
  308. package/src/cli/commands/dev.ts +18 -2
  309. package/src/cli/commands/doctor.ts +67 -28
  310. package/src/cli/commands/eval.ts +36 -4
  311. package/src/cli/commands/init.ts +6 -4
  312. package/src/cli/commands/skill.ts +96 -0
  313. package/src/cli/commands/validate.ts +10 -1
  314. package/src/cli/index.ts +5 -0
  315. package/src/cli/init/scaffold.ts +3 -3
  316. package/src/cli/prepare.ts +6 -3
  317. package/src/cli/required-secrets.ts +72 -30
  318. package/src/components/BlumePage.astro +2 -0
  319. package/src/components/content/ApiEndpoint.astro +18 -0
  320. package/src/components/content/ApiField.astro +79 -0
  321. package/src/components/content/ApiPlayground.astro +66 -0
  322. package/src/components/content/ApiRail.astro +19 -0
  323. package/src/components/content/Color.astro +1 -0
  324. package/src/components/content/FileTree.astro +1 -0
  325. package/src/components/content/GithubInfo.astro +1 -0
  326. package/src/components/content/Panel.astro +1 -0
  327. package/src/components/content/ParamField.astro +25 -0
  328. package/src/components/content/RequestExample.astro +17 -0
  329. package/src/components/content/ResponseExample.astro +17 -0
  330. package/src/components/content/ResponseField.astro +32 -0
  331. package/src/components/content/Step.astro +1 -0
  332. package/src/components/content/Tile.astro +1 -0
  333. package/src/components/content/Tooltip.astro +1 -0
  334. package/src/components/content/Tree.astro +1 -0
  335. package/src/components/content/TypeTable.astro +4 -1
  336. package/src/components/content/View.astro +21 -0
  337. package/src/components/content/ViewSwitcher.astro +168 -0
  338. package/src/components/content/api-field.ts +103 -0
  339. package/src/components/content/api-page.ts +248 -0
  340. package/src/components/content/mermaid-element.ts +15 -1
  341. package/src/components/content/view-switcher.ts +71 -0
  342. package/src/components/index.ts +2 -0
  343. package/src/components/islands/Assistant.astro +10 -1
  344. package/src/components/islands/assistant.tsx +104 -14
  345. package/src/components/islands/code-question.ts +62 -0
  346. package/src/components/islands/hooks.ts +136 -21
  347. package/src/components/islands/support-link.ts +68 -0
  348. package/src/components/layout/Analytics.astro +11 -2
  349. package/src/components/layout/Banner.astro +14 -10
  350. package/src/components/layout/Breadcrumbs.astro +6 -2
  351. package/src/components/layout/ConsentBanner.astro +62 -0
  352. package/src/components/layout/ConsentHead.astro +38 -0
  353. package/src/components/layout/Directory.astro +94 -0
  354. package/src/components/layout/DrawerSwitchers.astro +51 -0
  355. package/src/components/layout/Empty.astro +2 -2
  356. package/src/components/layout/Header.astro +122 -80
  357. package/src/components/layout/LanguageSwitcher.astro +48 -15
  358. package/src/components/layout/NarrationPlayer.astro +1085 -0
  359. package/src/components/layout/NavSelector.astro +33 -7
  360. package/src/components/layout/PageActions.astro +139 -24
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +71 -3
  363. package/src/components/layout/ReferenceLayout.astro +17 -6
  364. package/src/components/layout/RelatedPages.astro +42 -0
  365. package/src/components/layout/RootLayout.astro +318 -47
  366. package/src/components/layout/Search.astro +47 -19
  367. package/src/components/layout/SiteFooter.astro +124 -0
  368. package/src/components/layout/SiteMetatags.astro +16 -0
  369. package/src/components/layout/TableOfContents.astro +24 -17
  370. package/src/components/layout/consent/native.ts +79 -0
  371. package/src/components/layout/consent/types.ts +37 -0
  372. package/src/components/layout/footer-links.ts +53 -0
  373. package/src/components/layout/footer-socials.ts +87 -0
  374. package/src/components/layout/head-scripts.ts +6 -1
  375. package/src/components/layout/locale-redirect.ts +107 -0
  376. package/src/components/layout/nav-utils.ts +83 -0
  377. package/src/components/layout/page-feedback.ts +96 -0
  378. package/src/components/layout/search/algolia.ts +10 -1
  379. package/src/components/layout/search/analytics.ts +126 -0
  380. package/src/components/layout/search/endpoint.ts +48 -5
  381. package/src/components/layout/search/flexsearch.ts +17 -5
  382. package/src/components/layout/search/orama-cloud.ts +26 -14
  383. package/src/components/layout/search/pagefind.ts +114 -5
  384. package/src/components/layout/search/types.ts +32 -0
  385. package/src/components/layout/search/typesense.ts +19 -3
  386. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  387. package/src/components/openapi/Authorization.astro +10 -0
  388. package/src/components/openapi/GraphqlOperation.astro +5 -0
  389. package/src/components/openapi/Operation.astro +184 -13
  390. package/src/components/openapi/ParametersTable.astro +8 -4
  391. package/src/components/openapi/Playground.astro +7 -1
  392. package/src/components/openapi/RequestBody.astro +24 -5
  393. package/src/components/openapi/RequestPanel.astro +59 -7
  394. package/src/components/openapi/Responses.astro +7 -4
  395. package/src/components/openapi/SchemaTable.astro +15 -3
  396. package/src/components/openapi/async-snippets.ts +65 -9
  397. package/src/components/openapi/async.ts +123 -29
  398. package/src/components/openapi/code-samples.ts +97 -0
  399. package/src/components/openapi/graphql-helpers.ts +35 -12
  400. package/src/components/openapi/helpers.ts +36 -2
  401. package/src/components/openapi/message-model.ts +54 -11
  402. package/src/components/openapi/message.ts +14 -4
  403. package/src/components/openapi/operation-model.ts +16 -7
  404. package/src/components/openapi/playground-client.ts +24 -5
  405. package/src/components/openapi/playground-schema.ts +1 -1
  406. package/src/components/openapi/schema-tree.ts +44 -14
  407. package/src/components/openapi/security.ts +16 -3
  408. package/src/components/openapi/snippets.ts +873 -10
  409. package/src/components/openapi/validate-json.ts +4 -3
  410. package/src/components/props.ts +6 -0
  411. package/src/consent/client.ts +95 -0
  412. package/src/consent/clients.ts +19 -0
  413. package/src/consent/ethyca.ts +71 -0
  414. package/src/consent/head.ts +24 -0
  415. package/src/consent/index.ts +37 -0
  416. package/src/consent/init.ts +29 -0
  417. package/src/consent/native.ts +36 -0
  418. package/src/consent/osano.ts +59 -0
  419. package/src/consent/schema.ts +30 -0
  420. package/src/core/builtin-tags.ts +5 -0
  421. package/src/core/changelog-index.ts +31 -1
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/component-diagnostics.ts +53 -0
  424. package/src/core/component-overrides.ts +32 -0
  425. package/src/core/config-input.ts +225 -23
  426. package/src/core/data.ts +46 -4
  427. package/src/core/diagnostics.ts +8 -2
  428. package/src/core/footer.ts +26 -0
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/i18n-ui.ts +84 -2
  431. package/src/core/includes.ts +21 -4
  432. package/src/core/links.ts +63 -19
  433. package/src/core/localizable.ts +26 -0
  434. package/src/core/navigation.ts +43 -12
  435. package/src/core/page-modes.ts +56 -0
  436. package/src/core/probe.ts +26 -6
  437. package/src/core/project-graph.ts +163 -16
  438. package/src/core/redirect-patterns.ts +520 -0
  439. package/src/core/related.ts +131 -0
  440. package/src/core/schema.ts +339 -66
  441. package/src/core/sources/cache.ts +6 -0
  442. package/src/core/sources/contentful-rich-text.ts +29 -6
  443. package/src/core/sources/contentful.ts +135 -25
  444. package/src/core/sources/filesystem.ts +26 -4
  445. package/src/core/sources/github-releases.ts +12 -1
  446. package/src/core/sources/lexical.ts +15 -10
  447. package/src/core/sources/mdx-remote.ts +62 -10
  448. package/src/core/sources/normalize.ts +228 -41
  449. package/src/core/sources/notion.ts +7 -1
  450. package/src/core/sources/obsidian.ts +16 -3
  451. package/src/core/sources/payload.ts +38 -15
  452. package/src/core/sources/read.ts +8 -3
  453. package/src/core/sources/remote.ts +72 -15
  454. package/src/core/sources/resolve.ts +11 -1
  455. package/src/core/sources/sanity.ts +43 -25
  456. package/src/core/sources/strapi.ts +2 -2
  457. package/src/core/types.ts +19 -1
  458. package/src/core/ui-packs/ar.ts +44 -0
  459. package/src/core/ui-packs/bg.ts +46 -0
  460. package/src/core/ui-packs/bn.ts +44 -0
  461. package/src/core/ui-packs/ca.ts +47 -0
  462. package/src/core/ui-packs/cs.ts +45 -0
  463. package/src/core/ui-packs/da.ts +46 -0
  464. package/src/core/ui-packs/de.ts +47 -0
  465. package/src/core/ui-packs/el.ts +46 -0
  466. package/src/core/ui-packs/es.ts +47 -0
  467. package/src/core/ui-packs/fa.ts +46 -0
  468. package/src/core/ui-packs/fi.ts +46 -0
  469. package/src/core/ui-packs/fr.ts +47 -0
  470. package/src/core/ui-packs/he.ts +44 -0
  471. package/src/core/ui-packs/hi.ts +45 -0
  472. package/src/core/ui-packs/hr.ts +46 -0
  473. package/src/core/ui-packs/hu.ts +45 -0
  474. package/src/core/ui-packs/id.ts +46 -0
  475. package/src/core/ui-packs/it.ts +46 -0
  476. package/src/core/ui-packs/ja.ts +46 -0
  477. package/src/core/ui-packs/ko.ts +45 -0
  478. package/src/core/ui-packs/nl.ts +47 -0
  479. package/src/core/ui-packs/no.ts +45 -0
  480. package/src/core/ui-packs/pl.ts +46 -0
  481. package/src/core/ui-packs/pt-br.ts +47 -0
  482. package/src/core/ui-packs/pt.ts +46 -0
  483. package/src/core/ui-packs/ro.ts +46 -0
  484. package/src/core/ui-packs/ru.ts +46 -0
  485. package/src/core/ui-packs/sk.ts +45 -0
  486. package/src/core/ui-packs/sr.ts +46 -0
  487. package/src/core/ui-packs/sv.ts +45 -0
  488. package/src/core/ui-packs/th.ts +44 -0
  489. package/src/core/ui-packs/tr.ts +46 -0
  490. package/src/core/ui-packs/uk.ts +46 -0
  491. package/src/core/ui-packs/vi.ts +46 -0
  492. package/src/core/ui-packs/zh-tw.ts +44 -0
  493. package/src/core/ui-packs/zh.ts +44 -0
  494. package/src/core/variables.ts +103 -0
  495. package/src/deploy/adapter-output.ts +7 -2
  496. package/src/deploy/adapters/cloudflare.ts +7 -5
  497. package/src/deploy/adapters/node.ts +47 -9
  498. package/src/deploy/adapters/types.ts +7 -3
  499. package/src/deploy/artifacts.ts +79 -9
  500. package/src/deploy/cloudflare-negotiation.ts +35 -5
  501. package/src/deploy/headers.ts +2 -1
  502. package/src/deploy/node-headers.ts +63 -9
  503. package/src/deploy/platforms/cloudflare.ts +45 -9
  504. package/src/deploy/platforms/netlify.ts +35 -2
  505. package/src/deploy/platforms/node.ts +12 -2
  506. package/src/deploy/platforms/types.ts +21 -7
  507. package/src/deploy/platforms/vercel.ts +149 -17
  508. package/src/deploy/redirects.ts +151 -24
  509. package/src/deploy/rss.ts +3 -1
  510. package/src/deploy/sitemap.ts +39 -5
  511. package/src/deploy/vercel-negotiation.ts +326 -102
  512. package/src/eval/agents.ts +18 -1
  513. package/src/eval/prompts.ts +6 -3
  514. package/src/eval/report.ts +34 -13
  515. package/src/eval/run.ts +5 -0
  516. package/src/markdown/api-rail.ts +259 -0
  517. package/src/markdown/code-title.ts +35 -9
  518. package/src/markdown/features.ts +3 -2
  519. package/src/markdown/fence-meta.ts +2 -0
  520. package/src/markdown/include.ts +11 -1
  521. package/src/markdown/index.ts +28 -5
  522. package/src/markdown/math.ts +4 -4
  523. package/src/markdown/relative-links.ts +3 -2
  524. package/src/markdown/variables.ts +56 -0
  525. package/src/markdown/views.ts +82 -0
  526. package/src/narration/build.ts +412 -0
  527. package/src/narration/player.ts +249 -0
  528. package/src/narration/provider.ts +29 -0
  529. package/src/narration/script.ts +592 -0
  530. package/src/og/card.ts +7 -6
  531. package/src/openapi/checks.ts +273 -0
  532. package/src/openapi/graphql-build.ts +21 -0
  533. package/src/openapi/graphql.ts +11 -0
  534. package/src/openapi/model.ts +72 -27
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +182 -4
  537. package/src/openapi/references.ts +64 -8
  538. package/src/openapi/render-mdx.ts +5 -1
  539. package/src/openapi/scalar.ts +40 -6
  540. package/src/openapi/source.ts +40 -15
  541. package/src/ratelimit/cloudflare.ts +54 -0
  542. package/src/ratelimit/index.ts +30 -0
  543. package/src/ratelimit/memory.ts +45 -0
  544. package/src/ratelimit/runtime.ts +247 -0
  545. package/src/ratelimit/schema.ts +28 -0
  546. package/src/ratelimit/upstash.ts +60 -0
  547. package/src/ratelimit/wrangler.ts +70 -0
  548. package/src/reference/asyncapi.ts +2 -2
  549. package/src/reference/graphql.ts +20 -3
  550. package/src/reference/index.ts +1 -0
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +80 -8
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +23 -5
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/locale-scope.ts +30 -0
  557. package/src/search/adapters/typesense.ts +14 -4
  558. package/src/search/build.ts +21 -3
  559. package/src/search/documents.ts +26 -2
  560. package/src/search/orama-index.ts +191 -34
  561. package/src/search/pagefind-ranking.ts +39 -0
  562. package/src/search/source-pages.ts +29 -0
  563. package/src/search/sync/algolia.ts +111 -10
  564. package/src/search/sync/index.ts +52 -13
  565. package/src/search/sync/orama-cloud.ts +3 -0
  566. package/src/search/sync/typesense.ts +76 -26
  567. package/src/seo/metatags.ts +67 -0
  568. package/src/sources/contentful.ts +12 -0
  569. package/src/sources/github-releases.ts +7 -1
  570. package/src/sources/shared.ts +3 -0
  571. package/src/theme/chrome-icons.ts +1 -0
  572. package/src/theme/color.ts +500 -0
  573. package/src/theme/entry.ts +85 -6
  574. package/src/theme/palette.ts +121 -45
  575. package/src/translate/prompts.ts +1 -0
  576. package/src/translate/validate.ts +31 -3
  577. package/src/upgrade/upgrade.ts +4 -171
  578. package/dist/cli/chunk-1d7ve1dm.js.map +0 -12
  579. package/dist/cli/chunk-2hsdwb9n.js.map +0 -10
  580. package/dist/cli/chunk-35d4wj9f.js.map +0 -15
  581. package/dist/cli/chunk-364znk6q.js +0 -75
  582. package/dist/cli/chunk-364znk6q.js.map +0 -11
  583. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  584. package/dist/cli/chunk-8ktnccpt.js.map +0 -10
  585. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  586. package/dist/cli/chunk-b07cmahc.js +0 -145
  587. package/dist/cli/chunk-b07cmahc.js.map +0 -11
  588. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  589. package/dist/cli/chunk-e04dxsz1.js +0 -39
  590. package/dist/cli/chunk-e04dxsz1.js.map +0 -10
  591. package/dist/cli/chunk-j85vccga.js.map +0 -36
  592. package/dist/cli/chunk-p3v96n38.js.map +0 -10
  593. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  594. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  595. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  596. package/dist/cli/chunk-vg9r4eb9.js.map +0 -14
  597. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  598. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  599. package/docs/content/sources.mdx +0 -334
  600. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-3v3ffbrj.js.map} +0 -0
  601. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-8y2kmjv8.js.map} +0 -0
  602. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-c887fj0s.js.map} +0 -0
  603. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-dzwehpy7.js.map} +0 -0
  604. /package/dist/cli/{chunk-2z928egk.js.map → chunk-ft9vfys6.js.map} +0 -0
  605. /package/dist/cli/{chunk-a58773jm.js.map → chunk-kma9f6sf.js.map} +0 -0
  606. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-sds923fa.js.map} +0 -0
  607. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-tdqhyxfz.js.map} +0 -0
@@ -6,6 +6,7 @@ import {
6
6
  i18nDiagnostics,
7
7
  blumeReferences,
8
8
  frontmatter_default,
9
+ resolveLocalizable,
9
10
  resolveDocsCollection,
10
11
  CHANGELOG_INDEX_ROUTE,
11
12
  hasChangelogIndex,
@@ -22,6 +23,11 @@ import {
22
23
  pagesByRef,
23
24
  buildNavigation,
24
25
  PUBLIC_API_URL,
26
+ isPatternPath,
27
+ pathsUnderPattern,
28
+ hasVariables,
29
+ substituteVariables,
30
+ undefinedVariables,
25
31
  pageMetaSchema,
26
32
  folderMetaSchema,
27
33
  versionizeRoute,
@@ -29,6 +35,7 @@ import {
29
35
  slugify,
30
36
  slugifyPath,
31
37
  unloadablePathDiagnostic,
38
+ frontmatterYamlDiagnostic,
32
39
  extractHeadings,
33
40
  strippedLineOffset,
34
41
  isStringValue,
@@ -59,15 +66,17 @@ import {
59
66
  neutralizeUnsafeLinks,
60
67
  columnsPrefix,
61
68
  openApiSource,
62
- BLUME_IGNORE_DIRS,
63
- baselineScanIgnore,
64
- BLUME_WATCH_IGNORE_DIRS,
65
- excludeDirSegments,
66
- ignoringWatchListener,
69
+ isJsonObject,
70
+ getPath,
71
+ isStringValue2,
72
+ asString,
73
+ asObject,
74
+ asNumber,
75
+ objectsIn,
67
76
  createModuleLoader,
68
77
  resolveProjectContext,
69
78
  loadConfig
70
- } from "./chunk-pv29h0wf.js";
79
+ } from "./chunk-ehweg4fr.js";
71
80
  import {
72
81
  getBlumeVersion,
73
82
  BlumeError,
@@ -75,232 +84,18 @@ import {
75
84
  } from "./index.js";
76
85
  import {
77
86
  trimChar,
87
+ normalizePath,
78
88
  isInternalPath,
79
89
  withBasePath,
80
90
  mountBasePath
81
- } from "./chunk-3em5wd2y.js";
82
-
83
- // src/core/manifest.ts
84
- var versionAlternateKey = (versionKey, locale) => `${versionKey}\x00${locale}`;
85
- var versionAlternatesFor = (byKey, versionKey, locale) => {
86
- const key = versionAlternateKey(versionKey, locale);
87
- const existing = byKey.get(key);
88
- if (existing) {
89
- return existing;
90
- }
91
- const list = [];
92
- byKey.set(key, list);
93
- return list;
94
- };
95
- var MANIFEST_VERSION = 1;
96
- var contentIndexable = (page, config) => !page.meta.search.exclude && (!page.meta.sidebar.hidden || config.search.indexing.includeHiddenPages);
97
- var routeDescription = (page) => page.meta.seo.description ?? page.description;
98
- var buildFallbackRoutes = (graph, i18n, alternatesByKey, basePath, versionAlternatesByKey) => {
99
- const fallback = resolveFallbackLocale(i18n);
100
- if (!fallback) {
101
- return [];
102
- }
103
- const fallbackPages = new Map(graph.pages.flatMap((page) => page.locale === fallback && !page.monolingual ? [[page.translationKey, page]] : []));
104
- const routes = [];
105
- for (const { code } of i18n.locales) {
106
- if (code === fallback) {
107
- continue;
108
- }
109
- const present = new Set(graph.pages.flatMap((page) => page.locale === code ? [page.translationKey] : []));
110
- for (const [key, source] of fallbackPages) {
111
- if (present.has(key)) {
112
- continue;
113
- }
114
- const path = mountBasePath(basePath, localizeRoute(key, code, i18n));
115
- const versionAlternates = versionAlternatesByKey ? versionAlternatesFor(versionAlternatesByKey, source.versionKey, code) : [];
116
- if (versionAlternatesByKey) {
117
- versionAlternates.push({ path, version: source.version });
118
- }
119
- routes.push({
120
- alternates: alternatesByKey.get(key) ?? [],
121
- collection: source.collection ?? "docs",
122
- contentType: source.contentType,
123
- description: routeDescription(source),
124
- draft: source.meta.draft,
125
- editUrl: source.editUrl,
126
- entryId: source.entryId ?? source.source.ref,
127
- fallback: true,
128
- hidden: source.meta.sidebar.hidden,
129
- id: source.id,
130
- indexable: false,
131
- lastModified: source.lastModified,
132
- locale: code,
133
- monolingual: source.monolingual,
134
- path,
135
- source: source.source,
136
- sourcePath: source.sourcePath,
137
- title: source.title,
138
- version: source.version,
139
- versionAlternates
140
- });
141
- }
142
- }
143
- return routes;
144
- };
145
- var buildManifest = (options) => {
146
- const { context, config, graph } = options;
147
- const searchEnabled = config.search.provider.mode !== "none";
148
- const { i18n } = config;
149
- const alternatesByKey = new Map;
150
- if (i18n) {
151
- for (const page of graph.pages) {
152
- const list = alternatesByKey.get(page.translationKey) ?? [];
153
- list.push({ locale: page.locale, path: page.route });
154
- alternatesByKey.set(page.translationKey, list);
155
- }
156
- }
157
- const versionAlternatesByKey = config.versions ? new Map : undefined;
158
- if (versionAlternatesByKey) {
159
- for (const page of graph.pages) {
160
- versionAlternatesFor(versionAlternatesByKey, page.versionKey, page.locale).push({ path: page.route, version: page.version });
161
- }
162
- }
163
- const routes = graph.pages.map((page) => ({
164
- aiExclude: page.meta.ai.exclude,
165
- alternates: alternatesByKey.get(page.translationKey) ?? [],
166
- collection: page.collection ?? "docs",
167
- contentType: page.contentType,
168
- description: routeDescription(page),
169
- draft: page.meta.draft,
170
- editUrl: page.editUrl,
171
- entryId: page.entryId ?? page.source.ref,
172
- hidden: page.meta.sidebar.hidden,
173
- id: page.id,
174
- indexable: searchEnabled && contentIndexable(page, config),
175
- lastModified: page.lastModified,
176
- locale: page.locale,
177
- monolingual: page.monolingual,
178
- path: page.route,
179
- source: page.source,
180
- sourcePath: page.sourcePath,
181
- title: page.title,
182
- version: page.version,
183
- versionAlternates: versionAlternatesByKey ? versionAlternatesFor(versionAlternatesByKey, page.versionKey, page.locale) : []
184
- }));
185
- if (i18n) {
186
- routes.push(...buildFallbackRoutes(graph, i18n, alternatesByKey, config.basePath, versionAlternatesByKey));
187
- }
188
- if (versionAlternatesByKey && config.versions) {
189
- const rank = new Map(config.versions.archived.map((version, index) => [version.id, index + 1]));
190
- for (const list of versionAlternatesByKey.values()) {
191
- list.sort((a, b) => (rank.get(a.version) ?? 0) - (rank.get(b.version) ?? 0));
192
- }
193
- }
194
- routes.sort((a, b) => a.path.localeCompare(b.path));
195
- return {
196
- blumeVersion: getBlumeVersion(),
197
- contentRoot: context.contentRoot,
198
- output: config.deployment.options.output,
199
- projectRoot: context.root,
200
- routes,
201
- version: MANIFEST_VERSION
202
- };
203
- };
204
-
205
- // src/core/project-graph.ts
206
- import { isAbsolute as isAbsolute3, relative as relative5 } from "pathe";
207
-
208
- // src/core/directive-diagnostics.ts
209
- import { mdxToMdast } from "satteri";
210
-
211
- // src/markdown/directives.ts
212
- import { toString as mdastToString } from "mdast-util-to-string";
213
- var CALLOUT_TYPES = new Set([
214
- "danger",
215
- "info",
216
- "note",
217
- "success",
218
- "tip",
219
- "warning"
220
- ]);
221
- var CALLOUT_ALIASES = {
222
- caution: "warning",
223
- error: "danger",
224
- important: "note",
225
- warn: "warning"
226
- };
227
- var calloutTypeFor = (name) => {
228
- const lower = name.toLowerCase();
229
- if (CALLOUT_TYPES.has(lower)) {
230
- return lower;
231
- }
232
- return CALLOUT_ALIASES[lower] ?? null;
233
- };
234
- var LITERAL_MARKERS = new Map([
235
- ["leafDirective", "::"],
236
- ["textDirective", ":"]
237
- ]);
238
- var labelSource = (label) => label.length > 0 ? `[${mdastToString(label, { includeImageAlt: false })}]` : "";
239
- var attributeSource = (node) => {
240
- const attributes = Object.entries(node.attributes ?? {}).map(([key, value]) => value ? `${key}="${value}"` : key);
241
- return attributes.length > 0 ? `{${attributes.join(" ")}}` : "";
242
- };
243
- var spannedSource = (node, source) => {
244
- const start = node.position?.start?.offset;
245
- const end = node.position?.end?.offset;
246
- return start !== undefined && end !== undefined ? source.slice(start, end) : null;
247
- };
248
- var directiveSource = (node, marker, source) => {
249
- const opening = `${marker}${node.name}`;
250
- const slice = spannedSource(node, source);
251
- if (slice?.startsWith(opening)) {
252
- return slice;
253
- }
254
- return `${opening}${labelSource(node.children ?? [])}${attributeSource(node)}`;
255
- };
256
-
257
- // src/core/directive-diagnostics.ts
258
- var CONTAINER_OPENING = /^[\t >]*:{3,}(?<name>[a-z][\w-]*)/gimu;
259
- var codeList = (names) => [...names].map((name) => `\`${name}\``).join(", ");
260
- var CALLOUT_NAMES = `${codeList(CALLOUT_TYPES)} (or the aliases ${codeList(Object.keys(CALLOUT_ALIASES))})`;
261
- var unknownContainers = (text) => {
262
- const named = [...text.matchAll(CONTAINER_OPENING)].some((match) => calloutTypeFor(match.groups?.name ?? "") === null);
263
- if (!named) {
264
- return [];
265
- }
266
- let tree;
267
- try {
268
- tree = mdxToMdast(text, { features: MDX_BODY_FEATURES });
269
- } catch {
270
- return [];
271
- }
272
- const found = [];
273
- const walk = (node) => {
274
- if (node.type === "containerDirective" && calloutTypeFor(node.name) === null) {
275
- found.push({ line: node.position?.start.line ?? 1, name: node.name });
276
- }
277
- if ("children" in node) {
278
- for (const child of node.children) {
279
- walk(child);
280
- }
281
- }
282
- };
283
- walk(tree);
284
- return found;
285
- };
286
- var unknownDirectiveDiagnostics = (entry, sourceName) => {
287
- if (entry.body.format !== "mdx") {
288
- return [];
289
- }
290
- const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
291
- const offset = entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text);
292
- return unknownContainers(entry.expanded?.text ?? entry.body.text).map(({ line, name }) => {
293
- const origin = entry.expanded?.origins[line - 1];
294
- return {
295
- code: "BLUME_UNKNOWN_DIRECTIVE",
296
- file: origin?.file ?? page,
297
- line: origin?.line ?? line + offset,
298
- message: `\`:::${name}\` isn't a callout type, so the page shows its \`:::\` lines as written around its content.`,
299
- severity: "warning",
300
- suggestion: `Use a callout type — ${CALLOUT_NAMES} — or remove the \`:::\` lines to keep the content as plain prose.`
301
- };
302
- });
303
- };
91
+ } from "./chunk-zccbjcj0.js";
92
+ import {
93
+ BLUME_IGNORE_DIRS,
94
+ baselineScanIgnore,
95
+ BLUME_WATCH_IGNORE_DIRS,
96
+ excludeDirSegments,
97
+ ignoringWatchListener
98
+ } from "./chunk-kwrg63tm.js";
304
99
 
305
100
  // src/theme/icon-kind.ts
306
101
  var IMAGE_ICON = /^(?:https?:\/\/|data:image\/|\/|\.{1,2}\/)|\.(?:avif|gif|jpe?g|png|svg|webp)$/iu;
@@ -492,41 +287,263 @@ var duplicateLabelDiagnostics = (navigation) => {
492
287
  }
493
288
  }
494
289
  };
495
- const sidebars = [
496
- { nodes: navigation.sidebar, where: "at the top level" }
497
- ];
498
- for (const { nodes, where } of sidebars) {
499
- checkLevel(nodes, where);
290
+ const sidebars = [
291
+ { nodes: navigation.sidebar, where: "at the top level" }
292
+ ];
293
+ for (const { nodes, where } of sidebars) {
294
+ checkLevel(nodes, where);
295
+ }
296
+ return diagnostics;
297
+ };
298
+ var hiddenInSidebarDiagnostics = (navigation, pages) => {
299
+ const hidden = new Set(pages.flatMap((page) => page.meta.sidebar.hidden ? [page.id] : []));
300
+ if (hidden.size === 0) {
301
+ return [];
302
+ }
303
+ const sidebars = [navigation.sidebar];
304
+ const diagnostics = [];
305
+ const seen = new Set;
306
+ for (const sidebar of sidebars) {
307
+ for (const node of flattenNodes(sidebar)) {
308
+ if (node.kind === "page" && hidden.has(node.pageId) && !seen.has(node.pageId)) {
309
+ seen.add(node.pageId);
310
+ diagnostics.push({
311
+ code: "BLUME_NAV_HIDDEN_IN_SIDEBAR",
312
+ message: `Page "${node.label}" is marked hidden but appears in the sidebar (and its pagination).`,
313
+ severity: "warning",
314
+ suggestion: "Remove it from the navigation config, or unset sidebar.hidden."
315
+ });
316
+ }
317
+ }
318
+ }
319
+ return diagnostics;
320
+ };
321
+ var validateNavStructure = (navigation, pages) => [
322
+ ...duplicateLabelDiagnostics(navigation),
323
+ ...hiddenInSidebarDiagnostics(navigation, pages)
324
+ ];
325
+
326
+ // src/core/manifest.ts
327
+ var versionAlternateKey = (versionKey, locale) => `${versionKey}\x00${locale}`;
328
+ var versionAlternatesFor = (byKey, versionKey, locale) => {
329
+ const key = versionAlternateKey(versionKey, locale);
330
+ const existing = byKey.get(key);
331
+ if (existing) {
332
+ return existing;
333
+ }
334
+ const list = [];
335
+ byKey.set(key, list);
336
+ return list;
337
+ };
338
+ var MANIFEST_VERSION = 1;
339
+ var contentIndexable = (page, config) => !page.meta.search.exclude && (!page.meta.sidebar.hidden || config.search.indexing.includeHiddenPages);
340
+ var routeDescription = (page) => page.meta.seo.description ?? page.description;
341
+ var buildFallbackRoutes = (graph, i18n, alternatesByKey, basePath, versionAlternatesByKey) => {
342
+ const fallback = resolveFallbackLocale(i18n);
343
+ if (!fallback) {
344
+ return [];
345
+ }
346
+ const fallbackPages = new Map(graph.pages.flatMap((page) => page.locale === fallback && !page.monolingual ? [[page.translationKey, page]] : []));
347
+ const routes = [];
348
+ for (const { code } of i18n.locales) {
349
+ if (code === fallback) {
350
+ continue;
351
+ }
352
+ const present = new Set(graph.pages.flatMap((page) => page.locale === code ? [page.translationKey] : []));
353
+ for (const [key, source] of fallbackPages) {
354
+ if (present.has(key)) {
355
+ continue;
356
+ }
357
+ const path = mountBasePath(basePath, localizeRoute(key, code, i18n));
358
+ const versionAlternates = versionAlternatesByKey ? versionAlternatesFor(versionAlternatesByKey, source.versionKey, code) : [];
359
+ if (versionAlternatesByKey) {
360
+ versionAlternates.push({ path, version: source.version });
361
+ }
362
+ routes.push({
363
+ alternates: alternatesByKey.get(key) ?? [],
364
+ collection: source.collection ?? "docs",
365
+ contentType: source.contentType,
366
+ description: routeDescription(source),
367
+ draft: source.meta.draft,
368
+ editUrl: source.editUrl,
369
+ entryId: source.entryId ?? source.source.ref,
370
+ fallback: true,
371
+ hidden: source.meta.sidebar.hidden,
372
+ id: source.id,
373
+ indexable: false,
374
+ lastModified: source.lastModified,
375
+ locale: code,
376
+ monolingual: source.monolingual,
377
+ path,
378
+ source: source.source,
379
+ sourcePath: source.sourcePath,
380
+ title: source.title,
381
+ version: source.version,
382
+ versionAlternates
383
+ });
384
+ }
385
+ }
386
+ return routes;
387
+ };
388
+ var buildManifest = (options) => {
389
+ const { context, config, graph } = options;
390
+ const searchEnabled = config.search.provider.mode !== "none";
391
+ const { i18n } = config;
392
+ const alternatesByKey = new Map;
393
+ if (i18n) {
394
+ for (const page of graph.pages) {
395
+ const list = alternatesByKey.get(page.translationKey) ?? [];
396
+ list.push({ locale: page.locale, path: page.route });
397
+ alternatesByKey.set(page.translationKey, list);
398
+ }
399
+ }
400
+ const versionAlternatesByKey = config.versions ? new Map : undefined;
401
+ if (versionAlternatesByKey) {
402
+ for (const page of graph.pages) {
403
+ versionAlternatesFor(versionAlternatesByKey, page.versionKey, page.locale).push({ path: page.route, version: page.version });
404
+ }
405
+ }
406
+ const routes = graph.pages.map((page) => ({
407
+ aiExclude: page.meta.ai.exclude,
408
+ alternates: alternatesByKey.get(page.translationKey) ?? [],
409
+ collection: page.collection ?? "docs",
410
+ contentType: page.contentType,
411
+ description: routeDescription(page),
412
+ draft: page.meta.draft,
413
+ editUrl: page.editUrl,
414
+ entryId: page.entryId ?? page.source.ref,
415
+ hidden: page.meta.sidebar.hidden,
416
+ id: page.id,
417
+ indexable: searchEnabled && contentIndexable(page, config),
418
+ lastModified: page.lastModified,
419
+ locale: page.locale,
420
+ monolingual: page.monolingual,
421
+ path: page.route,
422
+ source: page.source,
423
+ sourcePath: page.sourcePath,
424
+ title: page.title,
425
+ version: page.version,
426
+ versionAlternates: versionAlternatesByKey ? versionAlternatesFor(versionAlternatesByKey, page.versionKey, page.locale) : []
427
+ }));
428
+ if (i18n) {
429
+ routes.push(...buildFallbackRoutes(graph, i18n, alternatesByKey, config.basePath, versionAlternatesByKey));
430
+ }
431
+ if (versionAlternatesByKey && config.versions) {
432
+ const rank = new Map(config.versions.archived.map((version, index) => [version.id, index + 1]));
433
+ for (const list of versionAlternatesByKey.values()) {
434
+ list.sort((a, b) => (rank.get(a.version) ?? 0) - (rank.get(b.version) ?? 0));
435
+ }
436
+ }
437
+ routes.sort((a, b) => a.path.localeCompare(b.path));
438
+ return {
439
+ blumeVersion: getBlumeVersion(),
440
+ contentRoot: context.contentRoot,
441
+ output: config.deployment.options.output,
442
+ projectRoot: context.root,
443
+ routes,
444
+ version: MANIFEST_VERSION
445
+ };
446
+ };
447
+
448
+ // src/core/project-graph.ts
449
+ import { isAbsolute as isAbsolute3, relative as relative5 } from "pathe";
450
+
451
+ // src/core/directive-diagnostics.ts
452
+ import { mdxToMdast } from "satteri";
453
+
454
+ // src/markdown/directives.ts
455
+ import { toString as mdastToString } from "mdast-util-to-string";
456
+ var CALLOUT_TYPES = new Set([
457
+ "danger",
458
+ "info",
459
+ "note",
460
+ "success",
461
+ "tip",
462
+ "warning"
463
+ ]);
464
+ var CALLOUT_ALIASES = {
465
+ caution: "warning",
466
+ error: "danger",
467
+ important: "note",
468
+ warn: "warning"
469
+ };
470
+ var calloutTypeFor = (name) => {
471
+ const lower = name.toLowerCase();
472
+ if (CALLOUT_TYPES.has(lower)) {
473
+ return lower;
500
474
  }
501
- return diagnostics;
475
+ return CALLOUT_ALIASES[lower] ?? null;
502
476
  };
503
- var hiddenInSidebarDiagnostics = (navigation, pages) => {
504
- const hidden = new Set(pages.flatMap((page) => page.meta.sidebar.hidden ? [page.id] : []));
505
- if (hidden.size === 0) {
477
+ var LITERAL_MARKERS = new Map([
478
+ ["leafDirective", "::"],
479
+ ["textDirective", ":"]
480
+ ]);
481
+ var labelSource = (label) => label.length > 0 ? `[${mdastToString(label, { includeImageAlt: false })}]` : "";
482
+ var attributeSource = (node) => {
483
+ const attributes = Object.entries(node.attributes ?? {}).map(([key, value]) => value ? `${key}="${value}"` : key);
484
+ return attributes.length > 0 ? `{${attributes.join(" ")}}` : "";
485
+ };
486
+ var spannedSource = (node, source) => {
487
+ const start = node.position?.start?.offset;
488
+ const end = node.position?.end?.offset;
489
+ return start !== undefined && end !== undefined ? source.slice(start, end) : null;
490
+ };
491
+ var directiveSource = (node, marker, source) => {
492
+ const opening = `${marker}${node.name}`;
493
+ const slice = spannedSource(node, source);
494
+ if (slice?.startsWith(opening)) {
495
+ return slice;
496
+ }
497
+ return `${opening}${labelSource(node.children ?? [])}${attributeSource(node)}`;
498
+ };
499
+
500
+ // src/core/directive-diagnostics.ts
501
+ var CONTAINER_OPENING = /^[\t >]*:{3,}(?<name>[a-z][\w-]*)/gimu;
502
+ var codeList = (names) => [...names].map((name) => `\`${name}\``).join(", ");
503
+ var CALLOUT_NAMES = `${codeList(CALLOUT_TYPES)} (or the aliases ${codeList(Object.keys(CALLOUT_ALIASES))})`;
504
+ var unknownContainers = (text) => {
505
+ const named = [...text.matchAll(CONTAINER_OPENING)].some((match) => calloutTypeFor(match.groups?.name ?? "") === null);
506
+ if (!named) {
506
507
  return [];
507
508
  }
508
- const sidebars = [navigation.sidebar];
509
- const diagnostics = [];
510
- const seen = new Set;
511
- for (const sidebar of sidebars) {
512
- for (const node of flattenNodes(sidebar)) {
513
- if (node.kind === "page" && hidden.has(node.pageId) && !seen.has(node.pageId)) {
514
- seen.add(node.pageId);
515
- diagnostics.push({
516
- code: "BLUME_NAV_HIDDEN_IN_SIDEBAR",
517
- message: `Page "${node.label}" is marked hidden but appears in the sidebar (and its pagination).`,
518
- severity: "warning",
519
- suggestion: "Remove it from the navigation config, or unset sidebar.hidden."
520
- });
509
+ let tree;
510
+ try {
511
+ tree = mdxToMdast(text, { features: MDX_BODY_FEATURES });
512
+ } catch {
513
+ return [];
514
+ }
515
+ const found = [];
516
+ const walk = (node) => {
517
+ if (node.type === "containerDirective" && calloutTypeFor(node.name) === null) {
518
+ found.push({ line: node.position?.start.line ?? 1, name: node.name });
519
+ }
520
+ if ("children" in node) {
521
+ for (const child of node.children) {
522
+ walk(child);
521
523
  }
522
524
  }
525
+ };
526
+ walk(tree);
527
+ return found;
528
+ };
529
+ var unknownDirectiveDiagnostics = (entry, sourceName) => {
530
+ if (entry.body.format !== "mdx") {
531
+ return [];
523
532
  }
524
- return diagnostics;
533
+ const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
534
+ const offset = entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text);
535
+ return unknownContainers(entry.expanded?.text ?? entry.body.text).map(({ line, name }) => {
536
+ const origin = entry.expanded?.origins[line - 1];
537
+ return {
538
+ code: "BLUME_UNKNOWN_DIRECTIVE",
539
+ file: origin?.file ?? page,
540
+ line: origin?.line ?? line + offset,
541
+ message: `\`:::${name}\` isn't a callout type, so the page shows its \`:::\` lines as written around its content.`,
542
+ severity: "warning",
543
+ suggestion: `Use a callout type — ${CALLOUT_NAMES} — or remove the \`:::\` lines to keep the content as plain prose.`
544
+ };
545
+ });
525
546
  };
526
- var validateNavStructure = (navigation, pages) => [
527
- ...duplicateLabelDiagnostics(navigation),
528
- ...hiddenInSidebarDiagnostics(navigation, pages)
529
- ];
530
547
 
531
548
  // src/core/graph.ts
532
549
  var collectRoutes = (pages) => {
@@ -566,13 +583,6 @@ var localePagesFor = (code, real, fallback, fallbackByKey, i18n, basePath) => {
566
583
  }
567
584
  return [...real, ...filled];
568
585
  };
569
- var isSingleLabel = (label) => typeof label === "string";
570
- var resolveLabel = (label, locale, defaultLocale) => {
571
- if (isSingleLabel(label)) {
572
- return label;
573
- }
574
- return label[locale] ?? (defaultLocale === undefined ? undefined : label[defaultLocale]) ?? Object.values(label)[0] ?? "";
575
- };
576
586
  var localizeSidebarHrefs = (items, localize) => items.map((item) => {
577
587
  if (isPageRef(item)) {
578
588
  return item;
@@ -590,10 +600,19 @@ var resolveTabLabels = (tabs, locale, defaultLocale) => (tabs ?? []).map((tab) =
590
600
  ...tab,
591
601
  items: tab.items?.map((item) => ({
592
602
  ...item,
593
- label: resolveLabel(item.label, locale, defaultLocale)
603
+ label: resolveLocalizable(item.label, locale, defaultLocale)
594
604
  })),
595
- label: resolveLabel(tab.label, locale, defaultLocale)
605
+ label: resolveLocalizable(tab.label, locale, defaultLocale)
596
606
  }));
607
+ var withLabel = (link, locale, defaultLocale) => ({
608
+ ...link,
609
+ label: resolveLocalizable(link.label, locale, defaultLocale)
610
+ });
611
+ var resolveHeaderLinks = (navigation, locale, defaultLocale) => ({
612
+ actions: navigation.actions.map((link) => withLabel(link, locale, defaultLocale)),
613
+ cta: navigation.cta ? withLabel(navigation.cta, locale, defaultLocale) : null,
614
+ featured: navigation.featured.map((link) => withLabel(link, locale, defaultLocale))
615
+ });
597
616
  var mountConfiguredLink = (path, basePath, extraRoutes) => extraRoutes.has(path) ? path : withBasePath(basePath, path);
598
617
  var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n, diagnostics, version = "") => {
599
618
  const basePath = options.basePath ?? "";
@@ -637,17 +656,17 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
637
656
  ...item,
638
657
  href: localizeServed(item.href, false)
639
658
  });
640
- const { actions, cta, featured } = options.navigation;
659
+ const { actions, cta, featured } = resolveHeaderLinks(options.navigation, code, i18n.defaultLocale);
641
660
  const sidebarItems = options.navigation.sidebar.items;
642
661
  const navigation = buildNavigation(localePages, {
643
- actions: actions?.map(localizeHref),
662
+ actions: actions.map(localizeHref),
644
663
  basePath,
645
664
  cta: cta ? localizeHref(cta) : null,
646
665
  diagnostics,
647
666
  display: options.navigation.sidebar.display,
648
667
  extraRoutes: options.extraRoutes,
649
668
  fallbackMetaPrefix,
650
- featured: featured?.map(localizeHref),
669
+ featured: featured.map(localizeHref),
651
670
  folderMeta: options.folderMeta,
652
671
  localizedRoot: localizeRoute(versionizeRoute("/", version), code, i18n),
653
672
  metaPrefix: [version, localeDir].filter(Boolean).join("/"),
@@ -709,13 +728,11 @@ var buildVersionNavigation = (id, versionPages, options, diagnostics) => {
709
728
  }
710
729
  return {
711
730
  "": buildNavigation(versionPages, {
712
- actions: options.navigation.actions,
731
+ ...resolveHeaderLinks(options.navigation, ""),
713
732
  basePath: options.basePath ?? "",
714
- cta: options.navigation.cta,
715
733
  diagnostics,
716
734
  display: options.navigation.sidebar.display,
717
735
  extraRoutes: options.extraRoutes,
718
- featured: options.navigation.featured,
719
736
  folderMeta: options.folderMeta,
720
737
  localizedRoot: versionizeRoute("/", id),
721
738
  metaPrefix: id,
@@ -737,13 +754,11 @@ var buildContentGraph = (pages, options) => {
737
754
  ({ navigation, navigationByLocale } = buildI18nNavigation(currentPages, options, i18n, diagnostics));
738
755
  } else {
739
756
  navigation = buildNavigation(currentPages, {
740
- actions: options.navigation.actions,
757
+ ...resolveHeaderLinks(options.navigation, ""),
741
758
  basePath: options.basePath ?? "",
742
- cta: options.navigation.cta,
743
759
  diagnostics,
744
760
  display: options.navigation.sidebar.display,
745
761
  extraRoutes: options.extraRoutes,
746
- featured: options.navigation.featured,
747
762
  folderMeta: options.folderMeta,
748
763
  selectors: options.navigation.selectors,
749
764
  sharedFolderMeta: options.sharedFolderMeta,
@@ -986,30 +1001,6 @@ var discoverFolderMeta = async (sources, options = {}) => {
986
1001
  // src/core/sources/resolve.ts
987
1002
  import { join as join7 } from "pathe";
988
1003
 
989
- // src/core/sources/json.ts
990
- var isJsonObject = (value) => typeof value === "object" && value !== null;
991
- var getPath = (doc, path) => {
992
- let current = doc;
993
- for (const key of path.split(".")) {
994
- if (isJsonObject(current)) {
995
- current = current[key];
996
- } else {
997
- return;
998
- }
999
- }
1000
- return current;
1001
- };
1002
- var isStringValue2 = (value) => typeof value === "string";
1003
- var asString = (value) => isStringValue2(value) ? value : undefined;
1004
- var asArray = (value) => Array.isArray(value) ? value : [];
1005
- var asObject = (value) => isJsonObject(value) && !Array.isArray(value) ? value : undefined;
1006
- var isNumberValue = (value) => typeof value === "number";
1007
- var asNumber = (value) => isNumberValue(value) ? value : undefined;
1008
- var objectsIn = (value) => asArray(value).flatMap((item) => {
1009
- const object = asObject(item);
1010
- return object ? [object] : [];
1011
- });
1012
-
1013
1004
  // src/core/sources/contentful-rich-text.ts
1014
1005
  var HEADING = /^heading-(?<level>[1-6])$/u;
1015
1006
  var CELL_PIPE = /\|/gu;
@@ -1020,7 +1011,9 @@ var assetFromEntry = (asset) => {
1020
1011
  return null;
1021
1012
  }
1022
1013
  return {
1014
+ contentType: asString(getPath(asset, "fields.file.contentType")),
1023
1015
  description: asString(getPath(asset, "fields.description")),
1016
+ fileName: asString(getPath(asset, "fields.file.fileName")),
1024
1017
  title: asString(getPath(asset, "fields.title")),
1025
1018
  url: absoluteUrl(url)
1026
1019
  };
@@ -1088,6 +1081,11 @@ var inlineNodeParts = (node, options) => {
1088
1081
  case "asset-hyperlink": {
1089
1082
  return linkParts(inlineParts(children(node), options), linkedAsset(node, options)?.url);
1090
1083
  }
1084
+ case "entry-hyperlink": {
1085
+ const entry = linkedTarget(node, options.resolveEntry);
1086
+ const href = entry ? options.entryHref?.(entry) : undefined;
1087
+ return linkParts(inlineParts(children(node), options), href);
1088
+ }
1091
1089
  case "embedded-entry-inline": {
1092
1090
  return [{ markdown: embeddedEntry(node, options) }];
1093
1091
  }
@@ -1139,7 +1137,14 @@ var renderBlock = (node, options) => {
1139
1137
  }
1140
1138
  case "embedded-asset-block": {
1141
1139
  const asset = linkedAsset(node, options);
1142
- return asset ? image(asset.description ?? asset.title ?? "", asset.url) : "";
1140
+ if (!asset) {
1141
+ return "";
1142
+ }
1143
+ if (asset.contentType && !asset.contentType.startsWith("image/")) {
1144
+ const label = asset.title ?? asset.fileName ?? asset.url;
1145
+ return renderInline(linkParts([{ marks: {}, text: label }], asset.url));
1146
+ }
1147
+ return image(asset.description ?? asset.title ?? "", asset.url);
1143
1148
  }
1144
1149
  case "embedded-entry-block": {
1145
1150
  return embeddedEntry(node, options);
@@ -1161,9 +1166,17 @@ var remoteSource = (options, ctx) => {
1161
1166
  let snapshot = new Map;
1162
1167
  const load = async (refresh = ctx?.refresh ?? true) => {
1163
1168
  options.assertConfigured?.();
1164
- const result = await loadWithCache(options.name, cache, options.fetchEntries, refresh);
1169
+ let warnings = [];
1170
+ const result = await loadWithCache(options.name, cache, async () => {
1171
+ const found = [];
1172
+ const entries = await options.fetchEntries((diagnostic) => {
1173
+ found.push(diagnostic);
1174
+ });
1175
+ warnings = found;
1176
+ return entries;
1177
+ }, refresh);
1165
1178
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
1166
- return result;
1179
+ return { ...result, diagnostics: [...result.diagnostics, ...warnings] };
1167
1180
  };
1168
1181
  const read = async (ref) => {
1169
1182
  const cached = snapshot.get(ref);
@@ -1194,6 +1207,12 @@ var stagedEntry = (slug, data, markdown, lastModified, format = "md") => {
1194
1207
  ref: `${slug}.${format}`
1195
1208
  };
1196
1209
  };
1210
+ var missingSecretError = (name, env) => new BlumeError({
1211
+ code: "BLUME_MISSING_SECRET",
1212
+ message: `Source "${name}" needs ${env}, which is not set.`,
1213
+ severity: "error",
1214
+ suggestion: `Set ${env} in .env.local for local dev, or in your host's environment for production.`
1215
+ });
1197
1216
  var REMOTE_TIMEOUT_MS = 30000;
1198
1217
  var fetchJson = async (url, client) => {
1199
1218
  const doFetch = client.fetchImpl ?? globalThis.fetch;
@@ -1215,9 +1234,9 @@ var queryString = (params) => {
1215
1234
  }
1216
1235
  return search.toString();
1217
1236
  };
1237
+ var documentSlug = (doc, slugField, id) => slugifyPath(asString(getPath(doc, slugField)) ?? id) || slugify(id) || "untitled";
1218
1238
  var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false) => {
1219
- const slugValue = asString(getPath(doc, fields.slug)) ?? id;
1220
- const slug = slugifyPath(slugValue) || slugify(id) || "untitled";
1239
+ const slug = documentSlug(doc, fields.slug, id);
1221
1240
  const data = {};
1222
1241
  const title = asString(getPath(doc, fields.title));
1223
1242
  const description = asString(getPath(doc, fields.description));
@@ -1230,6 +1249,10 @@ var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false)
1230
1249
  if (draft) {
1231
1250
  data.draft = true;
1232
1251
  }
1252
+ const order = fields.order ? asNumber(getPath(doc, fields.order)) : undefined;
1253
+ if (order !== undefined) {
1254
+ data.sidebar = { order };
1255
+ }
1233
1256
  const body = getPath(doc, fields.body);
1234
1257
  let markdown = "";
1235
1258
  let format = "md";
@@ -1244,8 +1267,8 @@ var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false)
1244
1267
  };
1245
1268
 
1246
1269
  // src/core/sources/contentful.ts
1247
- var DELIVERY_HOST = "https://cdn.contentful.com";
1248
- var PREVIEW_HOST = "https://preview.contentful.com";
1270
+ var DELIVERY_HOST = "cdn.contentful.com";
1271
+ var PREVIEW_HOST = "preview.contentful.com";
1249
1272
  var PAGE_SIZE = 100;
1250
1273
  var DEFAULT_FIELDS = {
1251
1274
  body: "body",
@@ -1255,20 +1278,50 @@ var DEFAULT_FIELDS = {
1255
1278
  title: "title"
1256
1279
  };
1257
1280
  var byId = (objects) => new Map(objects.map((object) => [asString(getPath(object, "sys.id")) ?? "", object]));
1281
+ var entryView = (item) => {
1282
+ const sys = asObject(item.sys) ?? {};
1283
+ return {
1284
+ id: asString(sys.id) ?? "",
1285
+ view: { ...asObject(item.fields), sys }
1286
+ };
1287
+ };
1258
1288
  var contentfulSource = (options, ctx) => {
1259
1289
  const fields = { ...DEFAULT_FIELDS, ...options.fields };
1260
- const toEntry = (item, assets, linked) => {
1261
- const sys = asObject(item.sys) ?? {};
1262
- const id = asString(sys.id) ?? "";
1263
- const view = { ...asObject(item.fields), sys };
1264
- return documentEntry(view, fields, id, (body) => isJsonObject(body) ? contentfulRichTextToMarkdown(body, {
1290
+ const routeOf = (view, id) => {
1291
+ const route = resolveEntryRoute({ ref: `${documentSlug(view, fields.slug, id)}.md` }, ".md", undefined, { i18n: options.i18n, prefix: options.prefix, versions: options.versions });
1292
+ return localizedRoute(route.logicalRoute, route.locales[0] ?? "", options.i18n);
1293
+ };
1294
+ const toEntry = (item, { assets, linked }, routes, warn) => {
1295
+ const { id, view } = entryView(item);
1296
+ const missing = new Set;
1297
+ const entry = documentEntry(view, fields, id, (body) => isJsonObject(body) ? contentfulRichTextToMarkdown(body, {
1298
+ entryHref: (target) => routes.get(asString(getPath(target, "sys.id")) ?? ""),
1265
1299
  resolveAsset: (assetId) => {
1266
1300
  const asset = assets.get(assetId);
1267
- return asset ? assetFromEntry(asset) : null;
1301
+ if (!asset) {
1302
+ missing.add(`asset "${assetId}"`);
1303
+ return null;
1304
+ }
1305
+ return assetFromEntry(asset);
1306
+ },
1307
+ resolveEntry: (entryId) => {
1308
+ const target = linked.get(entryId);
1309
+ if (!target) {
1310
+ missing.add(`entry "${entryId}"`);
1311
+ }
1312
+ return target ?? null;
1268
1313
  },
1269
- resolveEntry: (entryId) => linked.get(entryId) ?? null,
1270
1314
  serializers: options.serializers
1271
1315
  }) : "", false, writesMdx(options.serializers));
1316
+ for (const link of missing) {
1317
+ warn({
1318
+ code: "BLUME_SOURCE_UNRESOLVED_LINK",
1319
+ message: `Source "${options.name}": "${entry.ref}" links to Contentful ${link}, which the response didn't include, so the page renders without it.`,
1320
+ severity: "warning",
1321
+ suggestion: "Publish the linked asset or entry, or remove the link: the Delivery API returns only published content."
1322
+ });
1323
+ }
1324
+ return entry;
1272
1325
  };
1273
1326
  const preview = ctx?.preview ?? false;
1274
1327
  const previewToken = () => options.previewToken ?? process.env.CONTENTFUL_PREVIEW_TOKEN;
@@ -1282,15 +1335,18 @@ var contentfulSource = (options, ctx) => {
1282
1335
  });
1283
1336
  }
1284
1337
  };
1285
- const fetchEntries = async () => {
1338
+ const fetchEntries = async (warn) => {
1286
1339
  const token = preview ? previewToken() : options.token ?? process.env.CONTENTFUL_ACCESS_TOKEN;
1340
+ if (!token) {
1341
+ throw missingSecretError(options.name, "CONTENTFUL_ACCESS_TOKEN");
1342
+ }
1287
1343
  const client = {
1288
1344
  fetchImpl: options.fetchImpl,
1289
- headers: token ? { authorization: `Bearer ${token}` } : {}
1345
+ headers: { authorization: `Bearer ${token}` }
1290
1346
  };
1291
- const host = preview ? PREVIEW_HOST : DELIVERY_HOST;
1292
- const base = `${host}/spaces/${options.space}/environments/${options.environment ?? "master"}/entries`;
1293
- const entries = [];
1347
+ const host = preview ? options.previewHost ?? PREVIEW_HOST : options.host ?? DELIVERY_HOST;
1348
+ const base = `https://${host}/spaces/${options.space}/environments/${options.environment ?? "master"}/entries`;
1349
+ const pages = [];
1294
1350
  let skip = 0;
1295
1351
  let more = true;
1296
1352
  while (more) {
@@ -1306,16 +1362,20 @@ var contentfulSource = (options, ctx) => {
1306
1362
  if (!page) {
1307
1363
  throw new Error("Contentful returned a non-object response");
1308
1364
  }
1309
- const assets = byId(objectsIn(getPath(page, "includes.Asset")));
1310
- const linked = byId(objectsIn(getPath(page, "includes.Entry")));
1311
1365
  const items = objectsIn(page.items);
1312
- for (const item of items) {
1313
- entries.push(toEntry(item, assets, linked));
1314
- }
1366
+ pages.push({
1367
+ assets: byId(objectsIn(getPath(page, "includes.Asset"))),
1368
+ items,
1369
+ linked: byId([...objectsIn(getPath(page, "includes.Entry")), ...items])
1370
+ });
1315
1371
  skip += items.length;
1316
1372
  more = items.length > 0 && skip < (asNumber(page.total) ?? 0);
1317
1373
  }
1318
- return entries;
1374
+ const routes = new Map(pages.flatMap(({ items }) => items.map((item) => {
1375
+ const { id, view } = entryView(item);
1376
+ return [id, routeOf(view, id)];
1377
+ })));
1378
+ return pages.flatMap((page) => page.items.map((item) => toEntry(item, page, routes, warn)));
1319
1379
  };
1320
1380
  return remoteSource({
1321
1381
  assertConfigured,
@@ -1330,6 +1390,7 @@ var contentfulSource = (options, ctx) => {
1330
1390
  // src/core/sources/filesystem.ts
1331
1391
  import { existsSync, watch as fsWatch } from "node:fs";
1332
1392
  import { readFile } from "node:fs/promises";
1393
+ import { YAMLException } from "js-yaml";
1333
1394
  import { extname, isAbsolute, join as join2, relative as relative3, resolve } from "pathe";
1334
1395
  import { glob as glob2 } from "tinyglobby";
1335
1396
  var filesystemSource = (options) => {
@@ -1352,11 +1413,19 @@ var filesystemSource = (options) => {
1352
1413
  loadable.push(file);
1353
1414
  }
1354
1415
  }
1355
- const entries = await Promise.all(loadable.map(async (file) => {
1416
+ const read = await Promise.all(loadable.map(async (file) => {
1356
1417
  const source = await readFile(file, "utf-8");
1357
1418
  const ext = extname(file).toLowerCase();
1358
1419
  const format = ext === ".mdx" ? "mdx" : "md";
1359
- const parsed = frontmatter_default(source);
1420
+ let parsed;
1421
+ try {
1422
+ parsed = frontmatter_default(source);
1423
+ } catch (error) {
1424
+ if (error instanceof YAMLException) {
1425
+ return frontmatterYamlDiagnostic(error, file);
1426
+ }
1427
+ throw error;
1428
+ }
1360
1429
  return {
1361
1430
  body: { format, text: parsed.content },
1362
1431
  data: parsed.data,
@@ -1365,6 +1434,14 @@ var filesystemSource = (options) => {
1365
1434
  sourcePath: file
1366
1435
  };
1367
1436
  }));
1437
+ const entries = [];
1438
+ for (const item of read) {
1439
+ if ("body" in item) {
1440
+ entries.push(item);
1441
+ } else {
1442
+ diagnostics.push(item);
1443
+ }
1444
+ }
1368
1445
  return { diagnostics, entries };
1369
1446
  };
1370
1447
  const validate = () => {
@@ -1530,11 +1607,17 @@ var githubHeaders = () => {
1530
1607
  }
1531
1608
  return headers;
1532
1609
  };
1610
+ var releaseCategory = (release) => {
1611
+ if (release.draft) {
1612
+ return "Draft";
1613
+ }
1614
+ return release.prerelease ? "Prerelease" : "Release";
1615
+ };
1533
1616
  var releaseToEntry = (release, site) => {
1534
1617
  const version = release.tag_name.replace(LEADING_V, "");
1535
1618
  const title = release.name?.trim() || release.tag_name;
1536
1619
  const date = release.published_at ?? release.created_at;
1537
- const category = release.prerelease ? "Prerelease" : "Release";
1620
+ const category = releaseCategory(release);
1538
1621
  const body = liftHeadings(relativizeOwnLinks(neutralizeUnsafeLinks((release.body ?? "").replaceAll(`\r
1539
1622
  `, `
1540
1623
  `)).trim(), site));
@@ -1645,6 +1728,11 @@ var githubReleasesSource = (options, ctx) => {
1645
1728
 
1646
1729
  // src/core/sources/mdx-remote.ts
1647
1730
  import picomatch from "picomatch";
1731
+ var includeMatcher = (include) => {
1732
+ const included = picomatch(include.filter((pattern) => !pattern.startsWith("!")));
1733
+ const excluded = picomatch(include.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1)));
1734
+ return (ref) => included(ref) && !excluded(ref);
1735
+ };
1648
1736
  var GITHUB_HOSTS = new Set(["api.github.com", "raw.githubusercontent.com"]);
1649
1737
  var githubHeaders2 = (url) => {
1650
1738
  const token = process.env.GITHUB_TOKEN;
@@ -1683,7 +1771,7 @@ var enumerateGithub = async (github, include, doFetch) => {
1683
1771
  const text = await fetchText(treeUrl, doFetch);
1684
1772
  const body = JSON.parse(text);
1685
1773
  const prefix = base ? `${base}/` : "";
1686
- const included = picomatch(include);
1774
+ const included = includeMatcher(include);
1687
1775
  const refs = (body.tree ?? []).flatMap((node) => {
1688
1776
  if (!(node.type === "blob" && node.path.startsWith(prefix))) {
1689
1777
  return [];
@@ -1700,7 +1788,13 @@ var enumerateGithub = async (github, include, doFetch) => {
1700
1788
  }
1701
1789
  ];
1702
1790
  });
1703
- return { refs, truncated: body.truncated === true };
1791
+ const truncated = body.truncated === true;
1792
+ const found = prefix === "" || truncated || (body.tree ?? []).some((node) => node.path.startsWith(prefix));
1793
+ return {
1794
+ missing: found ? undefined : `"${base}" in ${owner}/${repo} at ${ref}`,
1795
+ refs,
1796
+ truncated
1797
+ };
1704
1798
  };
1705
1799
  var mdxRemoteSource = (options, ctx) => {
1706
1800
  const doFetch = options.fetchImpl ?? globalThis.fetch;
@@ -1721,7 +1815,7 @@ var mdxRemoteSource = (options, ctx) => {
1721
1815
  return await enumerateGithub(options.github, options.include, doFetch);
1722
1816
  }
1723
1817
  const base = (options.url ?? "").replace(/\/$/u, "");
1724
- const included = picomatch(options.include);
1818
+ const included = includeMatcher(options.include);
1725
1819
  const refs = (options.files ?? []).flatMap((ref) => included(ref) ? [{ editUrl: `${base}/${ref}`, fetchUrl: `${base}/${ref}`, ref }] : []);
1726
1820
  return { refs, truncated: false };
1727
1821
  };
@@ -1742,12 +1836,21 @@ var mdxRemoteSource = (options, ctx) => {
1742
1836
  assertConfigured();
1743
1837
  const skipped = [];
1744
1838
  const result = await loadWithCache(options.name, cache, async () => {
1745
- const { refs, truncated } = await enumerate();
1839
+ const { missing, refs, truncated } = await enumerate();
1840
+ if (missing) {
1841
+ skipped.push({
1842
+ code: "BLUME_SOURCE_PATH_MISSING",
1843
+ message: `Source "${options.name}" read no files: there is no folder ${missing}.`,
1844
+ severity: "warning",
1845
+ suggestion: "Check github.path and github.ref against the repository."
1846
+ });
1847
+ }
1746
1848
  if (truncated) {
1747
1849
  skipped.push({
1748
1850
  code: "BLUME_SOURCE_TRUNCATED",
1749
- message: `Source "${options.name}" hit GitHub's tree listing limit; some files were not enumerated. Narrow the source path or split the repo.`,
1750
- severity: "warning"
1851
+ message: `Source "${options.name}" hit GitHub's tree listing limit for the whole repository; some files were not enumerated.`,
1852
+ severity: "warning",
1853
+ suggestion: "List the files to read with { url, files } instead, or split the repository."
1751
1854
  });
1752
1855
  }
1753
1856
  const reasons = [];
@@ -2119,7 +2222,11 @@ var notionSource = (options, ctx) => {
2119
2222
  severity: "error"
2120
2223
  });
2121
2224
  }
2122
- return new Client({ auth: options.token ?? process.env.NOTION_TOKEN });
2225
+ const auth = options.token ?? process.env.NOTION_TOKEN;
2226
+ if (!auth) {
2227
+ throw missingSecretError(options.name, "NOTION_TOKEN");
2228
+ }
2229
+ return new Client({ auth });
2123
2230
  };
2124
2231
  const childrenOf = (client, blockId) => collectAll((cursor) => notionCall(() => client.blocks.children.list({ block_id: blockId, start_cursor: cursor })));
2125
2232
  const renderContainer = async (client, block, render) => {
@@ -2315,6 +2422,7 @@ ${nested}`;
2315
2422
  // src/core/sources/obsidian.ts
2316
2423
  import { existsSync as existsSync2, watch as fsWatch2, statSync } from "node:fs";
2317
2424
  import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
2425
+ import { YAMLException as YAMLException2 } from "js-yaml";
2318
2426
  import { basename as basename2, isAbsolute as isAbsolute2, join as join5, relative as relative4, resolve as resolve2 } from "pathe";
2319
2427
  var MARKDOWN_FILE = /\.md$/iu;
2320
2428
  var OBSIDIAN_COMMENT = /%%.*?%%/gu;
@@ -2647,6 +2755,9 @@ var readNote = async (vaultDir, rel, options) => {
2647
2755
  rel
2648
2756
  };
2649
2757
  } catch (error) {
2758
+ if (error instanceof YAMLException2) {
2759
+ return frontmatterYamlDiagnostic(error, absPath);
2760
+ }
2650
2761
  if (error.code === "ENOENT") {
2651
2762
  return null;
2652
2763
  }
@@ -2737,7 +2848,8 @@ var obsidianSource = (options, ctx) => {
2737
2848
  }
2738
2849
  }
2739
2850
  const read = await Promise.all(files.map((rel) => readNote(vaultDir, rel, options)));
2740
- const notes = read.filter((note) => note !== null);
2851
+ const notes = read.filter((note) => note !== null && ("rel" in note));
2852
+ skipped.push(...read.filter((note) => note !== null && ("code" in note)));
2741
2853
  const { index, pairs } = buildLinkIndex(notes, options);
2742
2854
  const unresolved = noTargets();
2743
2855
  const entries = pairs.map((pair) => noteToEntry(pair, index, options, unresolved));
@@ -2813,6 +2925,10 @@ var marksOf2 = (node) => {
2813
2925
  };
2814
2926
  };
2815
2927
  var children2 = (node) => objectsIn(node.children);
2928
+ var leftOut = (what, options) => {
2929
+ options.onUnsupported?.(what);
2930
+ return unsupported(what, writesMdx(options.serializers));
2931
+ };
2816
2932
  var typeOf = (node) => asString(node.type) ?? "";
2817
2933
  var customBlock = (node, options) => {
2818
2934
  const fields = asObject(node.fields);
@@ -2821,7 +2937,7 @@ var customBlock = (node, options) => {
2821
2937
  if (fields && serializer) {
2822
2938
  return serializer(fields);
2823
2939
  }
2824
- return unsupported(`Lexical block${blockType ? `: ${blockType}` : ""}`, writesMdx(options.serializers));
2940
+ return leftOut(`Lexical block${blockType ? `: ${blockType}` : ""}`, options);
2825
2941
  };
2826
2942
  var inlineParts2 = (nodes, options) => nodes.flatMap((node) => inlineNodeParts2(node, options));
2827
2943
  var inlineNodeParts2 = (node, options) => {
@@ -2854,7 +2970,7 @@ var renderUpload = (node, options) => {
2854
2970
  const value = asObject(node.value);
2855
2971
  const url = value ? asString(value.url) : undefined;
2856
2972
  if (!(value && url)) {
2857
- return unsupported("Lexical upload (fetch with depth 1 to populate it)", writesMdx(options.serializers));
2973
+ return leftOut("Lexical upload (fetch with depth 1 to populate it)", options);
2858
2974
  }
2859
2975
  const alt = asString(value.alt) ?? asString(value.filename) ?? "";
2860
2976
  return image(alt, absoluteUrl(url, options.baseUrl));
@@ -2913,7 +3029,7 @@ var renderBlock2 = (node, options) => {
2913
3029
  return customBlock(node, options);
2914
3030
  }
2915
3031
  default: {
2916
- return unsupported(`Lexical node: ${typeOf(node)}`, writesMdx(options.serializers));
3032
+ return leftOut(`Lexical node: ${typeOf(node)}`, options);
2917
3033
  }
2918
3034
  }
2919
3035
  };
@@ -2934,11 +3050,26 @@ var DEFAULT_FIELDS2 = {
2934
3050
  var payloadSource = (options, ctx) => {
2935
3051
  const fields = { ...DEFAULT_FIELDS2, ...options.fields };
2936
3052
  const origin = options.url.replace(/\/+$/u, "");
2937
- const lower = (body) => lexicalToMarkdown(body, {
3053
+ const lower = (body, leftOut) => lexicalToMarkdown(body, {
2938
3054
  baseUrl: origin,
3055
+ onUnsupported: (what) => leftOut.add(what),
2939
3056
  serializers: options.serializers
2940
3057
  });
2941
- const fetchEntries = async () => {
3058
+ const toEntry = (doc, draft, warn) => {
3059
+ const leftOut = new Set;
3060
+ const id = asString(doc.id) ?? String(doc.id ?? "");
3061
+ const entry = documentEntry(doc, fields, id, (body) => isJsonObject(body) ? lower(body, leftOut) : "", draft, writesMdx(options.serializers));
3062
+ for (const what of leftOut) {
3063
+ warn({
3064
+ code: "BLUME_SOURCE_UNSUPPORTED_NODE",
3065
+ message: `Source "${options.name}": "${entry.ref}" has content Blume has no Markdown for (${what}), left out as a comment.`,
3066
+ severity: "warning",
3067
+ suggestion: "Map a block type to a component with a serializer on payloadSource, passed to custom(); other nodes have no Markdown equivalent."
3068
+ });
3069
+ }
3070
+ return entry;
3071
+ };
3072
+ const fetchEntries = async (warn) => {
2942
3073
  const preview = ctx?.preview ?? false;
2943
3074
  const token = options.token ?? process.env.PAYLOAD_API_KEY;
2944
3075
  const client = {
@@ -2968,8 +3099,7 @@ var payloadSource = (options, ctx) => {
2968
3099
  if (draft && !preview) {
2969
3100
  continue;
2970
3101
  }
2971
- const id = asString(doc.id) ?? String(doc.id ?? "");
2972
- entries.push(documentEntry(doc, fields, id, (body) => isJsonObject(body) ? lower(body) : "", draft, writesMdx(options.serializers)));
3102
+ entries.push(toEntry(doc, draft, warn));
2973
3103
  }
2974
3104
  more = result.hasNextPage === true;
2975
3105
  page += 1;
@@ -3130,15 +3260,22 @@ var sanitySource = (options, ctx) => {
3130
3260
  if (description) {
3131
3261
  data.description = description;
3132
3262
  }
3133
- const blocks = getPath(doc, fields.body ?? "body") ?? [];
3134
- const markdown = Array.isArray(blocks) ? portableTextToMarkdown(blocks, {
3135
- imageUrl: (block) => {
3136
- const ref = block.asset?._ref;
3137
- return ref ? imageUrlFromRef(ref, options.projectId, options.dataset) : null;
3138
- },
3139
- serializers: options.serializers
3140
- }) : "";
3141
- const format = writesMdx(options.serializers) ? "mdx" : "md";
3263
+ const body = getPath(doc, fields.body ?? "body");
3264
+ let markdown = "";
3265
+ let format = writesMdx(options.serializers) ? "mdx" : "md";
3266
+ if (isStringValue2(body)) {
3267
+ markdown = neutralizeUnsafeLinks(`${body.trimEnd()}
3268
+ `);
3269
+ format = "md";
3270
+ } else if (Array.isArray(body)) {
3271
+ markdown = portableTextToMarkdown(body, {
3272
+ imageUrl: (block) => {
3273
+ const ref = block.asset?._ref;
3274
+ return ref ? imageUrlFromRef(ref, options.projectId, options.dataset) : null;
3275
+ },
3276
+ serializers: options.serializers
3277
+ });
3278
+ }
3142
3279
  const raw = frontmatter_default.stringify(markdown, data);
3143
3280
  return {
3144
3281
  body: { format, text: markdown },
@@ -3150,13 +3287,22 @@ var sanitySource = (options, ctx) => {
3150
3287
  };
3151
3288
  };
3152
3289
  const load = async (refresh = ctx?.refresh ?? true) => {
3290
+ const empty = [];
3153
3291
  const result = await loadWithCache(options.name, cache, async () => {
3154
3292
  const client = resolveClient(options, ctx?.preview ?? false);
3155
3293
  const docs = await client.fetch(options.query);
3294
+ if (docs.length === 0 && !(options.token ?? process.env.SANITY_TOKEN)) {
3295
+ empty.push({
3296
+ code: "BLUME_MISSING_SECRET",
3297
+ message: `Source "${options.name}" found no documents, and SANITY_TOKEN is not set. A private dataset returns nothing to a query without a token.`,
3298
+ severity: "warning",
3299
+ suggestion: "Set SANITY_TOKEN to a token with read access if the dataset is private."
3300
+ });
3301
+ }
3156
3302
  return docs.map(toEntry);
3157
3303
  }, refresh);
3158
3304
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
3159
- return result;
3305
+ return { ...result, diagnostics: [...result.diagnostics, ...empty] };
3160
3306
  };
3161
3307
  const read = async (ref) => {
3162
3308
  const cached = snapshot.get(ref);
@@ -3393,7 +3539,12 @@ var buildSource = (adapter, name, config, context, runtime) => {
3393
3539
  return notionSource({ ...adapter.options, name }, ctx());
3394
3540
  }
3395
3541
  case "contentful": {
3396
- return contentfulSource({ ...adapter.options, name }, ctx());
3542
+ return contentfulSource({
3543
+ ...adapter.options,
3544
+ i18n: config.i18n,
3545
+ name,
3546
+ versions: config.versions
3547
+ }, ctx());
3397
3548
  }
3398
3549
  case "payload": {
3399
3550
  return payloadSource({ ...adapter.options, name }, ctx());
@@ -3523,7 +3674,68 @@ var gitContentRoots = (sources, gitRoot) => {
3523
3674
  var isLogoShorthand = (logo) => typeof logo === "string";
3524
3675
  var logoHref = (logo) => logo === undefined || isLogoShorthand(logo) ? "/" : logo.href ?? "/";
3525
3676
  var isBannerShorthand = (banner) => typeof banner === "string";
3677
+ var substituteEntryVariables = (entry, variables) => {
3678
+ const { expanded } = entry;
3679
+ const text = expanded?.text ?? entry.body.text;
3680
+ const offset = expanded ? 0 : strippedLineOffset(entry.raw, entry.body.text);
3681
+ const diagnostics = undefinedVariables(text, variables).map(({ line, name }) => {
3682
+ const origin = expanded?.origins[line - 1];
3683
+ return {
3684
+ code: "BLUME_UNDEFINED_VARIABLE",
3685
+ file: origin?.file ?? entry.sourcePath,
3686
+ line: origin?.line ?? line + offset,
3687
+ message: `{{${name}}} isn't a defined variable, so it would show as written.`,
3688
+ severity: "error",
3689
+ suggestion: `Define "${name}" under variables in blume.config.ts, or put it in inline code to show it as written.`
3690
+ };
3691
+ });
3692
+ if (expanded) {
3693
+ expanded.text = substituteVariables(expanded.text, variables);
3694
+ } else {
3695
+ entry.body.text = substituteVariables(entry.body.text, variables);
3696
+ }
3697
+ return diagnostics;
3698
+ };
3699
+ var substituteLoadedVariables = (loaded, variables) => hasVariables(variables) ? loaded.flatMap(({ entries }) => entries.flatMap((entry) => substituteEntryVariables(entry, variables))) : [];
3700
+ var redirectPageDiagnostics = (config, pages, configFile) => config.redirects.flatMap((redirect) => {
3701
+ const from = withBasePath(config.basePath, redirect.from);
3702
+ if (!isPatternPath(redirect.from)) {
3703
+ const page = normalizePath(from);
3704
+ return pages.includes(page) ? [
3705
+ {
3706
+ code: "BLUME_REDIRECT_MATCHES_PAGE",
3707
+ file: configFile ?? undefined,
3708
+ message: `The redirect from ${redirect.from} is also the page ${page}, which never publishes: its URL redirects to ${redirect.to} instead.`,
3709
+ severity: "warning",
3710
+ suggestion: "If the page moved, delete it or give it a new slug; otherwise remove the redirect."
3711
+ }
3712
+ ] : [];
3713
+ }
3714
+ const matched = pathsUnderPattern(from, pages);
3715
+ const [first] = matched;
3716
+ if (first === undefined) {
3717
+ return [];
3718
+ }
3719
+ const more = matched.length > 1 ? ` and ${matched.length - 1} more` : "";
3720
+ return [
3721
+ {
3722
+ code: "BLUME_REDIRECT_MATCHES_PAGE",
3723
+ file: configFile ?? undefined,
3724
+ message: `The redirect from ${redirect.from} also matches the page ${first}${more}, which some hosts would redirect and others would serve.`,
3725
+ severity: "error",
3726
+ suggestion: "Narrow the pattern to the paths that moved, or move the pages out from under it."
3727
+ }
3728
+ ];
3729
+ });
3526
3730
  var bannerLinkHref = (banner) => banner === undefined || isBannerShorthand(banner) ? undefined : banner.link?.href;
3731
+ var validatedSources = (config, context, runtime, beforeSources) => {
3732
+ beforeSources?.(config);
3733
+ const sources = resolveSources(config, context, runtime);
3734
+ for (const source of sources) {
3735
+ source.validate?.();
3736
+ }
3737
+ return sources;
3738
+ };
3527
3739
  var scanProject = async (root, options = {}) => {
3528
3740
  const mode = options.mode ?? "dev";
3529
3741
  const preview = options.preview ?? false;
@@ -3534,14 +3746,7 @@ var scanProject = async (root, options = {}) => {
3534
3746
  const context = resolveProjectContext(root, config, {
3535
3747
  runtimeDir: options.runtimeDir
3536
3748
  });
3537
- const sources = resolveSources(config, context, {
3538
- mode,
3539
- preview,
3540
- refresh: options.refresh
3541
- });
3542
- for (const source of sources) {
3543
- source.validate?.();
3544
- }
3749
+ const sources = validatedSources(config, context, { mode, preview, refresh: options.refresh }, options.beforeSources);
3545
3750
  const metaSources = sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [
3546
3751
  {
3547
3752
  exclude: source.exclude,
@@ -3576,6 +3781,7 @@ var scanProject = async (root, options = {}) => {
3576
3781
  includeDiagnostics.push(...expansion.errors);
3577
3782
  });
3578
3783
  }));
3784
+ const variableDiagnostics = substituteLoadedVariables(loaded, config.variables);
3579
3785
  const sharedFolderMeta = new Map([
3580
3786
  ...loaded.flatMap(({ folderMeta: sourceMeta }) => Object.entries(sourceMeta ?? {})),
3581
3787
  ...folderMeta.shared
@@ -3602,14 +3808,15 @@ var scanProject = async (root, options = {}) => {
3602
3808
  lastModifiedWarnings.push(...lastModifiedShallowWarning(context.root, undated));
3603
3809
  }
3604
3810
  const customPages = context.pagesRoot ? await discoverPages(context.pagesRoot) : [];
3811
+ const extraRoutes = new Set([
3812
+ ...customStaticRoutes(customPages),
3813
+ ...hasChangelogIndex(pages, config) ? [CHANGELOG_INDEX_ROUTE] : []
3814
+ ]);
3605
3815
  const graph = buildContentGraph(pages, {
3606
3816
  bannerHref: bannerLinkHref(config.banner),
3607
3817
  basePath: config.basePath,
3608
3818
  brandHref: logoHref(config.logo),
3609
- extraRoutes: new Set([
3610
- ...customStaticRoutes(customPages),
3611
- ...hasChangelogIndex(pages, config) ? [CHANGELOG_INDEX_ROUTE] : []
3612
- ]),
3819
+ extraRoutes,
3613
3820
  folderMeta: folderMeta.meta,
3614
3821
  i18n: config.i18n,
3615
3822
  navigation: config.navigation,
@@ -3625,12 +3832,14 @@ var scanProject = async (root, options = {}) => {
3625
3832
  diagnostics: [
3626
3833
  ...contentDiagnostics,
3627
3834
  ...includeDiagnostics,
3835
+ ...variableDiagnostics,
3628
3836
  ...folderMeta.diagnostics,
3629
3837
  ...entryIdDiagnostics(pages, resolveDocsCollection(config, context.root).base),
3630
3838
  ...graph.diagnostics,
3631
3839
  ...i18nWarnings,
3632
3840
  ...versionWarnings,
3633
- ...lastModifiedWarnings
3841
+ ...lastModifiedWarnings,
3842
+ ...redirectPageDiagnostics(config, [...manifest.routes.map((route) => route.path), ...extraRoutes], context.configFile)
3634
3843
  ],
3635
3844
  droppedPages,
3636
3845
  graph,
@@ -3643,5 +3852,5 @@ var scanProject = async (root, options = {}) => {
3643
3852
 
3644
3853
  export { directiveSource, validateSearchPopularIcons, validateNavTargets, contentIndexable, buildManifest, findFolderMetaFiles, isWithin, scanProject };
3645
3854
 
3646
- //# debugId=B7A86608D56AB00864756E2164756E21
3647
- //# sourceMappingURL=chunk-j85vccga.js.map
3855
+ //# debugId=1F41F69354D6E33464756E2164756E21
3856
+ //# sourceMappingURL=chunk-ncj7w596.js.map