blume 1.7.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (677) hide show
  1. package/AGENTS.md +19 -0
  2. package/CHANGELOG.md +227 -0
  3. package/README.md +34 -21
  4. package/dist/cli/chunk-11j0384y.js +148 -0
  5. package/dist/cli/chunk-11j0384y.js.map +10 -0
  6. package/dist/cli/{chunk-xhtpx3ff.js → chunk-1w8dp3qb.js} +17 -16
  7. package/dist/cli/{chunk-xhtpx3ff.js.map → chunk-1w8dp3qb.js.map} +3 -3
  8. package/dist/cli/chunk-2q1dwty4.js +75 -0
  9. package/dist/cli/chunk-2q1dwty4.js.map +11 -0
  10. package/dist/cli/chunk-41za066z.js +122 -0
  11. package/dist/cli/chunk-41za066z.js.map +11 -0
  12. package/dist/cli/chunk-5a2z0198.js +133 -0
  13. package/dist/cli/chunk-5a2z0198.js.map +10 -0
  14. package/dist/cli/{chunk-9he6crym.js → chunk-6k8vp3ta.js} +25 -13
  15. package/dist/cli/{chunk-9he6crym.js.map → chunk-6k8vp3ta.js.map} +4 -4
  16. package/dist/cli/{chunk-mt76t7dj.js → chunk-79njf86q.js} +133 -54
  17. package/dist/cli/chunk-79njf86q.js.map +11 -0
  18. package/dist/cli/{chunk-3w7b2vcx.js → chunk-7ez8ny0t.js} +2 -2
  19. package/dist/cli/{chunk-688e0dde.js → chunk-88cpgt6h.js} +1 -1
  20. package/dist/cli/chunk-a9kptbw5.js +361 -0
  21. package/dist/cli/chunk-a9kptbw5.js.map +14 -0
  22. package/dist/cli/chunk-abh8yjkn.js +31 -0
  23. package/dist/cli/chunk-abh8yjkn.js.map +10 -0
  24. package/dist/cli/chunk-b5aj94ah.js +91 -0
  25. package/dist/cli/chunk-b5aj94ah.js.map +10 -0
  26. package/dist/cli/{chunk-hs3gbh8p.js → chunk-bctazmbk.js} +9 -5
  27. package/dist/cli/chunk-bctazmbk.js.map +10 -0
  28. package/dist/cli/chunk-beat36xx.js +279 -0
  29. package/dist/cli/chunk-beat36xx.js.map +10 -0
  30. package/dist/cli/chunk-bnbmcwfb.js +145 -0
  31. package/dist/cli/chunk-bnbmcwfb.js.map +11 -0
  32. package/dist/cli/{chunk-9bkjd11x.js → chunk-bw22s759.js} +15 -5
  33. package/dist/cli/{chunk-9bkjd11x.js.map → chunk-bw22s759.js.map} +4 -4
  34. package/dist/cli/chunk-by2290sx.js +39 -0
  35. package/dist/cli/chunk-by2290sx.js.map +10 -0
  36. package/dist/cli/chunk-d1tadaw7.js +79 -0
  37. package/dist/cli/chunk-d1tadaw7.js.map +10 -0
  38. package/dist/cli/{chunk-t3tj0dgr.js → chunk-d80hr03s.js} +24 -19
  39. package/dist/cli/chunk-d80hr03s.js.map +15 -0
  40. package/dist/cli/chunk-ernrthtr.js +97 -0
  41. package/dist/cli/chunk-ernrthtr.js.map +10 -0
  42. package/dist/cli/chunk-f2972sbt.js +374 -0
  43. package/dist/cli/chunk-f2972sbt.js.map +10 -0
  44. package/dist/cli/{chunk-exeeb35e.js → chunk-f7t03s3g.js} +2 -2
  45. package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
  46. package/dist/cli/chunk-fa25z98p.js.map +11 -0
  47. package/dist/cli/{chunk-12dxjqk7.js → chunk-fh5hj5jt.js} +44 -21
  48. package/dist/cli/chunk-fh5hj5jt.js.map +10 -0
  49. package/dist/cli/{chunk-8cd8tj54.js → chunk-j8mw0za6.js} +86 -57
  50. package/dist/cli/chunk-j8mw0za6.js.map +35 -0
  51. package/dist/cli/chunk-jts8mvcz.js +106 -0
  52. package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
  53. package/dist/cli/{chunk-ejjx8znq.js → chunk-mnqj32sj.js} +505 -536
  54. package/dist/cli/chunk-mnqj32sj.js.map +12 -0
  55. package/dist/cli/{chunk-196vjxp9.js → chunk-mwt1k8n7.js} +100 -372
  56. package/dist/cli/chunk-mwt1k8n7.js.map +10 -0
  57. package/dist/cli/chunk-nk3ts2xk.js +51 -0
  58. package/dist/cli/chunk-nk3ts2xk.js.map +10 -0
  59. package/dist/cli/{chunk-n9sra6sy.js → chunk-pat2zzwc.js} +10 -14
  60. package/dist/cli/{chunk-n9sra6sy.js.map → chunk-pat2zzwc.js.map} +2 -2
  61. package/dist/cli/chunk-pnnvybbk.js +176 -0
  62. package/dist/cli/chunk-pnnvybbk.js.map +11 -0
  63. package/dist/cli/{chunk-cvky9gb2.js → chunk-sqn5t4q0.js} +81 -87
  64. package/dist/cli/chunk-sqn5t4q0.js.map +10 -0
  65. package/dist/cli/{chunk-eevwt1sc.js → chunk-tzne8qfq.js} +15 -15
  66. package/dist/cli/{chunk-eevwt1sc.js.map → chunk-tzne8qfq.js.map} +1 -1
  67. package/dist/cli/chunk-xaz13gwg.js +12449 -0
  68. package/dist/cli/chunk-xaz13gwg.js.map +182 -0
  69. package/dist/cli/chunk-y3e45rc8.js +102 -0
  70. package/dist/cli/chunk-y3e45rc8.js.map +10 -0
  71. package/dist/cli/chunk-z01ze5c1.js +261 -0
  72. package/dist/cli/chunk-z01ze5c1.js.map +10 -0
  73. package/dist/cli/{chunk-5n7t497w.js → chunk-z1f5arsg.js} +247 -755
  74. package/dist/cli/chunk-z1f5arsg.js.map +36 -0
  75. package/dist/cli/{chunk-hdm2dkd2.js → chunk-zg2gtj10.js} +1086 -2596
  76. package/dist/cli/chunk-zg2gtj10.js.map +35 -0
  77. package/dist/cli/{chunk-ppfvdcd4.js → chunk-zxccj738.js} +1 -1
  78. package/dist/cli/index.js +214 -57
  79. package/dist/cli/index.js.map +8 -7
  80. package/dist/types/ai/agent-readability.d.ts +52 -0
  81. package/dist/types/ai/ai-catalog.d.ts +42 -0
  82. package/dist/types/ai/api/paths.d.ts +17 -0
  83. package/dist/types/ai/api-catalog.d.ts +18 -0
  84. package/dist/types/ai/ask.d.ts +368 -0
  85. package/dist/types/ai/changelog-markdown.d.ts +2 -0
  86. package/dist/types/ai/component-markdown.d.ts +2 -2
  87. package/dist/types/ai/index.d.ts +24 -0
  88. package/dist/types/ai/link-headers.d.ts +24 -0
  89. package/dist/types/ai/llms.d.ts +25 -0
  90. package/dist/types/ai/markdown.d.ts +45 -0
  91. package/dist/types/ai/mcp/discovery.d.ts +68 -0
  92. package/dist/types/ai/mcp/tools.d.ts +16 -0
  93. package/dist/types/ai/openapi-components.d.ts +43 -0
  94. package/dist/types/ai/relative-links.d.ts +26 -0
  95. package/dist/types/ai/serializers.d.ts +15 -0
  96. package/dist/types/ai/skills.d.ts +42 -0
  97. package/dist/types/ai/tar.d.ts +25 -0
  98. package/dist/types/ai/visibility.d.ts +17 -0
  99. package/dist/types/ai/web-bot-auth.d.ts +16 -0
  100. package/dist/types/analytics/adobe.d.ts +35 -0
  101. package/dist/types/analytics/amplitude.d.ts +50 -0
  102. package/dist/types/analytics/clarity.d.ts +31 -0
  103. package/dist/types/analytics/clearbit.d.ts +29 -0
  104. package/dist/types/analytics/cloudflare.d.ts +41 -0
  105. package/dist/types/analytics/fathom.d.ts +41 -0
  106. package/dist/types/analytics/google-analytics.d.ts +44 -0
  107. package/dist/types/analytics/google-tag-manager.d.ts +40 -0
  108. package/dist/types/analytics/head.d.ts +30 -0
  109. package/dist/types/analytics/heap.d.ts +40 -0
  110. package/dist/types/analytics/hightouch.d.ts +44 -0
  111. package/dist/types/analytics/hotjar.d.ts +33 -0
  112. package/dist/types/analytics/index.d.ts +61 -0
  113. package/dist/types/analytics/inline.d.ts +17 -0
  114. package/dist/types/analytics/logrocket.d.ts +44 -0
  115. package/dist/types/analytics/mixpanel.d.ts +67 -0
  116. package/dist/types/analytics/pirsch.d.ts +42 -0
  117. package/dist/types/analytics/plausible.d.ts +56 -0
  118. package/dist/types/analytics/posthog.d.ts +45 -0
  119. package/dist/types/analytics/schema.d.ts +320 -0
  120. package/dist/types/analytics/script.d.ts +46 -0
  121. package/dist/types/analytics/segment.d.ts +50 -0
  122. package/dist/types/analytics/vercel.d.ts +48 -0
  123. package/dist/types/astro/integration.d.ts +76 -0
  124. package/dist/types/astro/markdown-negotiation.d.ts +23 -0
  125. package/dist/types/astro/module-types.d.ts +14 -0
  126. package/dist/types/astro/pages.d.ts +44 -0
  127. package/dist/types/cli/env.d.ts +12 -0
  128. package/dist/types/cli/init/scaffold.d.ts +154 -0
  129. package/dist/types/components/layout/nav-utils.d.ts +11 -0
  130. package/dist/types/core/adapter.d.ts +47 -0
  131. package/dist/types/core/api-name.d.ts +7 -0
  132. package/dist/types/core/changelog-index.d.ts +13 -0
  133. package/dist/types/core/config-input.d.ts +209 -505
  134. package/dist/types/core/config.d.ts +60 -34
  135. package/dist/types/core/content-assets.d.ts +76 -0
  136. package/dist/types/core/custom-pages.d.ts +33 -0
  137. package/dist/types/core/data.d.ts +28 -11
  138. package/dist/types/core/define-components.d.ts +12 -9
  139. package/dist/types/core/deployment-env.d.ts +6 -11
  140. package/dist/types/core/frontmatter.d.ts +10 -0
  141. package/dist/types/core/graph.d.ts +18 -0
  142. package/dist/types/core/heading-markers.d.ts +54 -0
  143. package/dist/types/core/i18n-ui.d.ts +6 -2
  144. package/dist/types/core/i18n.d.ts +87 -0
  145. package/dist/types/core/includes.d.ts +138 -0
  146. package/dist/types/core/last-modified.d.ts +47 -0
  147. package/dist/types/core/links.d.ts +95 -0
  148. package/dist/types/core/locale-links.d.ts +60 -0
  149. package/dist/types/core/manifest.d.ts +17 -0
  150. package/dist/types/core/meta.d.ts +38 -0
  151. package/dist/types/core/nav-diagnostics.d.ts +26 -0
  152. package/dist/types/core/navigation.d.ts +6 -0
  153. package/dist/types/core/node-require.d.ts +19 -0
  154. package/dist/types/core/package-json.d.ts +13 -0
  155. package/dist/types/core/probe.d.ts +42 -0
  156. package/dist/types/core/project-graph.d.ts +51 -0
  157. package/dist/types/core/project.d.ts +2 -0
  158. package/dist/types/core/safe-href.d.ts +2 -0
  159. package/dist/types/core/safe-links.d.ts +26 -0
  160. package/dist/types/core/schema.d.ts +2291 -441
  161. package/dist/types/core/site-url.d.ts +16 -0
  162. package/dist/types/core/sources/assets.d.ts +36 -0
  163. package/dist/types/core/sources/cache.d.ts +37 -0
  164. package/dist/types/core/sources/collection.d.ts +28 -0
  165. package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
  166. package/dist/types/core/sources/contentful.d.ts +40 -0
  167. package/dist/types/core/sources/filesystem.d.ts +24 -0
  168. package/dist/types/core/sources/github-releases.d.ts +31 -0
  169. package/dist/types/core/sources/json.d.ts +30 -0
  170. package/dist/types/core/sources/lexical.d.ts +19 -0
  171. package/dist/types/core/sources/lower.d.ts +75 -0
  172. package/dist/types/core/sources/mdx-remote.d.ts +28 -0
  173. package/dist/types/core/sources/normalize.d.ts +147 -0
  174. package/dist/types/core/sources/notion.d.ts +131 -0
  175. package/dist/types/core/sources/obsidian.d.ts +46 -0
  176. package/dist/types/core/sources/payload.d.ts +39 -0
  177. package/dist/types/core/sources/portable-text.d.ts +42 -0
  178. package/dist/types/core/sources/read.d.ts +23 -0
  179. package/dist/types/core/sources/remote.d.ts +74 -0
  180. package/dist/types/core/sources/resolve.d.ts +19 -0
  181. package/dist/types/core/sources/sanity.d.ts +43 -0
  182. package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
  183. package/dist/types/core/sources/strapi.d.ts +33 -0
  184. package/dist/types/core/sources/types.d.ts +7 -0
  185. package/dist/types/core/sources/watch.d.ts +45 -0
  186. package/dist/types/core/text-width.d.ts +11 -0
  187. package/dist/types/core/types.d.ts +20 -1
  188. package/dist/types/core/unrecognized-keys.d.ts +7 -0
  189. package/dist/types/core/versions.d.ts +72 -0
  190. package/dist/types/core/yaml.d.ts +9 -0
  191. package/dist/types/deploy/adapter-output.d.ts +45 -0
  192. package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
  193. package/dist/types/deploy/adapters/index.d.ts +29 -0
  194. package/dist/types/deploy/adapters/netlify.d.ts +37 -0
  195. package/dist/types/deploy/adapters/node.d.ts +37 -0
  196. package/dist/types/deploy/adapters/registry.d.ts +133 -0
  197. package/dist/types/deploy/adapters/types.d.ts +71 -0
  198. package/dist/types/deploy/adapters/vercel.d.ts +38 -0
  199. package/dist/types/deploy/artifacts.d.ts +65 -0
  200. package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
  201. package/dist/types/deploy/function-bundle.d.ts +80 -0
  202. package/dist/types/deploy/headers.d.ts +50 -0
  203. package/dist/types/deploy/node-headers.d.ts +42 -0
  204. package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
  205. package/dist/types/deploy/platforms/index.d.ts +18 -0
  206. package/dist/types/deploy/platforms/netlify.d.ts +13 -0
  207. package/dist/types/deploy/platforms/node.d.ts +11 -0
  208. package/dist/types/deploy/platforms/paths.d.ts +27 -0
  209. package/dist/types/deploy/platforms/static.d.ts +10 -0
  210. package/dist/types/deploy/platforms/types.d.ts +104 -0
  211. package/dist/types/deploy/platforms/vercel.d.ts +32 -0
  212. package/dist/types/deploy/redirects.d.ts +53 -0
  213. package/dist/types/deploy/robots.d.ts +8 -0
  214. package/dist/types/deploy/rss.d.ts +31 -0
  215. package/dist/types/deploy/sitemap.d.ts +21 -0
  216. package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
  217. package/dist/types/markdown/code-title.d.ts +32 -0
  218. package/dist/types/markdown/fence-meta.d.ts +23 -0
  219. package/dist/types/markdown/themes.d.ts +3 -3
  220. package/dist/types/openapi/asyncapi.d.ts +129 -0
  221. package/dist/types/openapi/graphql-build.d.ts +8 -0
  222. package/dist/types/openapi/graphql.d.ts +122 -0
  223. package/dist/types/openapi/model.d.ts +158 -0
  224. package/dist/types/openapi/parse.d.ts +57 -0
  225. package/dist/types/openapi/references.d.ts +37 -25
  226. package/dist/types/openapi/render-mdx.d.ts +33 -0
  227. package/dist/types/openapi/sentence.d.ts +7 -0
  228. package/dist/types/openapi/signature.d.ts +10 -0
  229. package/dist/types/openapi/source.d.ts +22 -0
  230. package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
  231. package/dist/types/reference/asyncapi.d.ts +166 -0
  232. package/dist/types/reference/graphql.d.ts +181 -0
  233. package/dist/types/reference/index.d.ts +32 -0
  234. package/dist/types/reference/openapi.d.ts +165 -0
  235. package/dist/types/reference/options.d.ts +157 -0
  236. package/dist/types/reference/scalar.d.ts +136 -0
  237. package/dist/types/reference/schema.d.ts +630 -0
  238. package/dist/types/search/adapters/algolia.d.ts +32 -0
  239. package/dist/types/search/adapters/flexsearch.d.ts +12 -0
  240. package/dist/types/search/adapters/index.d.ts +31 -0
  241. package/dist/types/search/adapters/mixedbread.d.ts +22 -0
  242. package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
  243. package/dist/types/search/adapters/orama.d.ts +13 -0
  244. package/dist/types/search/adapters/pagefind.d.ts +12 -0
  245. package/dist/types/search/adapters/registry.d.ts +228 -0
  246. package/dist/types/search/adapters/types.d.ts +34 -0
  247. package/dist/types/search/adapters/typesense.d.ts +42 -0
  248. package/dist/types/search/build.d.ts +23 -0
  249. package/dist/types/search/documents.d.ts +90 -0
  250. package/dist/types/search/facets.d.ts +3 -0
  251. package/dist/types/search/sync/algolia.d.ts +14 -0
  252. package/dist/types/search/sync/index.d.ts +14 -0
  253. package/dist/types/search/sync/orama-cloud.d.ts +10 -0
  254. package/dist/types/search/sync/typesense.d.ts +14 -0
  255. package/dist/types/sources/contentful.d.ts +68 -0
  256. package/dist/types/sources/custom.d.ts +20 -0
  257. package/dist/types/sources/filesystem.d.ts +42 -0
  258. package/dist/types/sources/github-releases.d.ts +46 -0
  259. package/dist/types/sources/index.d.ts +48 -0
  260. package/dist/types/sources/mdx-remote.d.ts +63 -0
  261. package/dist/types/sources/notion.d.ts +65 -0
  262. package/dist/types/sources/obsidian.d.ts +34 -0
  263. package/dist/types/sources/payload.d.ts +68 -0
  264. package/dist/types/sources/registry.d.ts +1414 -0
  265. package/dist/types/sources/sanity.d.ts +69 -0
  266. package/dist/types/sources/shared.d.ts +46 -0
  267. package/dist/types/sources/strapi.d.ts +66 -0
  268. package/dist/types/theme/icon-kind.d.ts +11 -0
  269. package/dist/types/theme/icons.d.ts +20 -0
  270. package/docs/01-quickstart.mdx +18 -18
  271. package/docs/02-deployment.mdx +50 -26
  272. package/docs/03-upgrading.mdx +351 -0
  273. package/docs/04-migrating.mdx +58 -0
  274. package/docs/08-faq.mdx +3 -3
  275. package/docs/advanced/blog.mdx +13 -6
  276. package/docs/advanced/changelog.mdx +23 -35
  277. package/docs/advanced/custom-pages.mdx +20 -8
  278. package/docs/advanced/meta.ts +1 -8
  279. package/docs/advanced/skills.mdx +8 -0
  280. package/docs/cli/audit.mdx +646 -0
  281. package/docs/cli/doctor.mdx +29 -0
  282. package/docs/{reference/eval.mdx → cli/evals.mdx} +9 -8
  283. package/docs/cli/index.mdx +105 -0
  284. package/docs/cli/meta.ts +7 -0
  285. package/docs/{reference → cli}/translate.mdx +1 -1
  286. package/docs/cli/validate.mdx +41 -0
  287. package/docs/cli/version.mdx +40 -0
  288. package/docs/configuration/analytics.mdx +350 -59
  289. package/docs/configuration/ask-ai.mdx +176 -61
  290. package/docs/configuration/customization.mdx +15 -9
  291. package/docs/configuration/index.mdx +52 -31
  292. package/docs/configuration/search.mdx +75 -54
  293. package/docs/configuration/theming.mdx +24 -15
  294. package/docs/content/components.mdx +21 -5
  295. package/docs/{reference → content}/frontmatter.mdx +37 -1
  296. package/docs/content/i18n.mdx +8 -6
  297. package/docs/content/includes.mdx +2 -4
  298. package/docs/content/index.mdx +1 -1
  299. package/docs/content/islands.mdx +10 -5
  300. package/docs/content/meta.mdx +1 -1
  301. package/docs/content/meta.ts +1 -0
  302. package/docs/content/navigation.mdx +12 -7
  303. package/docs/content/sources.mdx +145 -50
  304. package/docs/content/syntax.mdx +26 -12
  305. package/docs/content/versioning.mdx +3 -3
  306. package/docs/discoverability/agent-discovery.mdx +91 -10
  307. package/docs/discoverability/index.mdx +5 -5
  308. package/docs/discoverability/json-api.mdx +4 -4
  309. package/docs/discoverability/llms-txt.mdx +6 -6
  310. package/docs/discoverability/markdown.mdx +4 -4
  311. package/docs/discoverability/mcp.mdx +11 -11
  312. package/docs/discoverability/sitemap-and-robots.mdx +3 -3
  313. package/docs/index.mdx +2 -2
  314. package/docs/references/asyncapi.mdx +59 -0
  315. package/docs/{advanced → references}/graphql.mdx +45 -32
  316. package/docs/{reference → references}/meta.ts +2 -2
  317. package/docs/references/openapi.mdx +171 -0
  318. package/docs/references/scalar.mdx +64 -0
  319. package/package.json +42 -8
  320. package/skills/blume/SKILL.md +21 -8
  321. package/skills/blume-migrate/SKILL.md +22 -21
  322. package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
  323. package/skills/blume-migrate/references/docusaurus.md +5 -4
  324. package/skills/blume-migrate/references/fumadocs.md +4 -4
  325. package/skills/blume-migrate/references/mintlify.md +23 -8
  326. package/skills/blume-migrate/references/monorepo.md +6 -6
  327. package/skills/blume-migrate/references/starlight.md +4 -4
  328. package/src/ai/agent-readability.ts +26 -22
  329. package/src/ai/ai-catalog.ts +259 -0
  330. package/src/ai/api/handlers.ts +46 -11
  331. package/src/ai/api-catalog.ts +8 -8
  332. package/src/ai/ask-data.ts +1 -1
  333. package/src/ai/ask.ts +624 -100
  334. package/src/ai/changelog-markdown.ts +91 -0
  335. package/src/ai/component-markdown.ts +328 -12
  336. package/src/ai/index.ts +45 -0
  337. package/src/ai/link-headers.ts +19 -6
  338. package/src/ai/llms.ts +26 -15
  339. package/src/ai/markdown.ts +35 -5
  340. package/src/ai/mcp/data.ts +9 -5
  341. package/src/ai/mcp/discovery.ts +1 -1
  342. package/src/ai/openapi-components.ts +4 -1
  343. package/src/ai/relative-links.ts +170 -0
  344. package/src/ai/serializers.ts +2 -2
  345. package/src/ai/skills.ts +1 -1
  346. package/src/ai/web-bot-auth.ts +2 -2
  347. package/src/analytics/adobe.ts +46 -0
  348. package/src/analytics/amplitude.ts +79 -0
  349. package/src/analytics/clarity.ts +46 -0
  350. package/src/analytics/clearbit.ts +47 -0
  351. package/src/analytics/cloudflare.ts +67 -0
  352. package/src/analytics/fathom.ts +64 -0
  353. package/src/analytics/google-analytics.ts +84 -0
  354. package/src/analytics/google-tag-manager.ts +61 -0
  355. package/src/analytics/head.ts +130 -0
  356. package/src/analytics/heap.ts +65 -0
  357. package/src/analytics/hightouch.ts +77 -0
  358. package/src/analytics/hotjar.ts +47 -0
  359. package/src/analytics/index.ts +71 -0
  360. package/src/analytics/inline.ts +21 -0
  361. package/src/analytics/logrocket.ts +71 -0
  362. package/src/analytics/mixpanel.ts +92 -0
  363. package/src/analytics/pirsch.ts +66 -0
  364. package/src/analytics/plausible.ts +83 -0
  365. package/src/analytics/posthog.ts +78 -0
  366. package/src/analytics/schema.ts +60 -0
  367. package/src/analytics/script.ts +60 -0
  368. package/src/analytics/segment.ts +85 -0
  369. package/src/analytics/vercel.ts +50 -0
  370. package/src/astro/adapter-root.ts +7 -9
  371. package/src/astro/component-slots.ts +131 -91
  372. package/src/astro/generate.ts +114 -345
  373. package/src/astro/integration.ts +2 -6
  374. package/src/astro/pages.ts +13 -101
  375. package/src/astro/render-deps.ts +379 -0
  376. package/src/astro/runtime-deps.ts +201 -0
  377. package/src/astro/templates.ts +554 -541
  378. package/src/audit/agent.ts +24 -0
  379. package/src/audit/catalog.ts +2 -2
  380. package/src/audit/checks/assets.ts +2 -2
  381. package/src/audit/checks/dns-aid.ts +1 -1
  382. package/src/audit/checks/duplicates.ts +3 -1
  383. package/src/audit/checks/i18n.ts +1 -1
  384. package/src/audit/checks/indexability.ts +7 -7
  385. package/src/audit/checks/links.ts +2 -2
  386. package/src/audit/checks/llms.ts +6 -6
  387. package/src/audit/checks/network.ts +3 -3
  388. package/src/audit/checks/og-image.ts +2 -2
  389. package/src/audit/checks/robots.ts +1 -1
  390. package/src/audit/checks/sitemap.ts +2 -2
  391. package/src/audit/checks/social.ts +1 -1
  392. package/src/audit/run.ts +4 -3
  393. package/src/audit/terms.ts +31 -0
  394. package/src/audit/url.ts +13 -13
  395. package/src/cli/command-meta.ts +10 -0
  396. package/src/cli/commands/audit.ts +39 -20
  397. package/src/cli/commands/build.ts +71 -314
  398. package/src/cli/commands/check.ts +1 -0
  399. package/src/cli/commands/dev.ts +40 -1
  400. package/src/cli/commands/doctor.ts +90 -14
  401. package/src/cli/commands/eject.ts +44 -7
  402. package/src/cli/commands/eval.ts +2 -2
  403. package/src/cli/commands/init.ts +161 -41
  404. package/src/cli/commands/migrate.ts +121 -0
  405. package/src/cli/commands/preview.ts +7 -1
  406. package/src/cli/commands/translate.ts +2 -2
  407. package/src/cli/commands/upgrade.ts +141 -0
  408. package/src/cli/commands/version.ts +57 -44
  409. package/src/cli/eject-scripts.ts +121 -6
  410. package/src/cli/index.ts +11 -1
  411. package/src/cli/init/install.ts +70 -0
  412. package/src/cli/init/questions.ts +7 -0
  413. package/src/cli/init/scaffold.ts +459 -75
  414. package/src/cli/lazy-command.ts +37 -1
  415. package/src/cli/prepare.ts +40 -6
  416. package/src/cli/required-secrets.ts +38 -11
  417. package/src/cli/unknown-flags.ts +266 -0
  418. package/src/cli/yarn-pnp.ts +52 -0
  419. package/src/components/content/AccordionItem.astro +7 -1
  420. package/src/components/content/Card.astro +4 -3
  421. package/src/components/content/ColorItem.astro +22 -4
  422. package/src/components/content/GithubInfo.astro +2 -2
  423. package/src/components/content/Prompt.astro +25 -25
  424. package/src/components/content/Tabs.astro +3 -1
  425. package/src/components/content/Tile.astro +2 -3
  426. package/src/components/content/Tooltip.astro +57 -9
  427. package/src/components/content/content-strings.ts +34 -0
  428. package/src/components/content/diff.ts +1 -1
  429. package/src/components/content/mermaid-element.ts +13 -2
  430. package/src/components/content/prompt-markdown.ts +292 -0
  431. package/src/components/content/tooltip-id.ts +41 -0
  432. package/src/components/islands/hooks.ts +75 -3
  433. package/src/components/layout/Analytics.astro +21 -79
  434. package/src/components/layout/Banner.astro +3 -1
  435. package/src/components/layout/DiscoveryLinks.astro +69 -0
  436. package/src/components/layout/Header.astro +83 -20
  437. package/src/components/layout/LanguageSwitcher.astro +4 -1
  438. package/src/components/layout/Logo.astro +23 -2
  439. package/src/components/layout/NavSelector.astro +13 -2
  440. package/src/components/layout/NavTree.astro +12 -3
  441. package/src/components/layout/NavTreeScript.astro +17 -3
  442. package/src/components/layout/PageActions.astro +3 -3
  443. package/src/components/layout/PageFeedback.astro +11 -1
  444. package/src/components/layout/PageLayout.astro +52 -5
  445. package/src/components/layout/ReferenceLayout.astro +21 -12
  446. package/src/components/layout/RootLayout.astro +69 -43
  447. package/src/components/layout/Search.astro +30 -6
  448. package/src/components/layout/WebMcp.astro +1 -1
  449. package/src/components/layout/analytics-client.ts +101 -15
  450. package/src/components/layout/drawer-inert.ts +113 -15
  451. package/src/components/layout/dropdown-clamp.ts +105 -0
  452. package/src/components/layout/head-scripts.ts +15 -6
  453. package/src/components/layout/nav-utils.ts +17 -0
  454. package/src/components/layout/search/algolia.ts +8 -8
  455. package/src/components/layout/search/orama-cloud.ts +9 -7
  456. package/src/components/layout/search/typesense.ts +11 -17
  457. package/src/components/openapi/MessageComposer.astro +2 -2
  458. package/src/components/openapi/Playground.astro +2 -2
  459. package/src/components/openapi/playground-client.ts +79 -10
  460. package/src/core/changelog-index.ts +24 -0
  461. package/src/core/component-overrides.ts +399 -154
  462. package/src/core/config-input.ts +212 -543
  463. package/src/core/config.ts +130 -41
  464. package/src/core/custom-pages.ts +105 -0
  465. package/src/core/data.ts +32 -11
  466. package/src/core/define-components.ts +12 -9
  467. package/src/core/deployment-env.ts +18 -74
  468. package/src/core/diagnostics.ts +14 -7
  469. package/src/core/graph.ts +69 -25
  470. package/src/core/i18n-ui.ts +6 -2
  471. package/src/core/i18n.ts +17 -0
  472. package/src/core/includes.ts +156 -38
  473. package/src/core/last-modified.ts +6 -11
  474. package/src/core/links.ts +166 -2
  475. package/src/core/manifest.ts +10 -3
  476. package/src/core/navigation.ts +137 -19
  477. package/src/core/new-tab.ts +35 -0
  478. package/src/core/node-require.ts +21 -0
  479. package/src/core/project-graph.ts +42 -26
  480. package/src/core/project.ts +9 -4
  481. package/src/core/request-body.ts +61 -0
  482. package/src/core/safe-href.ts +28 -0
  483. package/src/core/safe-links.ts +68 -0
  484. package/src/core/schema.ts +531 -721
  485. package/src/core/server-features.ts +8 -9
  486. package/src/core/sources/assets.ts +47 -11
  487. package/src/core/sources/collection.ts +67 -0
  488. package/src/core/sources/contentful-rich-text.ts +285 -0
  489. package/src/core/sources/contentful.ts +173 -0
  490. package/src/core/sources/github-releases.ts +51 -1
  491. package/src/core/sources/json.ts +71 -0
  492. package/src/core/sources/lexical.ts +195 -0
  493. package/src/core/sources/lower.ts +226 -0
  494. package/src/core/sources/normalize.ts +52 -2
  495. package/src/core/sources/notion.ts +39 -28
  496. package/src/core/sources/payload.ts +135 -0
  497. package/src/core/sources/portable-text.ts +11 -16
  498. package/src/core/sources/remote.ts +226 -0
  499. package/src/core/sources/resolve.ts +104 -166
  500. package/src/core/sources/sanity.ts +16 -49
  501. package/src/core/sources/strapi-blocks.ts +124 -0
  502. package/src/core/sources/strapi.ts +191 -0
  503. package/src/core/sources/types.ts +12 -1
  504. package/src/core/types.ts +20 -1
  505. package/src/core/ui-packs/ar.ts +6 -2
  506. package/src/core/ui-packs/bg.ts +6 -2
  507. package/src/core/ui-packs/bn.ts +6 -2
  508. package/src/core/ui-packs/ca.ts +3 -1
  509. package/src/core/ui-packs/cs.ts +6 -2
  510. package/src/core/ui-packs/da.ts +6 -2
  511. package/src/core/ui-packs/de.ts +6 -2
  512. package/src/core/ui-packs/el.ts +3 -1
  513. package/src/core/ui-packs/es.ts +3 -1
  514. package/src/core/ui-packs/fa.ts +6 -2
  515. package/src/core/ui-packs/fi.ts +6 -2
  516. package/src/core/ui-packs/fr.ts +3 -1
  517. package/src/core/ui-packs/he.ts +6 -2
  518. package/src/core/ui-packs/hi.ts +6 -2
  519. package/src/core/ui-packs/hr.ts +6 -2
  520. package/src/core/ui-packs/hu.ts +6 -2
  521. package/src/core/ui-packs/id.ts +6 -2
  522. package/src/core/ui-packs/it.ts +3 -1
  523. package/src/core/ui-packs/ja.ts +3 -1
  524. package/src/core/ui-packs/ko.ts +3 -1
  525. package/src/core/ui-packs/nl.ts +6 -2
  526. package/src/core/ui-packs/no.ts +6 -2
  527. package/src/core/ui-packs/pl.ts +6 -2
  528. package/src/core/ui-packs/pt-br.ts +3 -1
  529. package/src/core/ui-packs/pt.ts +3 -1
  530. package/src/core/ui-packs/ro.ts +6 -2
  531. package/src/core/ui-packs/ru.ts +6 -2
  532. package/src/core/ui-packs/sk.ts +6 -2
  533. package/src/core/ui-packs/sr.ts +6 -2
  534. package/src/core/ui-packs/sv.ts +6 -2
  535. package/src/core/ui-packs/th.ts +3 -1
  536. package/src/core/ui-packs/tr.ts +6 -2
  537. package/src/core/ui-packs/uk.ts +6 -2
  538. package/src/core/ui-packs/vi.ts +3 -1
  539. package/src/core/ui-packs/zh-tw.ts +3 -1
  540. package/src/core/ui-packs/zh.ts +3 -1
  541. package/src/core/unrecognized-keys.ts +10 -0
  542. package/src/core/version-cut.ts +116 -8
  543. package/src/deploy/adapter-output.ts +57 -97
  544. package/src/deploy/adapters/cloudflare.ts +39 -0
  545. package/src/deploy/adapters/index.ts +41 -0
  546. package/src/deploy/adapters/netlify.ts +34 -0
  547. package/src/deploy/adapters/node.ts +34 -0
  548. package/src/deploy/adapters/registry.ts +127 -0
  549. package/src/deploy/adapters/types.ts +92 -0
  550. package/src/deploy/adapters/vercel.ts +35 -0
  551. package/src/deploy/artifacts.ts +67 -28
  552. package/src/deploy/cloudflare-negotiation.ts +179 -100
  553. package/src/deploy/function-bundle.ts +18 -3
  554. package/src/deploy/headers.ts +129 -22
  555. package/src/deploy/node-headers.ts +198 -0
  556. package/src/deploy/platforms/cloudflare.ts +312 -0
  557. package/src/deploy/platforms/index.ts +67 -0
  558. package/src/deploy/platforms/netlify.ts +52 -0
  559. package/src/deploy/platforms/node.ts +40 -0
  560. package/src/deploy/platforms/paths.ts +42 -0
  561. package/src/deploy/platforms/static.ts +29 -0
  562. package/src/deploy/platforms/types.ts +112 -0
  563. package/src/deploy/platforms/vercel.ts +193 -0
  564. package/src/deploy/redirects.ts +30 -18
  565. package/src/deploy/robots.ts +3 -3
  566. package/src/deploy/rss.ts +2 -2
  567. package/src/deploy/sitemap.ts +2 -2
  568. package/src/deploy/vercel-negotiation.ts +27 -4
  569. package/src/eval/agents.ts +32 -1
  570. package/src/eval/findings.ts +19 -11
  571. package/src/eval/report.ts +10 -2
  572. package/src/markdown/external-links.ts +65 -0
  573. package/src/markdown/include.ts +45 -27
  574. package/src/markdown/index.ts +34 -9
  575. package/src/markdown/inline-code.ts +12 -6
  576. package/src/markdown/relative-links.ts +324 -0
  577. package/src/markdown/themes.ts +3 -3
  578. package/src/migrate/migrate.ts +149 -0
  579. package/src/openapi/parse.ts +64 -16
  580. package/src/openapi/proxy.ts +62 -10
  581. package/src/openapi/references.ts +147 -147
  582. package/src/openapi/render-mdx.ts +32 -10
  583. package/src/openapi/scalar.ts +15 -13
  584. package/src/openapi/sentence.ts +14 -0
  585. package/src/openapi/source.ts +37 -21
  586. package/src/openapi/spec-dependency-error.ts +15 -0
  587. package/src/reference/asyncapi.ts +83 -0
  588. package/src/reference/graphql.ts +89 -0
  589. package/src/reference/index.ts +40 -0
  590. package/src/reference/openapi.ts +83 -0
  591. package/src/reference/options.ts +201 -0
  592. package/src/reference/scalar.ts +136 -0
  593. package/src/reference/schema.ts +88 -0
  594. package/src/registry/eject.ts +97 -42
  595. package/src/search/adapters/algolia.ts +46 -0
  596. package/src/search/adapters/flexsearch.ts +29 -0
  597. package/src/search/adapters/index.ts +39 -0
  598. package/src/search/adapters/mixedbread.ts +39 -0
  599. package/src/search/adapters/orama-cloud.ts +51 -0
  600. package/src/search/adapters/orama.ts +24 -0
  601. package/src/search/adapters/pagefind.ts +27 -0
  602. package/src/search/adapters/registry.ts +131 -0
  603. package/src/search/adapters/types.ts +46 -0
  604. package/src/search/adapters/typesense.ts +57 -0
  605. package/src/search/build.ts +33 -7
  606. package/src/search/documents.ts +6 -0
  607. package/src/search/sync/algolia.ts +12 -11
  608. package/src/search/sync/index.ts +35 -20
  609. package/src/search/sync/orama-cloud.ts +12 -9
  610. package/src/search/sync/typesense.ts +15 -13
  611. package/src/sources/contentful.ts +63 -0
  612. package/src/sources/custom.ts +38 -0
  613. package/src/sources/filesystem.ts +51 -0
  614. package/src/sources/github-releases.ts +52 -0
  615. package/src/sources/index.ts +60 -0
  616. package/src/sources/mdx-remote.ts +76 -0
  617. package/src/sources/notion.ts +63 -0
  618. package/src/sources/obsidian.ts +38 -0
  619. package/src/sources/payload.ts +60 -0
  620. package/src/sources/registry.ts +182 -0
  621. package/src/sources/sanity.ts +66 -0
  622. package/src/sources/shared.ts +52 -0
  623. package/src/sources/strapi.ts +58 -0
  624. package/src/theme/entry.ts +47 -2
  625. package/src/translate/report.ts +40 -7
  626. package/src/upgrade/upgrade.ts +499 -0
  627. package/dist/cli/chunk-12dxjqk7.js.map +0 -10
  628. package/dist/cli/chunk-196vjxp9.js.map +0 -13
  629. package/dist/cli/chunk-2mzebbbz.js +0 -69
  630. package/dist/cli/chunk-2z47ypj8.js.map +0 -11
  631. package/dist/cli/chunk-30e87n55.js +0 -108
  632. package/dist/cli/chunk-30e87n55.js.map +0 -10
  633. package/dist/cli/chunk-450a7rcr.js +0 -185
  634. package/dist/cli/chunk-450a7rcr.js.map +0 -11
  635. package/dist/cli/chunk-5n7t497w.js.map +0 -40
  636. package/dist/cli/chunk-61j18dwk.js +0 -5342
  637. package/dist/cli/chunk-61j18dwk.js.map +0 -58
  638. package/dist/cli/chunk-88by27n5.js +0 -17
  639. package/dist/cli/chunk-88by27n5.js.map +0 -10
  640. package/dist/cli/chunk-8cd8tj54.js.map +0 -34
  641. package/dist/cli/chunk-aztttvb3.js +0 -381
  642. package/dist/cli/chunk-aztttvb3.js.map +0 -12
  643. package/dist/cli/chunk-cvky9gb2.js.map +0 -11
  644. package/dist/cli/chunk-ejjx8znq.js.map +0 -15
  645. package/dist/cli/chunk-fmceyezb.js +0 -1007
  646. package/dist/cli/chunk-fmceyezb.js.map +0 -13
  647. package/dist/cli/chunk-hdm2dkd2.js.map +0 -48
  648. package/dist/cli/chunk-hs3gbh8p.js.map +0 -10
  649. package/dist/cli/chunk-jbj4qhfw.js +0 -30
  650. package/dist/cli/chunk-jbj4qhfw.js.map +0 -10
  651. package/dist/cli/chunk-mqb2ka8m.js +0 -68
  652. package/dist/cli/chunk-mqb2ka8m.js.map +0 -10
  653. package/dist/cli/chunk-mt76t7dj.js.map +0 -11
  654. package/dist/cli/chunk-q4rae3bg.js +0 -60
  655. package/dist/cli/chunk-q4rae3bg.js.map +0 -10
  656. package/dist/cli/chunk-ra1v2nc2.js +0 -35
  657. package/dist/cli/chunk-ra1v2nc2.js.map +0 -10
  658. package/dist/cli/chunk-t3tj0dgr.js.map +0 -15
  659. package/dist/cli/chunk-tqa1s0k8.js +0 -69
  660. package/dist/cli/chunk-tqa1s0k8.js.map +0 -11
  661. package/dist/cli/chunk-vh9w1sgp.js +0 -73
  662. package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
  663. package/dist/cli/chunk-vkrsvbr5.js +0 -107
  664. package/dist/cli/chunk-vkrsvbr5.js.map +0 -11
  665. package/dist/cli/chunk-vrfp10qk.js +0 -81
  666. package/dist/cli/chunk-vrfp10qk.js.map +0 -10
  667. package/dist/cli/chunk-wjt80jps.js +0 -1049
  668. package/dist/cli/chunk-wjt80jps.js.map +0 -24
  669. package/docs/advanced/api-reference.mdx +0 -240
  670. package/docs/reference/cli.mdx +0 -197
  671. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
  672. package/src/components/content/changelog-element.ts +0 -69
  673. package/src/search/providers.ts +0 -91
  674. /package/dist/cli/{chunk-3w7b2vcx.js.map → chunk-7ez8ny0t.js.map} +0 -0
  675. /package/dist/cli/{chunk-688e0dde.js.map → chunk-88cpgt6h.js.map} +0 -0
  676. /package/dist/cli/{chunk-exeeb35e.js.map → chunk-f7t03s3g.js.map} +0 -0
  677. /package/dist/cli/{chunk-ppfvdcd4.js.map → chunk-zxccj738.js.map} +0 -0
@@ -1,115 +1,78 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- getBlumeVersion,
4
- BlumeError,
5
- diagnosticsFromIssues,
6
- diagnosticsFromZod
7
- } from "./index.js";
8
- import {
9
- applyDeploymentEnv,
10
- slugify,
11
3
  blumeReferences,
12
- createModuleLoader,
13
- resolveProjectContext,
4
+ frontmatter_default,
5
+ resolveDocsCollection,
6
+ CHANGELOG_INDEX_ROUTE,
7
+ hasChangelogIndex,
8
+ discoverPages,
9
+ customStaticRoutes,
10
+ nextFenceState,
11
+ hashText,
12
+ pollingWatch,
13
+ snapshotCache,
14
+ loadWithCache,
15
+ resolveFallbackLocale,
16
+ localizeRoute,
17
+ localizeInternalPath,
18
+ i18nDiagnostics,
14
19
  PUBLIC_API_URL,
15
- UI_PACKS,
16
20
  pageMetaSchema,
17
21
  folderMetaSchema,
22
+ versionizeRoute,
23
+ versionsDiagnostics,
24
+ slugify,
25
+ slugifyPath,
26
+ extractHeadings,
27
+ strippedLineOffset,
28
+ isStringValue,
29
+ placeEntryRef,
30
+ resolveEntryRoute,
31
+ localizedRoute,
32
+ normalizeEntry,
33
+ hasIncludeStatements,
34
+ expandIncludes,
35
+ parseYouTubeId,
36
+ nodeRequire,
37
+ escapeMarkdownText,
38
+ renderInline,
39
+ renderLink,
40
+ headingPrefix,
41
+ blockquote,
42
+ listItem,
43
+ indent,
44
+ codeFence,
45
+ image,
46
+ writesMdx,
47
+ unsupported,
48
+ joinBlocks,
49
+ markdownDocument,
50
+ absoluteUrl,
51
+ neutralizeUnsafeLinks,
52
+ columnsPrefix,
53
+ openApiSource,
54
+ createModuleLoader,
55
+ resolveProjectContext,
18
56
  loadConfig
19
- } from "./chunk-61j18dwk.js";
57
+ } from "./chunk-xaz13gwg.js";
58
+ import {
59
+ getBlumeVersion,
60
+ BlumeError,
61
+ diagnosticsFromZod
62
+ } from "./index.js";
20
63
  import {
21
64
  trimChar,
22
65
  isInternalPath,
23
66
  withBasePath,
24
67
  stripBasePath
25
- } from "./chunk-2mzebbbz.js";
26
-
27
- // src/core/i18n.ts
28
- var KNOWN_LOCALES = new Set([...Object.keys(UI_PACKS), "en"].map((code) => code.toLowerCase()));
29
- var i18nEnabled = (config) => config.i18n !== undefined;
30
- var localeCodes = (i18n) => i18n.locales.map((locale) => locale.code);
31
- var resolveFallbackLocale = (i18n) => {
32
- if (i18n.fallbackLocale === null) {
33
- return null;
34
- }
35
- return i18n.fallbackLocale ?? i18n.defaultLocale;
36
- };
37
- var localePrefix = (code, i18n) => code === i18n.defaultLocale && i18n.hideDefaultLocalePrefix ? "" : `/${code}`;
38
- var localizeRoute = (logicalRoute, code, i18n) => {
39
- const prefix = localePrefix(code, i18n);
40
- if (!prefix) {
41
- return logicalRoute;
42
- }
43
- return logicalRoute === "/" ? prefix : `${prefix}${logicalRoute}`;
44
- };
45
- var detectLocale = (parts, i18n) => {
46
- const first = parts[0]?.toLowerCase();
47
- const matched = i18n.locales.find((locale) => locale.code !== i18n.defaultLocale && locale.code.toLowerCase() === first);
48
- if (matched) {
49
- return { locale: matched.code, rest: parts.slice(1) };
50
- }
51
- return { locale: i18n.defaultLocale, rest: parts };
52
- };
53
- var localePlacement = (rel, ext, i18n) => {
54
- const base = rel.slice(0, rel.length - ext.length);
55
- if (base.endsWith(".$")) {
56
- const shared = `${base.slice(0, -2)}${ext}`;
57
- return {
58
- locales: i18n.locales.map((locale) => locale.code),
59
- navPath: i18n.parser === "dir" ? detectLocale(shared.split("/"), i18n).rest.join("/") : shared
60
- };
61
- }
62
- if (i18n.parser === "dot") {
63
- const lastDot = base.lastIndexOf(".");
64
- if (lastDot > base.lastIndexOf("/")) {
65
- const suffix = base.slice(lastDot + 1).toLowerCase();
66
- const matched = i18n.locales.find((locale) => locale.code.toLowerCase() === suffix);
67
- if (matched) {
68
- return {
69
- locales: [matched.code],
70
- navPath: `${base.slice(0, lastDot)}${ext}`
71
- };
72
- }
73
- }
74
- return { locales: [i18n.defaultLocale], navPath: rel };
75
- }
76
- const { locale, rest } = detectLocale(rel.split("/"), i18n);
77
- return { locales: [locale], navPath: rest.join("/") };
78
- };
79
- var localeTargetPath = (rel, ext, locale, i18n) => {
80
- if (i18n.parser === "dot") {
81
- let base = rel.slice(0, rel.length - ext.length);
82
- const lastDot = base.lastIndexOf(".");
83
- if (lastDot > base.lastIndexOf("/")) {
84
- const suffix = base.slice(lastDot + 1).toLowerCase();
85
- if (i18n.locales.some((entry) => entry.code.toLowerCase() === suffix)) {
86
- base = base.slice(0, lastDot);
87
- }
88
- }
89
- return `${base}.${locale}${ext}`;
90
- }
91
- return `${locale}/${rel}`;
92
- };
93
- var i18nDiagnostics = (pages, i18n, versions) => {
94
- const configured = new Set(i18n.locales.map((locale) => locale.code.toLowerCase()));
95
- const versionDirs = new Set(versions?.archived.map((version) => version.id));
96
- const seen = new Set;
97
- const diagnostics = [];
98
- for (const page of pages) {
99
- const parts = page.source.ref.split("/");
100
- const first = (parts[0] && versionDirs.has(parts[0]) ? parts[1] : parts[0])?.toLowerCase();
101
- if (first && !seen.has(first) && KNOWN_LOCALES.has(first) && !configured.has(first)) {
102
- seen.add(first);
103
- diagnostics.push({
104
- code: "BLUME_I18N_UNCONFIGURED_LOCALE",
105
- message: `Folder "${first}/" looks like a locale, but "${first}" is not in i18n.locales — its pages are treated as "${i18n.defaultLocale}" content at /${first}/….`,
106
- severity: "warning",
107
- suggestion: `Add { code: "${first}", label: "…" } to i18n.locales, or rename the folder if it isn't a translation.`
108
- });
109
- }
110
- }
111
- return diagnostics;
112
- };
68
+ } from "./chunk-jts8mvcz.js";
69
+ import {
70
+ BLUME_IGNORE_DIRS,
71
+ baselineScanIgnore,
72
+ BLUME_WATCH_IGNORE_DIRS,
73
+ excludeDirSegments,
74
+ ignoringWatchListener
75
+ } from "./chunk-abh8yjkn.js";
113
76
 
114
77
  // src/core/manifest.ts
115
78
  var versionAlternateKey = (versionKey, locale) => `${versionKey}\x00${locale}`;
@@ -131,7 +94,7 @@ var buildFallbackRoutes = (graph, i18n, alternatesByKey, basePath, versionAltern
131
94
  if (!fallback) {
132
95
  return [];
133
96
  }
134
- const fallbackPages = new Map(graph.pages.flatMap((page) => page.locale === fallback ? [[page.translationKey, page]] : []));
97
+ const fallbackPages = new Map(graph.pages.flatMap((page) => page.locale === fallback && !page.monolingual ? [[page.translationKey, page]] : []));
135
98
  const routes = [];
136
99
  for (const { code } of i18n.locales) {
137
100
  if (code === fallback) {
@@ -161,6 +124,7 @@ var buildFallbackRoutes = (graph, i18n, alternatesByKey, basePath, versionAltern
161
124
  indexable: false,
162
125
  lastModified: source.lastModified,
163
126
  locale: code,
127
+ monolingual: source.monolingual,
164
128
  path,
165
129
  source: source.source,
166
130
  sourcePath: source.sourcePath,
@@ -174,7 +138,7 @@ var buildFallbackRoutes = (graph, i18n, alternatesByKey, basePath, versionAltern
174
138
  };
175
139
  var buildManifest = (options) => {
176
140
  const { context, config, graph } = options;
177
- const searchEnabled = config.search.provider !== "none";
141
+ const searchEnabled = config.search.provider.mode !== "none";
178
142
  const { i18n } = config;
179
143
  const alternatesByKey = new Map;
180
144
  if (i18n) {
@@ -203,6 +167,7 @@ var buildManifest = (options) => {
203
167
  indexable: searchEnabled && contentIndexable(page, config),
204
168
  lastModified: page.lastModified,
205
169
  locale: page.locale,
170
+ monolingual: page.monolingual,
206
171
  path: page.route,
207
172
  source: page.source,
208
173
  sourcePath: page.sourcePath,
@@ -223,7 +188,7 @@ var buildManifest = (options) => {
223
188
  return {
224
189
  blumeVersion: getBlumeVersion(),
225
190
  contentRoot: context.contentRoot,
226
- output: config.deployment.output,
191
+ output: config.deployment.options.output,
227
192
  projectRoot: context.root,
228
193
  routes,
229
194
  version: MANIFEST_VERSION
@@ -231,7 +196,7 @@ var buildManifest = (options) => {
231
196
  };
232
197
 
233
198
  // src/core/project-graph.ts
234
- import { isAbsolute as isAbsolute5, relative as relative6 } from "pathe";
199
+ import { isAbsolute as isAbsolute3, relative as relative5 } from "pathe";
235
200
 
236
201
  // src/theme/icon-kind.ts
237
202
  var IMAGE_ICON = /^(?:https?:\/\/|data:image\/|\/|\.{1,2}\/)|\.(?:avif|gif|jpe?g|png|svg|webp)$/iu;
@@ -428,7 +393,46 @@ var GROUP_FOLDER = /^\((?<label>.+)\)$/u;
428
393
  var WORD_SPLIT = /[-_]/u;
429
394
  var isUnderPath = (route, base) => base === "/" || route === base || route.startsWith(`${base}/`);
430
395
  var isRootTab = (tab, root) => isUnderPath(root, tab.path);
431
- var humanize = (segment) => segment.replace(NUMERIC_PREFIX, "").split(WORD_SPLIT).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
396
+ var sidebarIcon = (page) => page.meta.sidebar.icon ?? page.meta.icon;
397
+ var WORD_FORMS = new Map([
398
+ ["ai", "AI"],
399
+ ["api", "API"],
400
+ ["apis", "APIs"],
401
+ ["asyncapi", "AsyncAPI"],
402
+ ["cli", "CLI"],
403
+ ["css", "CSS"],
404
+ ["faq", "FAQ"],
405
+ ["faqs", "FAQs"],
406
+ ["graphql", "GraphQL"],
407
+ ["html", "HTML"],
408
+ ["http", "HTTP"],
409
+ ["https", "HTTPS"],
410
+ ["id", "ID"],
411
+ ["ids", "IDs"],
412
+ ["ios", "iOS"],
413
+ ["js", "JS"],
414
+ ["json", "JSON"],
415
+ ["jwt", "JWT"],
416
+ ["llm", "LLM"],
417
+ ["llms", "LLMs"],
418
+ ["macos", "macOS"],
419
+ ["mcp", "MCP"],
420
+ ["oauth", "OAuth"],
421
+ ["openapi", "OpenAPI"],
422
+ ["rss", "RSS"],
423
+ ["sdk", "SDK"],
424
+ ["sdks", "SDKs"],
425
+ ["seo", "SEO"],
426
+ ["sql", "SQL"],
427
+ ["sso", "SSO"],
428
+ ["ts", "TS"],
429
+ ["ui", "UI"],
430
+ ["url", "URL"],
431
+ ["urls", "URLs"],
432
+ ["xml", "XML"],
433
+ ["yaml", "YAML"]
434
+ ]);
435
+ var humanize = (segment) => segment.replace(NUMERIC_PREFIX, "").split(WORD_SPLIT).filter(Boolean).map((word) => WORD_FORMS.get(word) ?? word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
432
436
  var numericOrder = (segment) => {
433
437
  const value = segment.match(NUMERIC_PREFIX)?.groups?.order;
434
438
  return value ? Math.trunc(Number(value)) : Number.POSITIVE_INFINITY;
@@ -486,7 +490,8 @@ var metaKey = (path, metaPrefix) => {
486
490
  }
487
491
  return path ? `${metaPrefix}/${path}` : metaPrefix;
488
492
  };
489
- var applyFolderMeta = (group, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay) => {
493
+ var applyFolderMeta = (group, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay, indexRoute) => {
494
+ group.route = indexRoute.get(group.path);
490
495
  const meta = folderMeta.get(metaKey(group.path, metaPrefix)) ?? sharedMeta.get(metaKey(group.path, sharedMetaPrefix));
491
496
  group.display = indexDisplay.get(group.path) ?? meta?.display;
492
497
  if (meta) {
@@ -509,12 +514,12 @@ var applyFolderMeta = (group, folderMeta, sharedMeta, metaPrefix, sharedMetaPref
509
514
  }
510
515
  for (const child of group.children) {
511
516
  if (child.kind === "group") {
512
- applyFolderMeta(child, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay);
517
+ applyFolderMeta(child, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay, indexRoute);
513
518
  }
514
519
  }
515
520
  };
516
521
  var indexTitleMismatchDiagnostic = (page, folderPath, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix) => {
517
- if (!page.meta.title || page.fallback || folderPath === "") {
522
+ if (!(page.meta.title && page.meta.sidebar.hidden) || page.fallback || folderPath === "") {
518
523
  return;
519
524
  }
520
525
  const meta = folderMeta.get(metaKey(folderPath, metaPrefix)) ?? sharedMeta.get(metaKey(folderPath, sharedMetaPrefix));
@@ -656,12 +661,14 @@ var toNavNode = (node, display) => {
656
661
  icon: node.icon,
657
662
  kind: "group",
658
663
  label: node.label,
659
- path: node.routePath
664
+ path: node.routePath,
665
+ route: node.route
660
666
  };
661
667
  };
662
668
  var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, display, tabPaths, diagnostics = []) => {
663
669
  const root = createGroup("", "", "", 0);
664
670
  const indexDisplay = new Map;
671
+ const indexRoute = new Map;
665
672
  for (const page of pages) {
666
673
  const parts = page.navPath.split("/");
667
674
  const filename = parts.at(-1) ?? page.navPath;
@@ -675,6 +682,9 @@ var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, sharedM
675
682
  if (page.meta.sidebar.display && !page.fallback) {
676
683
  indexDisplay.set(dirs.join("/"), page.meta.sidebar.display);
677
684
  }
685
+ if (dirs.length > 0) {
686
+ indexRoute.set(dirs.join("/"), page.route);
687
+ }
678
688
  }
679
689
  if (page.meta.sidebar.hidden) {
680
690
  continue;
@@ -698,7 +708,7 @@ var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, sharedM
698
708
  deprecated: page.meta.deprecated || undefined,
699
709
  description: page.description,
700
710
  file: page.sourcePath,
701
- icon: page.meta.sidebar.icon,
711
+ icon: sidebarIcon(page),
702
712
  key: segmentKey(stem),
703
713
  kind: "page",
704
714
  label: page.meta.sidebar.label ?? page.title,
@@ -708,7 +718,7 @@ var buildFileSystemSidebar = (pages, folderMeta, sharedMeta, metaPrefix, sharedM
708
718
  route: page.route
709
719
  });
710
720
  }
711
- applyFolderMeta(root, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay);
721
+ applyFolderMeta(root, folderMeta, sharedMeta, metaPrefix, sharedMetaPrefix, indexDisplay, indexRoute);
712
722
  sortNodes(root.children, diagnostics);
713
723
  hoistPages(root.children, display, true);
714
724
  hoistTabSections(root.children, tabPaths, display);
@@ -741,7 +751,7 @@ var configItemToNode = (item, byRoute, basePath) => {
741
751
  badge: page.meta.sidebar.badge,
742
752
  deprecated: page.meta.deprecated || undefined,
743
753
  description: page.description,
744
- icon: page.meta.sidebar.icon,
754
+ icon: sidebarIcon(page),
745
755
  kind: "page",
746
756
  label: page.meta.sidebar.label ?? page.title,
747
757
  pageId: page.id,
@@ -796,7 +806,10 @@ var buildConfigSidebar = (items, byRoute, display, basePath) => {
796
806
  }
797
807
  return nodes;
798
808
  };
799
- var resolveTabHref = (sidebar, path) => {
809
+ var resolveTabHref = (sidebar, path, extraRoutes, basePath) => {
810
+ if (extraRoutes.has(path)) {
811
+ return path;
812
+ }
800
813
  let first;
801
814
  const walk = (nodes) => {
802
815
  for (const node of nodes) {
@@ -813,10 +826,14 @@ var resolveTabHref = (sidebar, path) => {
813
826
  }
814
827
  return false;
815
828
  };
816
- return walk(sidebar) ? path : first ?? path;
829
+ if (walk(sidebar)) {
830
+ return path;
831
+ }
832
+ const bare = stripBasePath(basePath, path);
833
+ return extraRoutes.has(bare) ? bare : first ?? path;
817
834
  };
818
- var withTabHrefs = (tabs, sidebar) => tabs.map((tab) => {
819
- const href = tab.href ?? resolveTabHref(sidebar, tab.path);
835
+ var withTabHrefs = (tabs, sidebar, extraRoutes, basePath) => tabs.map((tab) => {
836
+ const href = tab.href ?? resolveTabHref(sidebar, tab.path, extraRoutes, basePath);
820
837
  return href === tab.path ? tab : { ...tab, href };
821
838
  });
822
839
  var rebaseNavChrome = (basePath, options) => {
@@ -863,6 +880,7 @@ var buildNavigation = (pages, options) => {
863
880
  const metaPrefix = options.metaPrefix ?? "";
864
881
  const sharedMetaPrefix = options.sharedMetaPrefix ?? "";
865
882
  const sharedFolderMeta = options.sharedFolderMeta ?? new Map;
883
+ const extraRoutes = options.extraRoutes ?? new Set;
866
884
  const { actions, cta, featured, selectors, tabs } = rebaseNavChrome(basePath, options);
867
885
  const byRoute = new Map(pages.map((page) => [
868
886
  options.refByLogical ? page.translationKey : page.route,
@@ -888,7 +906,7 @@ var buildNavigation = (pages, options) => {
888
906
  root: rootTabPath,
889
907
  selectors,
890
908
  sidebar,
891
- tabs: withTabHrefs(tabs, sidebar)
909
+ tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath)
892
910
  };
893
911
  }
894
912
  const sidebar = buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, sharedMetaPrefix, display, new Set(tabs.flatMap((tab) => isRootTab(tab, rootTabPath) ? [] : [tab.path])), diagnostics);
@@ -899,48 +917,10 @@ var buildNavigation = (pages, options) => {
899
917
  root: rootTabPath,
900
918
  selectors,
901
919
  sidebar,
902
- tabs: withTabHrefs(tabs, sidebar)
920
+ tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath)
903
921
  };
904
922
  };
905
923
 
906
- // src/core/versions.ts
907
- var VERSION_LIKE = /^v\d/iu;
908
- var detectVersion = (parts, versions) => {
909
- const [first] = parts;
910
- if (first && versions.archived.some((version) => version.id === first)) {
911
- return { rest: parts.slice(1), version: first };
912
- }
913
- return { rest: parts, version: "" };
914
- };
915
- var detectVersionRef = (ref, versions) => {
916
- const { version, rest } = detectVersion(ref.split("/"), versions);
917
- return { rest: rest.join("/"), version };
918
- };
919
- var versionizeRoute = (logicalRoute, version) => {
920
- if (!version) {
921
- return logicalRoute;
922
- }
923
- return logicalRoute === "/" ? `/${version}` : `/${version}${logicalRoute}`;
924
- };
925
- var versionsDiagnostics = (pages, versions) => {
926
- const configured = new Set(versions.archived.map((version) => version.id));
927
- const seen = new Set;
928
- const diagnostics = [];
929
- for (const page of pages) {
930
- const [first] = page.source.ref.split("/");
931
- if (first && !seen.has(first) && VERSION_LIKE.test(first) && !configured.has(first)) {
932
- seen.add(first);
933
- diagnostics.push({
934
- code: "BLUME_VERSIONS_UNCONFIGURED_VERSION",
935
- message: `Folder "${first}/" looks like a version, but "${first}" is not in versions.archived — its pages are treated as current-version content at /${first}/….`,
936
- severity: "warning",
937
- suggestion: `Add { id: "${first}" } to versions.archived, or rename the folder if it isn't a snapshot.`
938
- });
939
- }
940
- }
941
- return diagnostics;
942
- };
943
-
944
924
  // src/core/graph.ts
945
925
  var collectRoutes = (pages) => {
946
926
  const routes = new Map;
@@ -995,7 +975,26 @@ var resolveTabLabels = (tabs, locale, defaultLocale) => (tabs ?? []).map((tab) =
995
975
  label: resolveLabel(tab.label, locale, defaultLocale)
996
976
  }));
997
977
  var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n, diagnostics, version = "") => {
998
- const localizePath = (path) => path.startsWith("/") && !path.startsWith("//") ? localizeRoute(path, code, i18n) : path;
978
+ const basePath = options.basePath ?? "";
979
+ const real = pages.filter((page) => page.locale === code);
980
+ const localePages = localePagesFor(code, real, fallback, fallbackByKey, i18n, basePath);
981
+ const pageRoutes = [
982
+ ...new Set([...pages, ...localePages].map((page) => page.route))
983
+ ];
984
+ const extraRoutes = options.extraRoutes ?? new Set;
985
+ const serves = (path, section) => {
986
+ if (extraRoutes.has(path)) {
987
+ return true;
988
+ }
989
+ const based = withBasePath(basePath, path);
990
+ const prefix = based === "/" ? "/" : `${based}/`;
991
+ return pageRoutes.some((route) => route === based || section && route.startsWith(prefix));
992
+ };
993
+ const localizeServed = (path, section) => {
994
+ const localized = localizeInternalPath(path, code, i18n);
995
+ return localized === path || serves(localized, section) || !serves(path, section) ? localized : path;
996
+ };
997
+ const localizePath = (path) => localizeServed(path, true);
999
998
  const tabs = resolveTabLabels(options.navigation.tabs, code, i18n.defaultLocale).map((tab) => {
1000
999
  const localized = {
1001
1000
  ...tab,
@@ -1006,24 +1005,23 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
1006
1005
  path: localizePath(tab.path)
1007
1006
  };
1008
1007
  if (localized.href) {
1009
- localized.href = localizePath(localized.href);
1008
+ localized.href = localizeServed(localized.href, false);
1010
1009
  }
1011
1010
  return localized;
1012
1011
  });
1013
- const real = pages.filter((page) => page.locale === code);
1014
- const localePages = localePagesFor(code, real, fallback, fallbackByKey, i18n, options.basePath ?? "");
1015
1012
  const localeDir = i18n.parser === "dir" && code !== i18n.defaultLocale ? code : "";
1016
1013
  const localizeHref = (item) => ({
1017
1014
  ...item,
1018
- href: localizePath(item.href)
1015
+ href: localizeServed(item.href, false)
1019
1016
  });
1020
1017
  const { actions, cta, featured } = options.navigation;
1021
- return buildNavigation(localePages, {
1018
+ const navigation = buildNavigation(localePages, {
1022
1019
  actions: actions?.map(localizeHref),
1023
- basePath: options.basePath ?? "",
1020
+ basePath,
1024
1021
  cta: cta ? localizeHref(cta) : null,
1025
1022
  diagnostics,
1026
1023
  display: options.navigation.sidebar.display,
1024
+ extraRoutes: options.extraRoutes,
1027
1025
  featured: featured?.map(localizeHref),
1028
1026
  folderMeta: options.folderMeta,
1029
1027
  localizedRoot: localizeRoute(versionizeRoute("/", version), code, i18n),
@@ -1035,13 +1033,14 @@ var buildLocaleNavigation = (code, pages, fallback, fallbackByKey, options, i18n
1035
1033
  sidebar: version ? undefined : options.navigation.sidebar.items,
1036
1034
  tabs
1037
1035
  });
1036
+ return options.brandHref === undefined ? navigation : { ...navigation, brandHref: localizeServed(options.brandHref, false) };
1038
1037
  };
1039
1038
  var buildI18nNavigation = (pages, options, i18n, diagnostics, version = "") => {
1040
1039
  const fallback = resolveFallbackLocale(i18n);
1041
1040
  const fallbackByKey = new Map;
1042
1041
  if (fallback) {
1043
1042
  for (const page of pages) {
1044
- if (page.locale === fallback) {
1043
+ if (page.locale === fallback && !page.monolingual) {
1045
1044
  fallbackByKey.set(page.translationKey, page);
1046
1045
  }
1047
1046
  }
@@ -1081,6 +1080,7 @@ var buildVersionNavigation = (id, versionPages, options, diagnostics) => {
1081
1080
  cta: options.navigation.cta,
1082
1081
  diagnostics,
1083
1082
  display: options.navigation.sidebar.display,
1083
+ extraRoutes: options.extraRoutes,
1084
1084
  featured: options.navigation.featured,
1085
1085
  folderMeta: options.folderMeta,
1086
1086
  localizedRoot: versionizeRoute("/", id),
@@ -1107,6 +1107,7 @@ var buildContentGraph = (pages, options) => {
1107
1107
  cta: options.navigation.cta,
1108
1108
  diagnostics,
1109
1109
  display: options.navigation.sidebar.display,
1110
+ extraRoutes: options.extraRoutes,
1110
1111
  featured: options.navigation.featured,
1111
1112
  folderMeta: options.folderMeta,
1112
1113
  selectors: options.navigation.selectors,
@@ -1130,980 +1131,116 @@ var buildContentGraph = (pages, options) => {
1130
1131
  };
1131
1132
  };
1132
1133
 
1133
- // src/core/includes.ts
1134
- import { readFile } from "node:fs/promises";
1135
- import { dirname, extname as extname3, join, relative, resolve } from "pathe";
1136
-
1137
- // src/core/code-fences.ts
1138
- var CODE_FENCE = /^(?<run>`{3,}|~{3,})/u;
1139
- var nextFenceState = (line, fence) => {
1140
- const trimmed = line.trimStart();
1141
- const run = trimmed.match(CODE_FENCE)?.groups?.run;
1142
- if (run === undefined) {
1143
- return fence;
1144
- }
1145
- const rest = trimmed.slice(run.length);
1146
- const delimiter = run.startsWith("`") ? "`" : "~";
1147
- if (fence === null) {
1148
- if (delimiter === "`" && rest.includes("`")) {
1149
- return fence;
1150
- }
1151
- return { delimiter, length: run.length };
1152
- }
1153
- return fence.delimiter === delimiter && run.length >= fence.length && rest.trim() === "" ? null : fence;
1154
- };
1155
-
1156
- // src/core/frontmatter.ts
1157
- import baseMatter from "gray-matter";
1158
- import { dump, load } from "js-yaml";
1159
-
1160
- // src/core/yaml.ts
1161
- import {
1162
- binaryTag,
1163
- CORE_SCHEMA,
1164
- mergeTag,
1165
- omapTag,
1166
- pairsTag,
1167
- setTag,
1168
- timestampTag
1169
- } from "js-yaml";
1170
- var YAML_SCHEMA = CORE_SCHEMA.withTags(timestampTag, mergeTag, binaryTag, omapTag, pairsTag, setTag);
1171
-
1172
- // src/core/frontmatter.ts
1173
- var yamlEngine = {
1174
- parse: (input) => load(input, { schema: YAML_SCHEMA }) ?? {},
1175
- stringify: (data) => dump(data)
1176
- };
1177
- var withYamlEngine = (options) => ({
1178
- ...options,
1179
- engines: {
1180
- yaml: yamlEngine,
1181
- ...options?.engines
1182
- }
1183
- });
1184
- var opensWithThematicBreak = (input) => {
1185
- const [first = "", second] = input.split(/\r?\n/u, 2);
1186
- if (!/^-{3}\s*$/u.test(first)) {
1187
- return false;
1188
- }
1189
- if (second === undefined || second.trim() === "") {
1190
- return true;
1191
- }
1192
- return !input.includes(`
1193
- ---`, 1);
1194
- };
1195
- var passthrough = (input) => {
1196
- const file = {
1197
- content: input,
1198
- data: {},
1199
- excerpt: "",
1200
- isEmpty: false,
1201
- language: "",
1202
- matter: "",
1203
- orig: input,
1204
- stringify: () => input
1205
- };
1206
- return file;
1207
- };
1208
- var isStringInput = (input) => typeof input === "string";
1209
- var matter = Object.assign((input, options) => isStringInput(input) && opensWithThematicBreak(input) ? passthrough(input) : baseMatter(input, withYamlEngine(options)), baseMatter, {
1210
- read: (filepath, options) => baseMatter.read(filepath, withYamlEngine(options)),
1211
- stringify: (file, data, options) => baseMatter.stringify(isStringInput(file) ? { content: file } : file, data, withYamlEngine(options))
1212
- });
1213
- var frontmatter_default = matter;
1214
-
1215
- // src/core/sources/normalize.ts
1216
- import { existsSync, readFileSync } from "node:fs";
1217
- import GithubSlugger from "github-slugger";
1218
- import { extname as extname2 } from "pathe";
1219
-
1220
- // src/core/heading-markers.ts
1221
- var TOC_HIDDEN_KEY = "__blumeTocHidden";
1222
- var occupySlug = (slugger, id) => {
1223
- slugger.occurrences[id] ??= 0;
1224
- };
1225
- var MARKER = /\s*(?:\{#(?<curlyId>[^\s}]+)\}|\[(?:#(?<id>[^\s\]]+)|(?<hide>!)?toc)\])\s*$/u;
1226
- var parseHeadingMarkers = (text, isRefDefined) => {
1227
- let remaining = text;
1228
- let id;
1229
- let toc;
1230
- for (let match = MARKER.exec(remaining);match?.groups; match = MARKER.exec(remaining)) {
1231
- const explicitId = match.groups.curlyId ?? match.groups.id;
1232
- const label = explicitId === undefined ? `${match.groups.hide ?? ""}toc` : `#${explicitId}`;
1233
- if (match.groups.curlyId === undefined && isRefDefined?.(label)) {
1234
- break;
1235
- }
1236
- remaining = remaining.slice(0, match.index);
1237
- if (explicitId === undefined) {
1238
- toc ??= match.groups.hide === undefined ? "only" : "hide";
1239
- } else {
1240
- id ??= explicitId;
1241
- }
1242
- }
1243
- return { id, text: remaining, toc };
1244
- };
1245
-
1246
- // src/core/sources/normalize.ts
1247
- var NUMERIC_PREFIX2 = /^\d+[-_.]/u;
1248
- var GROUP_FOLDER2 = /^\((?<label>.+)\)$/u;
1249
- var WORD_SPLIT2 = /[-_]/u;
1250
- var stripNumericPrefix = (segment) => segment.replace(NUMERIC_PREFIX2, "");
1251
- var groupLabel = (segment) => segment.match(GROUP_FOLDER2)?.groups?.label ?? null;
1252
- var slugify2 = (text) => text.normalize("NFC").toLowerCase().trim().replaceAll(/[^\p{L}\p{M}\p{N}\s_-]/gu, "").replaceAll(/[\s_]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "");
1253
- var slugifyPath = (text) => text.split("/").map(slugify2).filter(Boolean).join("/");
1254
- var titleCase = (value) => value.split(WORD_SPLIT2).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1255
- var sanitizeSegment = (segment) => segment.replaceAll(/[:\p{Cc}]/gu, "");
1256
- var addRouteSegment = (part, segments, groups) => {
1257
- if (part === "") {
1258
- return;
1259
- }
1260
- const group = groupLabel(part);
1261
- if (group !== null) {
1262
- groups.push(group);
1263
- return;
1264
- }
1265
- const clean = stripNumericPrefix(part);
1266
- if (clean === "index") {
1267
- return;
1268
- }
1269
- const safe = sanitizeSegment(clean);
1270
- if (safe === "") {
1271
- return;
1272
- }
1273
- segments.push(safe);
1274
- };
1275
- var mapRoute = (relativePath) => {
1276
- const withoutExt = relativePath.slice(0, relativePath.length - extname2(relativePath).length);
1277
- const rawParts = withoutExt.split("/");
1278
- const segments = [];
1279
- const groups = [];
1280
- for (const part of rawParts) {
1281
- addRouteSegment(part, segments, groups);
1282
- }
1283
- const route = segments.length === 0 ? "/" : `/${segments.join("/")}`;
1284
- return { groups, route, segments };
1285
- };
1286
- var ATX_HEADING = /^ {0,3}(?<hashes>#{1,6})\s+(?<text>.+?)(?:\s+#+)?\s*$/u;
1287
- var SETEXT_UNDERLINE = /^ {0,3}(?<marker>=+|-+)\s*$/u;
1288
- var PARAGRAPH_INTERRUPT = /^ {0,3}(?:[-+*][ \t]|\d{1,9}[.)][ \t]|>)/u;
1289
- var THEMATIC_BREAK = /^ {0,3}(?:(?:-[ \t]*){3,}|(?:\*[ \t]*){3,}|(?:_[ \t]*){3,})$/u;
1290
- var FRONT_MATTER_CLOSE = /^(?:-{3}|\.{3})\s*$/u;
1291
- var PROMPT_OPEN = /^<Prompt(?![\w-])/u;
1292
- var PROMPT_CLOSE = /<\/Prompt>/u;
1293
- var linesWithoutFrontMatter = (body) => {
1294
- const lines = body.split(`
1295
- `);
1296
- if (!/^-{3}\s*$/u.test(lines[0] ?? "")) {
1297
- return { lines, offset: 0 };
1298
- }
1299
- if ((lines[1] ?? "").trim() === "") {
1300
- return { lines, offset: 0 };
1301
- }
1302
- const close = lines.findIndex((line, index) => index > 0 && FRONT_MATTER_CLOSE.test(line));
1303
- return close === -1 ? { lines, offset: 0 } : { lines: lines.slice(close + 1), offset: close + 1 };
1304
- };
1305
- var BARE_CURLY_MARKER = /(?<!\\)\{#(?<id>[^\s}]+)\}(?:\s*\[(?:#[^\s\]]+|!?toc)\])*\s*$/u;
1306
- var HTML_ID = /<[a-z][a-z0-9-]*(?:\s[^<>]*?)?\sid=(?:(?<quote>["'])(?<quoted>[^"'<>]+)\k<quote>|\{(?<jsxQuote>["'])(?<jsx>[^"'<>]+)\k<jsxQuote>\}|(?<bare>[^\s"'=<>`{}]+))/gu;
1307
- var HTML_COMMENT = /<!--[\s\S]*?-->/gu;
1308
- var INLINE_CODE = /`[^`]*`/gu;
1309
- var finishPromptTag = (line, start, state) => {
1310
- const end = line.indexOf(">", start);
1311
- if (end === -1) {
1312
- state.promptTag = true;
1313
- return;
1314
- }
1315
- state.promptTag = false;
1316
- if (line[end - 1] === "/" || line.includes("</Prompt>", end)) {
1317
- return;
1318
- }
1319
- state.promptDepth += 1;
1320
- };
1321
- var ESCAPED_PUNCTUATION = /\\(?<char>[!-/:-@[-`{-~])/gu;
1322
- var REF_DEFINITION = /^(?: {0,3}|[ \t]*(?:(?:>|(?:[-*+]|\d{1,9}[.)])[ \t])[ \t]*)+)\[(?<label>[^\]]+)\]:[ \t]*(?<rest>.*)$/u;
1323
- var refDefinitionLabels = (lines) => {
1324
- const labels = new Set;
1325
- let fence = null;
1326
- for (const [index, line] of lines.entries()) {
1327
- const next = nextFenceState(line, fence);
1328
- if (fence !== null || next !== null) {
1329
- fence = next;
1330
- continue;
1331
- }
1332
- const groups = line.match(REF_DEFINITION)?.groups;
1333
- if (groups?.label === undefined || groups.label.trim() === "") {
1334
- continue;
1335
- }
1336
- const destination = groups.rest ?? "";
1337
- if (destination === "" && (lines[index + 1] ?? "").trim() === "") {
1338
- continue;
1134
+ // src/core/last-modified.ts
1135
+ import { execFileSync } from "node:child_process";
1136
+ import { relative } from "pathe";
1137
+ var GIT_LOCATION_VARS = new Set([
1138
+ "GIT_COMMON_DIR",
1139
+ "GIT_DIR",
1140
+ "GIT_INDEX_FILE",
1141
+ "GIT_OBJECT_DIRECTORY",
1142
+ "GIT_PREFIX",
1143
+ "GIT_WORK_TREE"
1144
+ ]);
1145
+ var gitEnv = () => Object.fromEntries(Object.entries(process.env).filter(([key]) => !GIT_LOCATION_VARS.has(key)));
1146
+ var resolveLastModifiedConfig = (value) => value === false ? { enabled: false, source: "git" } : { enabled: true, source: value };
1147
+ var parseGitLog = (output) => {
1148
+ const times = new Map;
1149
+ let current = null;
1150
+ for (const line of output.split(`
1151
+ `)) {
1152
+ if (line.startsWith("\x00")) {
1153
+ current = line.slice(1);
1154
+ } else if (line && current && !times.has(line)) {
1155
+ times.set(line, current);
1339
1156
  }
1340
- labels.add(groups.label.trim().replaceAll(/\s+/gu, " ").toLowerCase());
1341
- }
1342
- return labels;
1343
- };
1344
- var toHeading = (depth, raw, slugger, isRefDefined) => {
1345
- const unescaped = raw.replaceAll(ESCAPED_PUNCTUATION, "$<char>");
1346
- const markers = parseHeadingMarkers(unescaped, isRefDefined);
1347
- const text = markers.text.trim();
1348
- if (text === "" && (markers.id !== undefined || markers.toc !== undefined)) {
1349
- return {
1350
- heading: { depth, slug: slugger.slug(unescaped), text: unescaped },
1351
- pinned: false
1352
- };
1353
- }
1354
- if (markers.id !== undefined) {
1355
- occupySlug(slugger, markers.id);
1356
- return { heading: { depth, slug: markers.id, text }, pinned: true };
1357
- }
1358
- return { heading: { depth, slug: slugger.slug(text), text }, pinned: false };
1359
- };
1360
- var pushHeading = (headings, state, scanned, line) => {
1361
- headings.push(scanned.heading);
1362
- state.sites.push({ line, pinned: scanned.pinned });
1363
- };
1364
- var noteCurlyMarker = (text, line, state) => {
1365
- const id = text.match(BARE_CURLY_MARKER)?.groups?.id;
1366
- if (id !== undefined) {
1367
- state.curlyMarkers.push({ id, line });
1368
1157
  }
1158
+ return times;
1369
1159
  };
1370
- var scanContentLine = (line, state, slugger, headings, isRefDefined) => {
1371
- state.anchorLines.push(line.replaceAll(INLINE_CODE, ""));
1372
- const atx = line.match(ATX_HEADING);
1373
- if (atx?.groups) {
1374
- const depth = atx.groups.hashes?.length ?? 1;
1375
- const text = (atx.groups.text ?? "").trim();
1376
- pushHeading(headings, state, toHeading(depth, text, slugger, isRefDefined), state.line);
1377
- noteCurlyMarker(text, state.line, state);
1378
- state.paragraph = [];
1379
- return;
1380
- }
1381
- const setext = line.match(SETEXT_UNDERLINE);
1382
- if (setext?.groups && state.paragraph.length > 0) {
1383
- const depth = setext.groups.marker?.startsWith("=") ? 1 : 2;
1384
- const text = state.paragraph.join(" ").trim();
1385
- pushHeading(headings, state, toHeading(depth, text, slugger, isRefDefined), state.line - 1);
1386
- noteCurlyMarker(text, state.paragraphStart, state);
1387
- state.paragraph = [];
1388
- return;
1389
- }
1390
- if (line.trim() === "" || THEMATIC_BREAK.test(line) || PARAGRAPH_INTERRUPT.test(line)) {
1391
- state.paragraph = [];
1392
- return;
1393
- }
1394
- if (state.paragraph.length === 0) {
1395
- state.paragraphStart = state.line;
1160
+ var gitRepositoryRoot = (root) => {
1161
+ try {
1162
+ return execFileSync("git", ["-C", root, "rev-parse", "--show-toplevel"], { encoding: "utf-8", env: gitEnv(), stdio: ["ignore", "pipe", "ignore"] }).trim();
1163
+ } catch {
1164
+ return null;
1396
1165
  }
1397
- state.paragraph.push(line.trim());
1398
1166
  };
1399
- var scanHeadingLine = (line, state, slugger, headings, isRefDefined) => {
1400
- const next = nextFenceState(line, state.fence);
1401
- if (state.fence !== null || next !== null) {
1402
- state.fence = next;
1403
- state.paragraph = [];
1404
- return;
1405
- }
1406
- const trimmed = line.trimStart();
1407
- if (state.promptTag) {
1408
- finishPromptTag(trimmed, 0, state);
1409
- state.paragraph = [];
1410
- return;
1411
- }
1412
- if (PROMPT_OPEN.test(trimmed)) {
1413
- finishPromptTag(trimmed, "<Prompt".length, state);
1414
- state.paragraph = [];
1415
- return;
1416
- }
1417
- if (state.promptDepth > 0) {
1418
- if (PROMPT_CLOSE.test(line)) {
1419
- state.promptDepth -= 1;
1420
- }
1421
- state.paragraph = [];
1422
- return;
1167
+ var gitLastModifiedTimes = (root, contentRoots, sourcePaths, repositoryRoot) => {
1168
+ if (sourcePaths.length === 0 || contentRoots.length === 0) {
1169
+ return new Map;
1423
1170
  }
1424
- scanContentLine(line, state, slugger, headings, isRefDefined);
1425
- };
1426
- var scanBody = (body) => {
1427
- const headings = [];
1428
- const slugger = new GithubSlugger;
1429
- const state = {
1430
- anchorLines: [],
1431
- curlyMarkers: [],
1432
- fence: null,
1433
- line: 0,
1434
- paragraph: [],
1435
- paragraphStart: 0,
1436
- promptDepth: 0,
1437
- promptTag: false,
1438
- sites: []
1439
- };
1440
- const { lines, offset } = linesWithoutFrontMatter(body);
1441
- const definedLabels = refDefinitionLabels(lines);
1442
- const isRefDefined = (label) => definedLabels.has(label.toLowerCase());
1443
- for (const [index, line] of lines.entries()) {
1444
- state.line = index + offset + 1;
1445
- scanHeadingLine(line, state, slugger, headings, isRefDefined);
1446
- }
1447
- const anchors = new Set;
1448
- const markup = state.anchorLines.join(`
1449
- `).replaceAll(HTML_COMMENT, " ");
1450
- for (const match of markup.matchAll(HTML_ID)) {
1451
- const id = match.groups?.quoted ?? match.groups?.jsx ?? match.groups?.bare;
1452
- if (id !== undefined) {
1453
- anchors.add(id);
1454
- }
1171
+ const gitRoot = repositoryRoot === undefined ? gitRepositoryRoot(root) : repositoryRoot;
1172
+ if (gitRoot === null) {
1173
+ return new Map;
1455
1174
  }
1456
- return {
1457
- anchors: [...anchors],
1458
- curlyMarkers: state.curlyMarkers,
1459
- headings,
1460
- sites: state.sites
1461
- };
1462
- };
1463
- var extractHeadings = (body) => scanBody(body).headings;
1464
- var MD_LINK = /\[(?<label>(?:[^[\]]|\[[^\]]*\])*)\]\((?<target>(?:[^()\s]|\([^()\s]*\))+)(?<title>\s+"[^"]*")?\)/gu;
1465
- var MD_IMAGE = /!\[[^\]]*\]\((?<target>(?:[^()\s]|\([^()\s]*\))+)(?<title>\s+"[^"]*")?\)/gu;
1466
- var targetOffsetIn = (matched, target, title) => matched.length - 1 - (title?.length ?? 0) - target.length;
1467
- var scanLinkLine = (line, lineNumber, fence, links) => {
1468
- const next = nextFenceState(line, fence);
1469
- if (fence !== null || next !== null) {
1470
- return next;
1471
- }
1472
- const masked = line.replaceAll(INLINE_CODE, (span) => " ".repeat(span.length));
1473
- for (const match of masked.matchAll(MD_LINK)) {
1474
- const target = match.groups?.target;
1475
- if (target === undefined || match.index === undefined) {
1476
- continue;
1477
- }
1478
- const targetOffset = targetOffsetIn(match[0], target, match.groups?.title);
1479
- const entry = {
1480
- column: match.index + targetOffset + 1,
1481
- line: lineNumber,
1482
- target
1483
- };
1484
- if (masked[match.index - 1] === "!") {
1485
- entry.image = true;
1486
- }
1487
- links.push(entry);
1488
- const label = match[0].slice(0, targetOffset - "](".length);
1489
- for (const image of label.matchAll(MD_IMAGE)) {
1490
- const imageTarget = image.groups?.target;
1491
- if (imageTarget === undefined || image.index === undefined) {
1492
- continue;
1175
+ try {
1176
+ const output = execFileSync("git", [
1177
+ "-C",
1178
+ root,
1179
+ "-c",
1180
+ "core.quotePath=false",
1181
+ "log",
1182
+ "--format=%x00%cI",
1183
+ "--name-only",
1184
+ "--",
1185
+ ...contentRoots
1186
+ ], { encoding: "utf-8", env: gitEnv(), maxBuffer: 256 * 1024 * 1024 });
1187
+ const byRepoPath = parseGitLog(output);
1188
+ const result = new Map;
1189
+ for (const sourcePath of sourcePaths) {
1190
+ const iso = byRepoPath.get(relative(gitRoot, sourcePath));
1191
+ if (iso) {
1192
+ result.set(sourcePath, iso);
1493
1193
  }
1494
- links.push({
1495
- column: match.index + image.index + targetOffsetIn(image[0], imageTarget, image.groups?.title) + 1,
1496
- image: true,
1497
- line: lineNumber,
1498
- target: imageTarget
1499
- });
1500
1194
  }
1195
+ return result;
1196
+ } catch {
1197
+ return new Map;
1501
1198
  }
1502
- return next;
1503
1199
  };
1504
- var extractLinks = (body, lineOffset = 0) => {
1505
- const links = [];
1506
- let fence = null;
1507
- let lineNumber = lineOffset;
1508
- for (const line of body.split(`
1509
- `)) {
1510
- lineNumber += 1;
1511
- fence = scanLinkLine(line, lineNumber, fence, links);
1200
+ var isShallowGitRepository = (root) => {
1201
+ try {
1202
+ return execFileSync("git", ["-C", root, "rev-parse", "--is-shallow-repository"], {
1203
+ encoding: "utf-8",
1204
+ env: gitEnv(),
1205
+ stdio: ["ignore", "pipe", "ignore"]
1206
+ }).trim() === "true";
1207
+ } catch {
1208
+ return false;
1512
1209
  }
1513
- return links;
1514
1210
  };
1515
- var DOUBLE_QUOTED = /"[^"]*"/gu;
1516
- var JSX_OPEN = /<(?<tag>[A-Z][A-Za-z0-9]*)/gu;
1517
- var scanTagLine = (line, fence, tags) => {
1518
- const next = nextFenceState(line, fence);
1519
- if (fence !== null || next !== null) {
1520
- return next;
1211
+ var lastModifiedShallowWarning = (root, undatedCount) => {
1212
+ if (undatedCount === 0 || !isShallowGitRepository(root)) {
1213
+ return [];
1521
1214
  }
1522
- const clean = line.replaceAll(INLINE_CODE, "").replaceAll(DOUBLE_QUOTED, "");
1523
- for (const match of clean.matchAll(JSX_OPEN)) {
1524
- const tag = match.groups?.tag;
1525
- if (tag) {
1526
- tags.add(tag);
1215
+ return [
1216
+ {
1217
+ code: "BLUME_SHALLOW_GIT_HISTORY",
1218
+ message: `lastModified is on, but this build runs in a shallow git clone, so ${undatedCount} page(s) have no git-derived date — their sitemap <lastmod> and "Last updated" stamps are omitted.`,
1219
+ severity: "warning",
1220
+ suggestion: "Fetch full history in CI: set the VERCEL_DEEP_CLONE=true environment variable on Vercel, or fetch-depth: 0 for actions/checkout."
1527
1221
  }
1528
- }
1529
- return next;
1222
+ ];
1530
1223
  };
1531
- var extractComponentTags = (body) => {
1532
- const tags = new Set;
1533
- let fence = null;
1534
- for (const line of body.split(`
1535
- `)) {
1536
- fence = scanTagLine(line, fence, tags);
1224
+
1225
+ // src/core/meta.ts
1226
+ import { basename, dirname, relative as relative2 } from "pathe";
1227
+ import { glob } from "tinyglobby";
1228
+ var META_FILES = [
1229
+ "**/meta.ts",
1230
+ "**/meta.js",
1231
+ "**/meta.mjs",
1232
+ "**/meta.$.ts",
1233
+ "**/meta.$.js",
1234
+ "**/meta.$.mjs"
1235
+ ];
1236
+ var isMetaFactory = (mod) => typeof mod === "function";
1237
+ var resolveMeta = async (mod) => isMetaFactory(mod) ? await mod() : mod;
1238
+ var metaKeyFor = (prefix, dir) => {
1239
+ const clean = prefix ? trimChar(prefix, "/") : "";
1240
+ if (!clean) {
1241
+ return dir;
1537
1242
  }
1538
- return [...tags];
1539
- };
1540
- var strippedLineOffset = (raw, body) => raw ? Math.max(0, raw.split(`
1541
- `).length - body.split(`
1542
- `).length) : 0;
1543
- var remapExpandedLinks = (links, origins, sourcePath) => links.map((link) => {
1544
- const origin = origins[link.line - 1];
1545
- if (!origin) {
1546
- return link;
1547
- }
1548
- const remapped = { ...link, line: origin.line };
1549
- if (origin.file !== sourcePath) {
1550
- remapped.file = origin.file;
1551
- }
1552
- return remapped;
1553
- });
1554
- var entryIncludes = (entry) => entry.expanded && entry.expanded.includes.length > 0 ? entry.expanded.includes : undefined;
1555
- var entryLinks = (entry) => entry.expanded ? remapExpandedLinks(extractLinks(entry.expanded.text), entry.expanded.origins, entry.sourcePath) : extractLinks(entry.body.text, strippedLineOffset(entry.raw, entry.body.text));
1556
- var curlyMarkerDiagnostics = (entry, markers, sourceName) => markers.map(({ id, line }) => {
1557
- const origin = entry.expanded?.origins[line - 1];
1558
- const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
1559
- const inPartial = origin !== undefined && origin.file !== entry.sourcePath;
1560
- return {
1561
- code: "BLUME_MDX_CURLY_ANCHOR",
1562
- file: origin?.file ?? page,
1563
- line: origin?.line ?? line + strippedLineOffset(entry.raw, entry.body.text),
1564
- message: inPartial ? `\`{#${id}}\` is a JSX expression once this partial is included in ${page} (.mdx), so that page fails to compile.` : `\`{#${id}}\` is a JSX expression in .mdx, so this page fails to compile.`,
1565
- severity: "error",
1566
- suggestion: `Write \`[#${id}]\` or escape it as \`\\{#${id}\\}\` — both pin the same anchor in .md and .mdx.`
1567
- };
1568
- });
1569
- var deriveTitle = (meta, headings, id) => {
1570
- if (meta.title) {
1571
- return meta.title;
1572
- }
1573
- const firstHeading = headings.find((h) => h.depth === 1) ?? headings[0];
1574
- if (firstHeading) {
1575
- return firstHeading.text;
1576
- }
1577
- const base = id.split("/").pop() ?? id;
1578
- return titleCase(stripNumericPrefix(base.replace(extname2(base), "")));
1579
- };
1580
- var trimSlashes = (value) => trimChar(value, "/");
1581
- var isStringValue = (value) => typeof value === "string";
1582
- var withPrefix = (prefix, path) => {
1583
- const clean = prefix ? trimSlashes(prefix) : "";
1584
- return clean ? `${clean}/${path}` : path;
1585
- };
1586
- var placeEntryRef = (ref, ext, ctx) => {
1587
- const { version, rest } = ctx.versions ? detectVersionRef(ref, ctx.versions) : { rest: ref, version: "" };
1588
- const { navPath, locales } = ctx.i18n ? localePlacement(rest, ext, ctx.i18n) : { locales: [""], navPath: rest };
1589
- return { locales, navPath, version };
1590
- };
1591
- var resolveEntryRoute = (entry, ext, frontmatterSlug, ctx) => {
1592
- const { locales, navPath, version } = placeEntryRef(entry.ref, ext, ctx);
1593
- const slugInput = frontmatterSlug ?? entry.slug;
1594
- const slug = slugInput ? trimSlashes(slugInput) : "";
1595
- const routeInput = withPrefix(ctx.prefix, slug ? `${slug}${ext}` : navPath);
1596
- const { segments, groups, route: versionKey } = mapRoute(routeInput);
1597
- return {
1598
- groups,
1599
- locales,
1600
- logicalRoute: versionizeRoute(versionKey, version),
1601
- navPath: withPrefix(ctx.prefix, navPath),
1602
- segments,
1603
- version,
1604
- versionKey
1605
- };
1606
- };
1607
- var localizedRoute = (logicalRoute, locale, i18n) => i18n ? localizeRoute(logicalRoute, locale, i18n) : logicalRoute;
1608
- var isKeyCarrier = (segment) => typeof segment === "object" && segment !== null;
1609
- var isSymbolKey = (key) => typeof key === "symbol";
1610
- var segmentKey2 = (segment) => {
1611
- const key = isKeyCarrier(segment) ? segment.key : segment;
1612
- return isSymbolKey(key) ? String(key) : key;
1613
- };
1614
- var validateCustomKeys = (data, extend) => {
1615
- const custom = {};
1616
- const issues = [];
1617
- for (const [key, schema] of Object.entries(extend)) {
1618
- const outcome = schema["~standard"].validate(data[key]);
1619
- if (outcome instanceof Promise) {
1620
- issues.push({
1621
- message: "Async schemas are not supported for custom frontmatter keys.",
1622
- path: [key]
1623
- });
1624
- continue;
1625
- }
1626
- if (outcome.issues !== undefined) {
1627
- issues.push(...outcome.issues.map((issue) => ({
1628
- message: issue.message,
1629
- path: [key, ...(issue.path ?? []).map(segmentKey2)]
1630
- })));
1631
- continue;
1632
- }
1633
- if (outcome.value !== undefined || Object.hasOwn(data, key)) {
1634
- custom[key] = outcome.value;
1635
- }
1636
- }
1637
- return {
1638
- custom: Object.keys(custom).length > 0 ? custom : undefined,
1639
- issues
1640
- };
1641
- };
1642
- var parseEntryMeta = (entry, ctx) => {
1643
- const entryType = isStringValue(entry.data.type) ? entry.data.type : ctx.defaultType;
1644
- const typeExtend = ctx.typeFrontmatter?.[entryType];
1645
- const extend = ctx.frontmatterExtend || typeExtend ? { ...ctx.frontmatterExtend, ...typeExtend } : undefined;
1646
- const known = extend ? Object.fromEntries(Object.entries(entry.data).filter(([key]) => !Object.hasOwn(extend, key))) : entry.data;
1647
- const result = pageMetaSchema.safeParse(known);
1648
- const customResult = extend ? validateCustomKeys(entry.data, extend) : null;
1649
- if (result.success && (customResult?.issues.length ?? 0) === 0) {
1650
- return { custom: customResult?.custom, meta: result.data };
1651
- }
1652
- const source = entry.raw ?? (entry.sourcePath && existsSync(entry.sourcePath) ? readFileSync(entry.sourcePath, "utf-8") : undefined);
1653
- const location = {
1654
- code: "BLUME_FRONTMATTER_INVALID",
1655
- file: entry.sourcePath ?? `${ctx.source.name}:${entry.ref}`,
1656
- source
1657
- };
1658
- return {
1659
- diagnostics: [
1660
- ...result.success ? [] : diagnosticsFromZod(result.error, location),
1661
- ...customResult ? diagnosticsFromIssues(customResult.issues, location) : []
1662
- ]
1663
- };
1664
- };
1665
- var normalizeEntry = (entry, ctx) => {
1666
- const { format } = entry.body;
1667
- const ext = format === "mdx" ? ".mdx" : ".md";
1668
- const parsed = parseEntryMeta(entry, ctx);
1669
- if (parsed.diagnostics) {
1670
- return { diagnostics: parsed.diagnostics, pages: [] };
1671
- }
1672
- const { meta } = parsed;
1673
- if (meta.hidden) {
1674
- meta.sidebar.hidden = true;
1675
- }
1676
- if (meta.noindex) {
1677
- meta.seo.noindex = true;
1678
- }
1679
- const {
1680
- groups,
1681
- locales,
1682
- logicalRoute,
1683
- navPath,
1684
- segments,
1685
- version,
1686
- versionKey
1687
- } = resolveEntryRoute(entry, ext, meta.slug, {
1688
- i18n: ctx.i18n,
1689
- prefix: ctx.source.prefix,
1690
- versions: ctx.versions
1691
- });
1692
- const bodyText = entry.expanded?.text ?? entry.body.text;
1693
- const { anchors, curlyMarkers, headings } = scanBody(bodyText);
1694
- const { staged } = ctx.source;
1695
- const base = {
1696
- anchors,
1697
- body: staged ? { format, text: entry.raw ?? entry.body.text } : undefined,
1698
- collection: staged ? "staged" : undefined,
1699
- componentsUsed: format === "mdx" ? extractComponentTags(bodyText) : undefined,
1700
- contentType: meta.type ?? ctx.defaultType,
1701
- custom: parsed.custom,
1702
- description: meta.description,
1703
- editUrl: entry.editUrl,
1704
- entryId: staged ? `${ctx.source.name}/${entry.ref}` : undefined,
1705
- format,
1706
- groups,
1707
- headings,
1708
- id: `${ctx.source.name}:${entry.ref}`,
1709
- includes: entryIncludes(entry),
1710
- lastModified: meta.lastModified ?? entry.lastModified,
1711
- links: entryLinks(entry),
1712
- meta,
1713
- navPath,
1714
- segments,
1715
- source: { name: ctx.source.name, ref: entry.ref },
1716
- sourcePath: entry.sourcePath,
1717
- title: deriveTitle(meta, headings, navPath),
1718
- translationKey: logicalRoute,
1719
- version,
1720
- versionKey
1721
- };
1722
- const pages = locales.map((locale) => ({
1723
- ...base,
1724
- locale,
1725
- route: withBasePath(ctx.basePath ?? "", localizedRoute(logicalRoute, locale, ctx.i18n))
1726
- }));
1727
- return {
1728
- diagnostics: format === "mdx" ? curlyMarkerDiagnostics(entry, curlyMarkers, ctx.source.name) : [],
1729
- pages
1730
- };
1731
- };
1732
-
1733
- // src/core/includes.ts
1734
- var CONTENT_EXTENSIONS = new Set([".md", ".mdx"]);
1735
- var INCLUDE_STATEMENT = /^<include(?<attrs>(?:\s+[a-z][\w-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*)\s*>\s*(?<target>[^<>\s](?:[^<>]*[^<>\s])?)\s*<\/include\s*>$/u;
1736
- var INCLUDE_ATTRIBUTE = /(?<name>[a-z][\w-]*)(?:\s*=\s*(?:"(?<double>[^"]*)"|'(?<single>[^']*)'))?/gu;
1737
- var hasIncludeStatements = (text) => text.includes("<include");
1738
- var INDENTED_CODE = /^(?: {4}|\t)/u;
1739
- var SETEXT_UNDERLINE2 = /^ {0,3}(?:=+|-+)\s*$/u;
1740
- var COMMENT_DELIMITERS = {
1741
- md: { close: "-->", open: "<!--" },
1742
- mdx: { close: "*/}", open: "{/*" }
1743
- };
1744
- var advanceCommentState = (line, delimiters, inComment) => {
1745
- let inside = inComment;
1746
- let cursor = 0;
1747
- for (;; ) {
1748
- const marker = inside ? delimiters.close : delimiters.open;
1749
- const index = line.indexOf(marker, cursor);
1750
- if (index === -1) {
1751
- return inside;
1752
- }
1753
- inside = !inside;
1754
- cursor = index + marker.length;
1755
- }
1756
- };
1757
- var parseIncludeStatement = (line) => {
1758
- const match = INCLUDE_STATEMENT.exec(line);
1759
- if (!match?.groups) {
1760
- return null;
1761
- }
1762
- const attributes = {};
1763
- for (const attr of (match.groups.attrs ?? "").matchAll(INCLUDE_ATTRIBUTE)) {
1764
- const name = attr.groups?.name;
1765
- const value = attr.groups?.double ?? attr.groups?.single;
1766
- if (name === "lang" && value) {
1767
- attributes.lang = value;
1768
- }
1769
- if (name === "meta" && value) {
1770
- attributes.meta = value;
1771
- }
1772
- }
1773
- return { attributes, target: match.groups.target ?? "" };
1774
- };
1775
- var parseIncludeLine = (line) => INDENTED_CODE.test(line) ? null : parseIncludeStatement(line.trim());
1776
- var DOCS_SUGGESTION = "Include paths resolve relative to the including file; paths starting with / resolve from the content root.";
1777
- var includeError = (code, message, file, line, suggestion) => ({ code, file, line, message, severity: "error", suggestion });
1778
- var escapesRoot = (root, path) => {
1779
- const rel = relative(root, path);
1780
- return rel === ".." || rel.startsWith("../");
1781
- };
1782
- var resolveIncludePath = (target, filePath, ctx, line) => {
1783
- if (target.startsWith("/")) {
1784
- if (!ctx.contentRoot) {
1785
- return {
1786
- error: includeError("BLUME_INCLUDE_OUTSIDE_ROOT", `Include target ${target} is root-relative, but no content root is configured here.`, filePath, line, "Use a path relative to the including file instead.")
1787
- };
1788
- }
1789
- const path = join(ctx.contentRoot, target);
1790
- if (escapesRoot(ctx.contentRoot, path)) {
1791
- return {
1792
- error: includeError("BLUME_INCLUDE_OUTSIDE_ROOT", `Include target ${target} resolves outside the content root.`, filePath, line, "Root-relative include paths resolve from the content root and cannot climb above it.")
1793
- };
1794
- }
1795
- return { path };
1796
- }
1797
- const path = resolve(dirname(filePath), target);
1798
- if (ctx.contentRoot && escapesRoot(ctx.contentRoot, path)) {
1799
- return {
1800
- error: includeError("BLUME_INCLUDE_OUTSIDE_ROOT", `Include target ${target} resolves outside the content root.`, filePath, line, "Move the included file under the content root so builds, version snapshots, and ejects all see it.")
1801
- };
1802
- }
1803
- return { path };
1804
- };
1805
- var rebaseImageLine = (line, fromDir, toDir) => {
1806
- const masked = line.replaceAll(INLINE_CODE, (span) => " ".repeat(span.length));
1807
- let out = "";
1808
- let cursor = 0;
1809
- for (const match of masked.matchAll(MD_IMAGE)) {
1810
- const target = match.groups?.target ?? "";
1811
- if (target.startsWith("/") || target.startsWith("#") || URL.canParse(target)) {
1812
- continue;
1813
- }
1814
- const rebased = relative(toDir, resolve(fromDir, target));
1815
- const url = rebased.startsWith(".") ? rebased : `./${rebased}`;
1816
- const offset = (match.index ?? 0) + targetOffsetIn(match[0], target, match.groups?.title);
1817
- out += line.slice(cursor, offset) + url;
1818
- cursor = offset + target.length;
1819
- }
1820
- return out + line.slice(cursor);
1821
- };
1822
- var rebaseImages = (lines, fromDir, toDir) => {
1823
- if (fromDir === toDir) {
1824
- return lines;
1825
- }
1826
- let fence = null;
1827
- return lines.map((line) => {
1828
- const next = nextFenceState(line, fence);
1829
- const inFence = fence !== null || next !== null;
1830
- fence = next;
1831
- return inFence ? line : rebaseImageLine(line, fromDir, toDir);
1832
- });
1833
- };
1834
- var codeBlockLines = (content, path, attributes) => {
1835
- const body = content.replace(/\n$/u, "");
1836
- let longestRun = 0;
1837
- for (const run of body.match(/`+/gu) ?? []) {
1838
- longestRun = Math.max(longestRun, run.length);
1839
- }
1840
- const fence = "`".repeat(Math.max(3, longestRun + 1));
1841
- const ext = extname3(path);
1842
- const lang = attributes.lang ?? (ext ? ext.slice(1) : "text");
1843
- const meta = attributes.meta ? ` ${attributes.meta}` : "";
1844
- return [`${fence}${lang}${meta}`, ...body.split(`
1845
- `), fence];
1846
- };
1847
- var expandStatement = async (statement, filePath, line, ctx, stack) => {
1848
- const resolved = resolveIncludePath(statement.target, filePath, ctx, line);
1849
- if ("error" in resolved) {
1850
- return resolved;
1851
- }
1852
- const { path } = resolved;
1853
- if (stack.includes(path)) {
1854
- return {
1855
- error: includeError("BLUME_INCLUDE_CYCLE", `Circular include: ${[...stack, path].map((entry) => relative(ctx.contentRoot ?? dirname(path), entry)).join(" -> ")}.`, filePath, line, "Remove the include statement that closes the loop.")
1856
- };
1857
- }
1858
- let content;
1859
- try {
1860
- content = await readFile(path, "utf-8");
1861
- } catch (error) {
1862
- return {
1863
- error: includeError("BLUME_INCLUDE_NOT_FOUND", `Include target ${statement.target} was not found (looked at ${path}): ${error instanceof Error ? error.message : String(error)}.`, filePath, line, DOCS_SUGGESTION)
1864
- };
1865
- }
1866
- ctx.includes.add(path);
1867
- const asContent = !statement.attributes.lang && CONTENT_EXTENSIONS.has(extname3(path).toLowerCase());
1868
- if (!asContent) {
1869
- const lines = codeBlockLines(content, path, statement.attributes);
1870
- return {
1871
- lines,
1872
- origins: lines.map((_, i) => ({ file: path, line: Math.max(1, i) }))
1873
- };
1874
- }
1875
- const parsed = frontmatter_default(content);
1876
- const body = parsed.content;
1877
- const strippedOffset = Math.max(0, content.split(`
1878
- `).length - body.split(`
1879
- `).length);
1880
- const expanded = await expandLines(body, path, strippedOffset, ctx, [
1881
- ...stack,
1882
- path
1883
- ]);
1884
- const lines = [...expanded.lines];
1885
- const origins = [...expanded.origins];
1886
- while (lines.at(-1)?.trim() === "") {
1887
- lines.pop();
1888
- origins.pop();
1889
- }
1890
- while (lines[0]?.trim() === "") {
1891
- lines.shift();
1892
- origins.shift();
1893
- }
1894
- return {
1895
- lines: rebaseImages(lines, dirname(path), dirname(filePath)),
1896
- origins
1897
- };
1898
- };
1899
- var expandLines = async (body, filePath, lineOffset, ctx, stack) => {
1900
- const sourceLines = body.split(`
1901
- `);
1902
- const isMdx = extname3(filePath).toLowerCase() === ".mdx";
1903
- const delimiters = COMMENT_DELIMITERS[isMdx ? "mdx" : "md"];
1904
- let fence = null;
1905
- let inComment = false;
1906
- const statements = sourceLines.map((line, index) => {
1907
- if (inComment) {
1908
- inComment = advanceCommentState(line, delimiters, true);
1909
- return null;
1910
- }
1911
- const next = nextFenceState(line, fence);
1912
- const inFence = fence !== null || next !== null;
1913
- fence = next;
1914
- if (inFence) {
1915
- return null;
1916
- }
1917
- inComment = advanceCommentState(line, delimiters, false);
1918
- if (inComment || !hasIncludeStatements(line)) {
1919
- return null;
1920
- }
1921
- const statement = isMdx ? parseIncludeStatement(line.trim()) : parseIncludeLine(line);
1922
- if (statement && !isMdx && SETEXT_UNDERLINE2.test(sourceLines[index + 1] ?? "")) {
1923
- return null;
1924
- }
1925
- return statement;
1926
- });
1927
- const expansions = await Promise.all(statements.map((statement, index) => statement ? expandStatement(statement, filePath, lineOffset + index + 1, ctx, stack) : null));
1928
- const lines = [];
1929
- const origins = [];
1930
- for (const [index, line] of sourceLines.entries()) {
1931
- const rawLine = lineOffset + index + 1;
1932
- const expanded = expansions[index];
1933
- if (!expanded || "error" in expanded) {
1934
- if (expanded) {
1935
- ctx.errors.push(expanded.error);
1936
- }
1937
- lines.push(line);
1938
- origins.push({ file: filePath, line: rawLine });
1939
- continue;
1940
- }
1941
- const pad = () => {
1942
- lines.push("");
1943
- origins.push({ file: filePath, line: rawLine });
1944
- };
1945
- if (lines.at(-1)?.trim()) {
1946
- pad();
1947
- }
1948
- lines.push(...expanded.lines);
1949
- origins.push(...expanded.origins);
1950
- if (sourceLines[index + 1]?.trim()) {
1951
- pad();
1952
- }
1953
- }
1954
- return { lines, origins };
1955
- };
1956
- var expandIncludes = async (body, options) => {
1957
- const ctx = {
1958
- contentRoot: options.contentRoot,
1959
- errors: [],
1960
- includes: new Set
1961
- };
1962
- const { lines, origins } = await expandLines(body, options.sourcePath, options.lineOffset ?? 0, ctx, [options.sourcePath]);
1963
- return {
1964
- errors: ctx.errors,
1965
- includes: [...ctx.includes],
1966
- origins,
1967
- text: lines.join(`
1968
- `)
1969
- };
1970
- };
1971
- var buildIncludeGraph = (pages) => {
1972
- const graph = {};
1973
- for (const page of pages) {
1974
- const { sourcePath } = page;
1975
- if (!sourcePath) {
1976
- continue;
1977
- }
1978
- for (const partial of page.includes ?? []) {
1979
- const includers = graph[partial] ?? [];
1980
- graph[partial] = includers;
1981
- if (!includers.includes(sourcePath)) {
1982
- includers.push(sourcePath);
1983
- }
1984
- }
1985
- }
1986
- return graph;
1987
- };
1988
-
1989
- // src/core/last-modified.ts
1990
- import { execFileSync } from "node:child_process";
1991
- import { relative as relative2 } from "pathe";
1992
- var GIT_LOCATION_VARS = new Set([
1993
- "GIT_COMMON_DIR",
1994
- "GIT_DIR",
1995
- "GIT_INDEX_FILE",
1996
- "GIT_OBJECT_DIRECTORY",
1997
- "GIT_PREFIX",
1998
- "GIT_WORK_TREE"
1999
- ]);
2000
- var gitEnv = () => Object.fromEntries(Object.entries(process.env).filter(([key]) => !GIT_LOCATION_VARS.has(key)));
2001
- var resolveLastModifiedConfig = (value) => {
2002
- if (value === false) {
2003
- return { enabled: false, source: "git" };
2004
- }
2005
- if (value === true) {
2006
- return { enabled: true, source: "git" };
2007
- }
2008
- return { enabled: true, source: value.type };
2009
- };
2010
- var parseGitLog = (output) => {
2011
- const times = new Map;
2012
- let current = null;
2013
- for (const line of output.split(`
2014
- `)) {
2015
- if (line.startsWith("\x00")) {
2016
- current = line.slice(1);
2017
- } else if (line && current && !times.has(line)) {
2018
- times.set(line, current);
2019
- }
2020
- }
2021
- return times;
2022
- };
2023
- var gitRepositoryRoot = (root) => {
2024
- try {
2025
- return execFileSync("git", ["-C", root, "rev-parse", "--show-toplevel"], { encoding: "utf-8", env: gitEnv(), stdio: ["ignore", "pipe", "ignore"] }).trim();
2026
- } catch {
2027
- return null;
2028
- }
2029
- };
2030
- var gitLastModifiedTimes = (root, contentRoots, sourcePaths, repositoryRoot) => {
2031
- if (sourcePaths.length === 0 || contentRoots.length === 0) {
2032
- return new Map;
2033
- }
2034
- const gitRoot = repositoryRoot === undefined ? gitRepositoryRoot(root) : repositoryRoot;
2035
- if (gitRoot === null) {
2036
- return new Map;
2037
- }
2038
- try {
2039
- const output = execFileSync("git", [
2040
- "-C",
2041
- root,
2042
- "-c",
2043
- "core.quotePath=false",
2044
- "log",
2045
- "--format=%x00%cI",
2046
- "--name-only",
2047
- "--",
2048
- ...contentRoots
2049
- ], { encoding: "utf-8", env: gitEnv(), maxBuffer: 256 * 1024 * 1024 });
2050
- const byRepoPath = parseGitLog(output);
2051
- const result = new Map;
2052
- for (const sourcePath of sourcePaths) {
2053
- const iso = byRepoPath.get(relative2(gitRoot, sourcePath));
2054
- if (iso) {
2055
- result.set(sourcePath, iso);
2056
- }
2057
- }
2058
- return result;
2059
- } catch {
2060
- return new Map;
2061
- }
2062
- };
2063
- var isShallowGitRepository = (root) => {
2064
- try {
2065
- return execFileSync("git", ["-C", root, "rev-parse", "--is-shallow-repository"], {
2066
- encoding: "utf-8",
2067
- env: gitEnv(),
2068
- stdio: ["ignore", "pipe", "ignore"]
2069
- }).trim() === "true";
2070
- } catch {
2071
- return false;
2072
- }
2073
- };
2074
- var lastModifiedShallowWarning = (root, undatedCount) => {
2075
- if (undatedCount === 0 || !isShallowGitRepository(root)) {
2076
- return [];
2077
- }
2078
- return [
2079
- {
2080
- code: "BLUME_SHALLOW_GIT_HISTORY",
2081
- message: `lastModified is on, but this build runs in a shallow git clone, so ${undatedCount} page(s) have no git-derived date — their sitemap <lastmod> and "Last updated" stamps are omitted.`,
2082
- severity: "warning",
2083
- suggestion: "Fetch full history in CI: set the VERCEL_DEEP_CLONE=true environment variable on Vercel, or fetch-depth: 0 for actions/checkout."
2084
- }
2085
- ];
2086
- };
2087
-
2088
- // src/core/meta.ts
2089
- import { basename, dirname as dirname2, relative as relative3 } from "pathe";
2090
- import { glob } from "tinyglobby";
2091
- var META_FILES = [
2092
- "**/meta.ts",
2093
- "**/meta.js",
2094
- "**/meta.mjs",
2095
- "**/meta.$.ts",
2096
- "**/meta.$.js",
2097
- "**/meta.$.mjs"
2098
- ];
2099
- var isMetaFactory = (mod) => typeof mod === "function";
2100
- var resolveMeta = async (mod) => isMetaFactory(mod) ? await mod() : mod;
2101
- var metaKeyFor = (prefix, dir) => {
2102
- const clean = prefix ? trimChar(prefix, "/") : "";
2103
- if (!clean) {
2104
- return dir;
2105
- }
2106
- return dir ? `${clean}/${dir}` : clean;
1243
+ return dir ? `${clean}/${dir}` : clean;
2107
1244
  };
2108
1245
  var discoverFolderMeta = async (sources, options = {}) => {
2109
1246
  const list = Array.isArray(sources) ? sources : [{ root: sources }];
@@ -2135,7 +1272,7 @@ var discoverFolderMeta = async (sources, options = {}) => {
2135
1272
  }));
2136
1273
  for (const { loaded, source } of perSource) {
2137
1274
  for (const entry of loaded) {
2138
- const dir = relative3(source.root, dirname2(entry.file));
1275
+ const dir = relative2(source.root, dirname(entry.file));
2139
1276
  const [head, ...tail] = dir.split("/");
2140
1277
  const version = head && versionDirs.has(head) ? head : "";
2141
1278
  const afterVersion = version ? tail : [head ?? "", ...tail];
@@ -2168,1287 +1305,345 @@ var discoverFolderMeta = async (sources, options = {}) => {
2168
1305
  };
2169
1306
 
2170
1307
  // src/core/sources/resolve.ts
2171
- import { isAbsolute as isAbsolute4, join as join9, resolve as resolve4 } from "pathe";
1308
+ import { join as join7 } from "pathe";
2172
1309
 
2173
- // src/core/sources/cache.ts
2174
- import { createHash } from "node:crypto";
2175
- import { mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
2176
- import { join as join2 } from "pathe";
2177
- var hashText = (text) => createHash("sha256").update(text).digest("hex").slice(0, 16);
2178
- var entriesDigest = (entries) => hashText(entries.map((entry) => `${entry.ref}:${entry.hash ?? hashText(entry.body.text)}`).join("|"));
2179
- var pollingWatch = (load, intervalSeconds, seed) => (onChange) => {
2180
- let last = "";
2181
- if (seed) {
2182
- const seedBaseline = async () => {
2183
- try {
2184
- const { entries } = await seed();
2185
- last ||= entriesDigest(entries);
2186
- } catch {}
2187
- };
2188
- seedBaseline();
2189
- }
2190
- const tick = async () => {
2191
- try {
2192
- const { entries } = await load();
2193
- const next = entriesDigest(entries);
2194
- if (last && next !== last) {
2195
- onChange();
2196
- }
2197
- last = next;
2198
- } catch {}
2199
- };
2200
- const timer = setInterval(() => {
2201
- tick();
2202
- }, intervalSeconds * 1000);
2203
- return () => clearInterval(timer);
2204
- };
2205
- var snapshotCache = (cacheDir) => {
2206
- const file = join2(cacheDir, "entries.json");
2207
- return {
2208
- read: async () => {
2209
- try {
2210
- return JSON.parse(await readFile2(file, "utf-8"));
2211
- } catch {
2212
- return [];
2213
- }
2214
- },
2215
- write: async (entries) => {
2216
- try {
2217
- await mkdir(cacheDir, { recursive: true });
2218
- await writeFile(file, `${JSON.stringify(entries)}
2219
- `, "utf-8");
2220
- } catch {}
2221
- }
2222
- };
2223
- };
2224
- var loadWithCache = async (name, cache, fetchEntries, refresh = true) => {
2225
- if (!refresh) {
2226
- const cached = await cache.read();
2227
- if (cached.length > 0) {
2228
- return { diagnostics: [], entries: cached };
2229
- }
2230
- }
2231
- try {
2232
- const entries = await fetchEntries();
2233
- await cache.write(entries);
2234
- return { diagnostics: [], entries };
2235
- } catch (error) {
2236
- const fallback = await cache.read();
2237
- if (fallback.length > 0) {
2238
- const diagnostic = {
2239
- code: "BLUME_SOURCE_OFFLINE",
2240
- message: `Source "${name}" could not be fetched (${error.message}); served ${fallback.length} cached entries.`,
2241
- severity: "warning"
2242
- };
2243
- return { diagnostics: [diagnostic], entries: fallback };
1310
+ // src/core/sources/json.ts
1311
+ var isJsonObject = (value) => typeof value === "object" && value !== null;
1312
+ var getPath = (doc, path) => {
1313
+ let current = doc;
1314
+ for (const key of path.split(".")) {
1315
+ if (isJsonObject(current)) {
1316
+ current = current[key];
1317
+ } else {
1318
+ return;
2244
1319
  }
2245
- throw new BlumeError({
2246
- code: "BLUME_SOURCE_FETCH_FAILED",
2247
- message: `Source "${name}" failed to load and no cache is available: ${error.message}`,
2248
- severity: "error"
2249
- });
2250
1320
  }
1321
+ return current;
2251
1322
  };
1323
+ var isStringValue2 = (value) => typeof value === "string";
1324
+ var asString = (value) => isStringValue2(value) ? value : undefined;
1325
+ var asArray = (value) => Array.isArray(value) ? value : [];
1326
+ var asObject = (value) => isJsonObject(value) && !Array.isArray(value) ? value : undefined;
1327
+ var isNumberValue = (value) => typeof value === "number";
1328
+ var asNumber = (value) => isNumberValue(value) ? value : undefined;
1329
+ var objectsIn = (value) => asArray(value).flatMap((item) => {
1330
+ const object = asObject(item);
1331
+ return object ? [object] : [];
1332
+ });
2252
1333
 
2253
- // src/openapi/model.ts
2254
- var HTTP_METHODS = [
2255
- "get",
2256
- "put",
2257
- "post",
2258
- "delete",
2259
- "options",
2260
- "head",
2261
- "patch",
2262
- "trace"
2263
- ];
2264
- var UNTAGGED = "Operations";
2265
- var CASE_BOUNDARY = /(?<before>[\p{Ll}\p{N}])(?<capital>\p{Lu})/gu;
2266
- var ACRONYM_BOUNDARY = /(?<acronym>\p{Lu})(?<word>\p{Lu}\p{Ll})/gu;
2267
- var splitIdentifier = (identifier) => identifier.replace(CASE_BOUNDARY, "$<before>-$<capital>").replace(ACRONYM_BOUNDARY, "$<acronym>-$<word>");
2268
- var operationKey = (method, path, operationId) => {
2269
- const fromId = operationId ? slugify(splitIdentifier(operationId)) : "";
2270
- return fromId || slugify(`${method}-${path}`);
2271
- };
2272
- var specOf = (specs, source) => Object.hasOwn(specs, source) ? specs[source] : undefined;
2273
- var operationOf = (spec, id) => Object.hasOwn(spec.operations, id) ? spec.operations[id] : undefined;
2274
- var specAddresses = (spec) => {
2275
- const addresses = [];
2276
- if (spec.kind === "graphql") {
2277
- if (spec.endpoint) {
2278
- addresses.push(spec.endpoint);
2279
- }
2280
- return { addresses, label: "Endpoint" };
2281
- }
2282
- if (spec.kind === "asyncapi") {
2283
- const servers = spec.document.servers ?? {};
2284
- for (const server of Object.values(servers)) {
2285
- if (server?.host) {
2286
- addresses.push(`${server.protocol ? `${server.protocol}://` : ""}${server.host}${server.pathname ?? ""}`);
2287
- }
2288
- }
2289
- return { addresses, label: "Servers" };
2290
- }
2291
- const declared = spec.document.servers;
2292
- for (const server of Array.isArray(declared) ? declared : []) {
2293
- if (server.url) {
2294
- addresses.push(server.url);
2295
- }
1334
+ // src/core/sources/contentful-rich-text.ts
1335
+ var HEADING = /^heading-(?<level>[1-6])$/u;
1336
+ var CELL_PIPE = /\|/gu;
1337
+ var CELL_BREAK = /\n+/gu;
1338
+ var assetFromEntry = (asset) => {
1339
+ const url = asString(getPath(asset, "fields.file.url"));
1340
+ if (!url) {
1341
+ return null;
2296
1342
  }
2297
- return { addresses, label: "Base URL" };
2298
- };
2299
- var isOperation = (value) => typeof value === "object" && value !== null;
2300
- var hasTagName = (tag) => typeof tag.name === "string";
2301
- var tagSlugger = () => {
2302
- const assigned = new Map;
2303
- const taken = new Set;
2304
- return (name) => {
2305
- const existing = assigned.get(name);
2306
- if (existing) {
2307
- return existing;
2308
- }
2309
- const base = slugify(name) || "operations";
2310
- let slug = base;
2311
- for (let suffix = 2;taken.has(slug); suffix += 1) {
2312
- slug = `${base}-${suffix}`;
2313
- }
2314
- taken.add(slug);
2315
- assigned.set(name, slug);
2316
- return slug;
2317
- };
2318
- };
2319
- var operationCollector = (baseRoute, tagMeta) => {
2320
- const operations = [];
2321
- const tagOrder = [];
2322
- const tagsSeen = new Set;
2323
- const seen = new Set;
2324
- const slugForTag = tagSlugger();
2325
- const add = (entry) => {
2326
- const tagSlug = slugForTag(entry.tag);
2327
- if (!tagsSeen.has(entry.tag)) {
2328
- tagsSeen.add(entry.tag);
2329
- tagOrder.push(entry.tag);
2330
- }
2331
- let { key } = entry;
2332
- while (seen.has(key)) {
2333
- key = `${key}-${entry.method}`;
2334
- }
2335
- seen.add(key);
2336
- operations.push({
2337
- ...entry,
2338
- key,
2339
- route: `${baseRoute === "/" ? "" : baseRoute}/${tagSlug}/${key}`,
2340
- tagSlug
2341
- });
2342
- };
2343
- const finish = () => ({
2344
- operations,
2345
- tags: tagOrder.map((name) => ({
2346
- description: tagMeta.get(name) ?? "",
2347
- name,
2348
- slug: slugForTag(name)
2349
- }))
2350
- });
2351
- return { add, finish };
2352
- };
2353
- var extractOperations = (document, baseRoute) => {
2354
- const warnings = [];
2355
- const tagMeta = new Map((document.tags ?? []).filter(hasTagName).map((tag) => [tag.name, tag.description ?? ""]));
2356
- const collector = operationCollector(baseRoute, tagMeta);
2357
- for (const [path, item] of Object.entries(document.paths ?? {})) {
2358
- if (!item) {
2359
- continue;
2360
- }
2361
- if ("$ref" in item) {
2362
- warnings.push(`Path "${path}" is a $ref to a shared path item; referenced path items are not resolved, so its operations are missing from the reference. Inline the path item under "paths" to render it.`);
2363
- continue;
2364
- }
2365
- for (const method of HTTP_METHODS) {
2366
- const operation = item[method];
2367
- if (!isOperation(operation)) {
2368
- continue;
1343
+ return {
1344
+ description: asString(getPath(asset, "fields.description")),
1345
+ title: asString(getPath(asset, "fields.title")),
1346
+ url: absoluteUrl(url)
1347
+ };
1348
+ };
1349
+ var children = (node) => objectsIn(node.content);
1350
+ var marksOf = (node) => {
1351
+ const marks = {};
1352
+ for (const mark of objectsIn(node.marks)) {
1353
+ switch (asString(mark.type)) {
1354
+ case "bold": {
1355
+ marks.bold = true;
1356
+ break;
2369
1357
  }
2370
- collector.add({
2371
- deprecated: operation.deprecated ?? false,
2372
- description: operation.description ?? "",
2373
- key: operationKey(method, path, operation.operationId),
2374
- method,
2375
- operationId: operation.operationId,
2376
- path,
2377
- summary: operation.summary ?? "",
2378
- tag: operation.tags?.[0] ?? UNTAGGED
2379
- });
2380
- }
2381
- }
2382
- return { ...collector.finish(), warnings };
2383
- };
2384
-
2385
- // src/openapi/asyncapi.ts
2386
- var ASYNCAPI_ACTIONS = ["send", "receive"];
2387
- var unescapePointer = (token) => token.replaceAll("~1", "/").replaceAll("~0", "~");
2388
- var CHANNEL_REF = /^#\/channels\/(?<id>.+)$/u;
2389
- var isString = (value) => typeof value === "string";
2390
- var channelIdOf = (channel) => {
2391
- const id = CHANNEL_REF.exec(channel?.$ref ?? "")?.groups?.id;
2392
- return id === undefined ? undefined : unescapePointer(id);
2393
- };
2394
- var channelAddress = (channelId, channel) => {
2395
- const address = channel?.address;
2396
- return isString(address) && address !== "" ? address : channelId;
2397
- };
2398
- var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2399
- var COMPONENT_SECTION_REF = /^#\/components\/(?<section>channels|operations)\/(?<name>[^/]+)$/u;
2400
- var inlineComponentRefs = (document) => {
2401
- const maps = [
2402
- [document.channels, "channels"],
2403
- [document.operations, "operations"]
2404
- ];
2405
- for (const [map, section] of maps) {
2406
- if (!isObject(map)) {
2407
- continue;
2408
- }
2409
- const table = document.components?.[section];
2410
- for (const [id, entry] of Object.entries(map)) {
2411
- if (!isObject(entry) || !isString(entry.$ref)) {
2412
- continue;
1358
+ case "italic": {
1359
+ marks.italic = true;
1360
+ break;
2413
1361
  }
2414
- const groups = COMPONENT_SECTION_REF.exec(entry.$ref)?.groups;
2415
- if (groups?.section !== section) {
2416
- continue;
1362
+ case "code": {
1363
+ marks.code = true;
1364
+ break;
2417
1365
  }
2418
- const resolved = isObject(table) ? table[unescapePointer(groups.name ?? "")] : undefined;
2419
- if (isObject(resolved) && !isString(resolved.$ref)) {
2420
- map[id] = { ...resolved };
1366
+ case "strikethrough": {
1367
+ marks.strike = true;
1368
+ break;
2421
1369
  }
1370
+ default:
2422
1371
  }
2423
1372
  }
1373
+ return marks;
2424
1374
  };
2425
- var TRAIT_REF = /^#\/components\/(?<section>operationTraits|messageTraits)\/(?<name>[^/]+)$/u;
2426
- var applyAsyncApiTraits = (document) => {
2427
- const resolveTrait = (trait) => {
2428
- if (!isObject(trait)) {
2429
- return;
2430
- }
2431
- if (!isString(trait.$ref)) {
2432
- return trait;
2433
- }
2434
- const groups = TRAIT_REF.exec(trait.$ref)?.groups;
2435
- const section = groups?.section ? document.components?.[groups.section] : undefined;
2436
- const resolved = section?.[groups?.name ?? ""];
2437
- return isObject(resolved) ? resolved : undefined;
2438
- };
2439
- const mergeTraits = (node) => {
2440
- const { traits } = node;
2441
- if (!Array.isArray(traits)) {
2442
- return;
2443
- }
2444
- delete node.traits;
2445
- const merged = {};
2446
- for (const trait of traits) {
2447
- Object.assign(merged, resolveTrait(trait));
2448
- }
2449
- for (const [key, value] of Object.entries(merged)) {
2450
- if (!(key in node)) {
2451
- node[key] = value;
2452
- }
2453
- }
2454
- };
2455
- for (const operation of Object.values(document.operations ?? {})) {
2456
- if (isObject(operation)) {
2457
- mergeTraits(operation);
2458
- }
2459
- }
2460
- const channelMaps = [
2461
- ...Object.values(document.channels ?? {}),
2462
- ...Object.values(document.components?.channels ?? {})
2463
- ];
2464
- const messageMaps = [
2465
- ...channelMaps.map((channel) => isObject(channel) ? channel.messages : undefined),
2466
- document.components?.messages
2467
- ];
2468
- for (const messages of messageMaps) {
2469
- if (!isObject(messages)) {
2470
- continue;
2471
- }
2472
- for (const message of Object.values(messages)) {
2473
- if (isObject(message) && !isString(message.$ref)) {
2474
- mergeTraits(message);
2475
- }
2476
- }
1375
+ var linkedTarget = (node, resolve) => {
1376
+ const target = getPath(node, "data.target");
1377
+ if (getPath(node, "data.target.fields") !== undefined) {
1378
+ return target;
2477
1379
  }
2478
- return document;
2479
- };
2480
- var normalizeAsyncApiDocument = (document) => {
2481
- inlineComponentRefs(document);
2482
- return applyAsyncApiTraits(document);
1380
+ const id = asString(getPath(node, "data.target.sys.id"));
1381
+ return id ? resolve?.(id) ?? null : null;
2483
1382
  };
2484
- var operationSite = (id, operation, channels) => {
2485
- const action = ASYNCAPI_ACTIONS.find((a) => a === operation.action);
2486
- if (!action) {
2487
- return `Operation "${id}" declares no send/receive action; it is missing from the reference.`;
1383
+ var linkedAsset = (node, options) => {
1384
+ if (getPath(node, "data.target.fields") !== undefined) {
1385
+ const inline = linkedTarget(node);
1386
+ return inline ? assetFromEntry(inline) : null;
2488
1387
  }
2489
- const channelId = channelIdOf(operation.channel);
2490
- const channel = channelId === undefined ? undefined : channels[channelId];
2491
- if (channelId === undefined || !isObject(channel) || isString(channel.$ref)) {
2492
- return `Operation "${id}" references a channel that isn't declared under "channels"; it is missing from the reference.`;
2493
- }
2494
- return { action, address: channelAddress(channelId, channel), channelId };
1388
+ const id = asString(getPath(node, "data.target.sys.id"));
1389
+ return id ? options.resolveAsset?.(id) ?? null : null;
2495
1390
  };
2496
- var extractAsyncApiOperations = (document, baseRoute) => {
2497
- const warnings = [];
2498
- const tagMeta = new Map((document.info?.tags ?? []).filter((tag) => typeof tag?.name === "string").map((tag) => [tag.name, tag.description ?? ""]));
2499
- const collector = operationCollector(baseRoute, tagMeta);
2500
- const channels = document.channels ?? {};
2501
- for (const [id, operation] of Object.entries(document.operations ?? {})) {
2502
- if (!isObject(operation)) {
2503
- continue;
2504
- }
2505
- if (isString(operation.$ref)) {
2506
- warnings.push(`Operation "${id}" is a reference that doesn't resolve to a components operation; it is missing from the reference.`);
2507
- continue;
2508
- }
2509
- const site = operationSite(id, operation, channels);
2510
- if (isString(site)) {
2511
- warnings.push(site);
2512
- continue;
2513
- }
2514
- const { action, address, channelId } = site;
2515
- const tag = operation.tags?.find((candidate) => typeof candidate?.name === "string")?.name;
2516
- collector.add({
2517
- channelId,
2518
- deprecated: operation.deprecated === true,
2519
- description: operation.description ?? "",
2520
- key: operationKey(action, address, id),
2521
- method: action,
2522
- operationId: id,
2523
- path: address,
2524
- summary: operation.title ?? operation.summary ?? "",
2525
- tag: tag ?? address
2526
- });
1391
+ var embeddedEntry = (node, options) => {
1392
+ const entry = linkedTarget(node, options.resolveEntry);
1393
+ const contentType = entry ? asString(getPath(entry, "sys.contentType.sys.id")) : undefined;
1394
+ const serializer = contentType ? options.serializers?.[contentType] : undefined;
1395
+ if (entry && serializer) {
1396
+ return serializer(entry);
2527
1397
  }
2528
- return { ...collector.finish(), warnings };
1398
+ return unsupported(`Contentful embedded entry${contentType ? `: ${contentType}` : ""}`, writesMdx(options.serializers));
2529
1399
  };
2530
-
2531
- // src/openapi/graphql.ts
2532
- var GRAPHQL_OPERATION_KINDS = [
2533
- "query",
2534
- "mutation",
2535
- "subscription"
2536
- ];
2537
- var GRAPHQL_TYPE_KINDS = [
2538
- "object",
2539
- "input",
2540
- "enum",
2541
- "interface",
2542
- "union",
2543
- "scalar"
2544
- ];
2545
- var GRAPHQL_GROUP_LABELS = {
2546
- enum: "Enums",
2547
- input: "Input Objects",
2548
- interface: "Interfaces",
2549
- mutation: "Mutations",
2550
- object: "Objects",
2551
- query: "Queries",
2552
- scalar: "Scalars",
2553
- subscription: "Subscriptions",
2554
- union: "Unions"
2555
- };
2556
- var isGraphqlOperationKind = (method) => GRAPHQL_OPERATION_KINDS.some((kind) => kind === method);
2557
- var GRAPHQL_TYPE_KEYWORDS = {
2558
- enum: "enum",
2559
- input: "input",
2560
- interface: "interface",
2561
- object: "type",
2562
- scalar: "scalar",
2563
- union: "union"
2564
- };
2565
- var graphqlSignature = (operation) => {
2566
- const member = operation.method;
2567
- const keyword = isGraphqlOperationKind(member) ? member : GRAPHQL_TYPE_KEYWORDS[member];
2568
- return `${keyword} ${operation.path}`;
2569
- };
2570
- var byName = (a, b) => a.name < b.name ? -1 : 1;
2571
- var extractGraphqlOperations = (document, baseRoute) => {
2572
- const collector = operationCollector(baseRoute, new Map);
2573
- const rootNames = new Set(Object.values(document.roots));
2574
- for (const kind of GRAPHQL_OPERATION_KINDS) {
2575
- const rootName = document.roots[kind];
2576
- const root = rootName === undefined ? undefined : document.types[rootName];
2577
- for (const field of root?.fields ?? []) {
2578
- collector.add({
2579
- deprecated: field.deprecationReason !== undefined,
2580
- description: field.description,
2581
- key: operationKey(kind, field.name, field.name),
2582
- method: kind,
2583
- operationId: field.name,
2584
- path: field.name,
2585
- summary: "",
2586
- tag: GRAPHQL_GROUP_LABELS[kind]
2587
- });
2588
- }
2589
- }
2590
- for (const kind of GRAPHQL_TYPE_KINDS) {
2591
- const members = Object.values(document.types).filter((type) => type.kind === kind && !(type.builtIn || rootNames.has(type.name))).toSorted(byName);
2592
- for (const type of members) {
2593
- collector.add({
2594
- deprecated: false,
2595
- description: type.description,
2596
- key: operationKey(kind, type.name, type.name),
2597
- method: kind,
2598
- operationId: type.name,
2599
- path: type.name,
2600
- summary: "",
2601
- tag: GRAPHQL_GROUP_LABELS[kind]
2602
- });
1400
+ var renderInlines = (nodes, options) => nodes.map((node) => renderInlineNode(node, options)).join("");
1401
+ var renderInlineNode = (node, options) => {
1402
+ switch (asString(node.nodeType)) {
1403
+ case "text": {
1404
+ return renderInline(asString(node.value) ?? "", marksOf(node));
2603
1405
  }
2604
- }
2605
- return { ...collector.finish(), warnings: [] };
2606
- };
2607
-
2608
- // src/openapi/parse.ts
2609
- import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
2610
- import { setTimeout as sleep } from "node:timers/promises";
2611
- import { convert } from "@asyncapi/converter";
2612
- import { normalize as normalize2, upgrade } from "@scalar/openapi-parser";
2613
- import pRetry, { AbortError } from "p-retry";
2614
- import { isAbsolute, join as join3 } from "pathe";
2615
-
2616
- // src/openapi/graphql-build.ts
2617
- import {
2618
- buildClientSchema,
2619
- buildSchema,
2620
- getNamedType,
2621
- isEnumType,
2622
- isInputObjectType,
2623
- isInterfaceType,
2624
- isObjectType,
2625
- isSpecifiedScalarType,
2626
- isUnionType,
2627
- print
2628
- } from "graphql";
2629
- var typeRef = (type) => ({
2630
- display: String(type),
2631
- name: getNamedType(type).name
2632
- });
2633
- var defaultLiteral = (input) => {
2634
- const literal = input.default?.literal;
2635
- return literal ? print(literal) : undefined;
2636
- };
2637
- var inputValue = (input) => {
2638
- const value = {
2639
- description: input.description ?? "",
2640
- name: input.name,
2641
- type: typeRef(input.type)
2642
- };
2643
- const printed = defaultLiteral(input);
2644
- if (printed !== undefined) {
2645
- value.default = printed;
2646
- }
2647
- if (input.deprecationReason !== null && input.deprecationReason !== undefined) {
2648
- value.deprecationReason = input.deprecationReason;
2649
- }
2650
- return value;
2651
- };
2652
- var outputField = (field) => {
2653
- const value = {
2654
- args: field.args.map(inputValue),
2655
- description: field.description ?? "",
2656
- name: field.name,
2657
- type: typeRef(field.type)
2658
- };
2659
- if (field.deprecationReason !== null && field.deprecationReason !== undefined) {
2660
- value.deprecationReason = field.deprecationReason;
2661
- }
2662
- return value;
2663
- };
2664
- var enumValues = (type) => type.getValues().map((value) => {
2665
- const member = {
2666
- description: value.description ?? "",
2667
- name: value.name
2668
- };
2669
- if (value.deprecationReason !== null && value.deprecationReason !== undefined) {
2670
- member.deprecationReason = value.deprecationReason;
2671
- }
2672
- return member;
2673
- });
2674
- var objectDef = (type, kind) => {
2675
- const interfaces = type.getInterfaces().map((iface) => iface.name);
2676
- const def = {
2677
- description: type.description ?? "",
2678
- fields: Object.values(type.getFields()).map(outputField),
2679
- kind,
2680
- name: type.name
2681
- };
2682
- if (interfaces.length > 0) {
2683
- def.interfaces = interfaces;
2684
- }
2685
- return def;
2686
- };
2687
- var inputDef = (type) => ({
2688
- description: type.description ?? "",
2689
- inputFields: Object.values(type.getFields()).map(inputValue),
2690
- kind: "input",
2691
- name: type.name
2692
- });
2693
- var unionDef = (type) => ({
2694
- description: type.description ?? "",
2695
- kind: "union",
2696
- name: type.name,
2697
- possibleTypes: type.getTypes().map((member) => member.name)
2698
- });
2699
- var scalarDef = (type) => {
2700
- const def = {
2701
- description: type.description ?? "",
2702
- kind: "scalar",
2703
- name: type.name
2704
- };
2705
- if (type.specifiedByURL !== null && type.specifiedByURL !== undefined) {
2706
- def.specifiedByUrl = type.specifiedByURL;
2707
- }
2708
- if (isSpecifiedScalarType(type)) {
2709
- def.builtIn = true;
2710
- }
2711
- return def;
2712
- };
2713
- var typeDef = (type, schema) => {
2714
- if (isObjectType(type)) {
2715
- return objectDef(type, "object");
2716
- }
2717
- if (isInterfaceType(type)) {
2718
- const def = objectDef(type, "interface");
2719
- const implementers = schema.getImplementations(type).objects.map((object) => object.name);
2720
- if (implementers.length > 0) {
2721
- def.possibleTypes = implementers;
1406
+ case "hyperlink": {
1407
+ return renderLink(renderInlines(children(node), options), asString(getPath(node, "data.uri")));
2722
1408
  }
2723
- return def;
2724
- }
2725
- if (isInputObjectType(type)) {
2726
- return inputDef(type);
2727
- }
2728
- if (isEnumType(type)) {
2729
- return {
2730
- description: type.description ?? "",
2731
- enumValues: enumValues(type),
2732
- kind: "enum",
2733
- name: type.name
2734
- };
2735
- }
2736
- if (isUnionType(type)) {
2737
- return unionDef(type);
2738
- }
2739
- return scalarDef(type);
2740
- };
2741
- var documentOf = (schema) => {
2742
- const types = {};
2743
- for (const [name, type] of Object.entries(schema.getTypeMap())) {
2744
- if (name.startsWith("__")) {
2745
- continue;
1409
+ case "asset-hyperlink": {
1410
+ return renderLink(renderInlines(children(node), options), linkedAsset(node, options)?.url);
2746
1411
  }
2747
- types[name] = typeDef(type, schema);
2748
- }
2749
- const roots = {};
2750
- const query = schema.getQueryType();
2751
- const mutation = schema.getMutationType();
2752
- const subscription = schema.getSubscriptionType();
2753
- if (query) {
2754
- roots.query = query.name;
2755
- }
2756
- if (mutation) {
2757
- roots.mutation = mutation.name;
2758
- }
2759
- if (subscription) {
2760
- roots.subscription = subscription.name;
2761
- }
2762
- return {
2763
- info: { description: schema.description ?? "" },
2764
- roots,
2765
- types
2766
- };
2767
- };
2768
- var hasSchemaKey = (value) => typeof value === "object" && value !== null && ("__schema" in value);
2769
- var hasDataEnvelope = (value) => typeof value === "object" && value !== null && ("data" in value);
2770
- var introspectionOf = (parsed) => {
2771
- if (hasSchemaKey(parsed)) {
2772
- return parsed;
2773
- }
2774
- return hasDataEnvelope(parsed) && hasSchemaKey(parsed.data) ? parsed.data : undefined;
2775
- };
2776
- var buildGraphqlDocument = (text) => {
2777
- let parsed;
2778
- let isJson = true;
2779
- try {
2780
- parsed = JSON.parse(text);
2781
- } catch {
2782
- isJson = false;
2783
- }
2784
- if (isJson) {
2785
- const introspection = introspectionOf(parsed);
2786
- if (!introspection) {
2787
- throw new Error("the JSON document is not a GraphQL introspection result (no `__schema` key).");
1412
+ case "embedded-entry-inline": {
1413
+ return embeddedEntry(node, options);
2788
1414
  }
2789
- return documentOf(buildClientSchema(introspection));
2790
- }
2791
- return documentOf(buildSchema(text));
2792
- };
2793
-
2794
- // src/openapi/parse.ts
2795
- var URL_SPEC = /^https?:\/\//u;
2796
- var FETCH_TIMEOUT_MS = 15000;
2797
- var MAX_ATTEMPTS = 3;
2798
- var BASE_BACKOFF_MS = 500;
2799
- var MAX_RETRY_WAIT_MS = 1e4;
2800
- var SECOND_MS = 1000;
2801
- var RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504]);
2802
- var USER_AGENT = "blume (+https://github.com/haydenbleasel/blume)";
2803
- var PROXY_ENV_VARS = [
2804
- "HTTP_PROXY",
2805
- "http_proxy",
2806
- "HTTPS_PROXY",
2807
- "https_proxy",
2808
- "ALL_PROXY",
2809
- "all_proxy"
2810
- ];
2811
-
2812
- class InvalidSpecError extends Error {
2813
- constructor(message) {
2814
- super(message);
2815
- this.name = "InvalidSpecError";
2816
- }
2817
- }
2818
- var proxyInstalled = false;
2819
- var ensureProxyDispatcher = async () => {
2820
- if (proxyInstalled || !PROXY_ENV_VARS.some((name) => process.env[name])) {
2821
- return;
2822
- }
2823
- try {
2824
- const { EnvHttpProxyAgent, setGlobalDispatcher } = await import("undici");
2825
- setGlobalDispatcher(new EnvHttpProxyAgent);
2826
- proxyInstalled = true;
2827
- } catch {}
2828
- };
2829
- var retryAfterMs = (response) => {
2830
- const header = response.headers.get("retry-after");
2831
- if (!header) {
2832
- return;
2833
- }
2834
- const seconds = Number(header);
2835
- if (Number.isFinite(seconds)) {
2836
- return seconds > 0 ? seconds * SECOND_MS : undefined;
2837
- }
2838
- const delta = Date.parse(header) - Date.now();
2839
- return Number.isFinite(delta) && delta > 0 ? delta : undefined;
2840
- };
2841
- var attemptFetch = async (spec) => {
2842
- try {
2843
- const response = await fetch(spec, {
2844
- headers: {
2845
- accept: "application/json, application/yaml, text/yaml, */*",
2846
- "user-agent": USER_AGENT
2847
- },
2848
- redirect: "follow",
2849
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
2850
- });
2851
- if (response.ok) {
2852
- return { text: await response.text() };
1415
+ default: {
1416
+ return renderInlines(children(node), options);
2853
1417
  }
2854
- return {
2855
- error: new Error(`${spec} -> ${response.status} ${response.statusText}`),
2856
- retryAfter: retryAfterMs(response),
2857
- retryable: RETRYABLE_STATUS.has(response.status)
2858
- };
2859
- } catch (error) {
2860
- return {
2861
- error: error instanceof Error ? error : new Error(String(error)),
2862
- retryable: true
2863
- };
2864
1418
  }
2865
1419
  };
2866
- var retryableFetchError = (error, retryAfter) => {
2867
- const wrapper = new Error(error.message, {
2868
- cause: error
2869
- });
2870
- wrapper.name = "RetryableFetchError";
2871
- wrapper.retryAfter = retryAfter;
2872
- return wrapper;
2873
- };
2874
- var fetchSpecText = async (spec) => {
2875
- await ensureProxyDispatcher();
2876
- return await pRetry(async () => {
2877
- const attempt = await attemptFetch(spec);
2878
- if ("text" in attempt) {
2879
- return attempt.text;
2880
- }
2881
- if (!attempt.retryable) {
2882
- throw new AbortError(attempt.error);
2883
- }
2884
- throw retryableFetchError(attempt.error, attempt.retryAfter);
2885
- }, {
2886
- factor: 2,
2887
- maxTimeout: MAX_RETRY_WAIT_MS,
2888
- minTimeout: BASE_BACKOFF_MS,
2889
- onFailedAttempt: async (context) => {
2890
- const { retryAfter } = context.error;
2891
- if (retryAfter !== undefined && context.retriesLeft > 0) {
2892
- await sleep(Math.max(0, Math.min(retryAfter, MAX_RETRY_WAIT_MS) - context.retryDelay));
2893
- }
2894
- },
2895
- retries: MAX_ATTEMPTS - 1
2896
- });
2897
- };
2898
- var cacheFileFor = (cacheDir, spec) => join3(cacheDir, `spec-${hashText(spec)}.cache`);
2899
- var readCache = async (file) => {
2900
- try {
2901
- return await readFile3(file, "utf-8");
2902
- } catch {
2903
- return;
1420
+ var renderList = (items, marker, options) => items.map((item, index) => listItem(marker(index), children(item).map((child) => renderBlock(child, options)).filter((block) => block !== "").join(`
1421
+ `))).join(`
1422
+ `);
1423
+ var cellText = (cell, options) => children(cell).map((paragraph) => renderInlines(children(paragraph), options)).join(" ").replaceAll(CELL_BREAK, " ").replaceAll(CELL_PIPE, String.raw`\|`);
1424
+ var tableRow = (cells) => `| ${cells.join(" | ")} |`;
1425
+ var renderTable = (node, options) => {
1426
+ const rows = children(node).map((row) => children(row).map((cell) => cellText(cell, options)));
1427
+ const [head, ...body] = rows;
1428
+ if (!head) {
1429
+ return "";
2904
1430
  }
1431
+ return [
1432
+ tableRow(head),
1433
+ tableRow(head.map(() => "---")),
1434
+ ...body.map((cells) => tableRow(cells))
1435
+ ].join(`
1436
+ `);
2905
1437
  };
2906
- var writeCache = async (dir, file, text) => {
2907
- try {
2908
- await mkdir2(dir, { recursive: true });
2909
- await writeFile2(file, text, "utf-8");
2910
- } catch {}
2911
- };
2912
- var readSpecText = async (spec, root, options) => {
2913
- if (!URL_SPEC.test(spec)) {
2914
- const absolute = isAbsolute(spec) ? spec : join3(root, spec);
2915
- return { text: await readFile3(absolute, "utf-8"), warnings: [] };
2916
- }
2917
- const cacheFile = options.cacheDir ? cacheFileFor(options.cacheDir, spec) : undefined;
2918
- if (cacheFile && options.refresh === false) {
2919
- const cached = await readCache(cacheFile);
2920
- if (cached !== undefined) {
2921
- return { text: cached, warnings: [] };
2922
- }
1438
+ var renderBlock = (node, options) => {
1439
+ const type = asString(node.nodeType) ?? "";
1440
+ const heading = HEADING.exec(type)?.groups?.level;
1441
+ if (heading) {
1442
+ return `${headingPrefix(Number(heading))}${renderInlines(children(node), options)}`;
2923
1443
  }
2924
- try {
2925
- const text = await fetchSpecText(spec);
2926
- if (options.cacheDir && cacheFile) {
2927
- await writeCache(options.cacheDir, cacheFile, text);
1444
+ switch (type) {
1445
+ case "paragraph": {
1446
+ return renderInlines(children(node), options);
2928
1447
  }
2929
- return { text, warnings: [] };
2930
- } catch (error) {
2931
- if (cacheFile) {
2932
- const cached = await readCache(cacheFile);
2933
- if (cached !== undefined) {
2934
- return {
2935
- text: cached,
2936
- warnings: [
2937
- `Could not fetch ${spec} (${error.message}); using the last cached copy.`
2938
- ]
2939
- };
2940
- }
1448
+ case "blockquote": {
1449
+ return blockquote(joinBlocks(children(node).map((child) => renderBlock(child, options))));
2941
1450
  }
2942
- throw error;
2943
- }
2944
- };
2945
- var isApiDocument = (value) => typeof value === "object" && value !== null;
2946
- var parseSpec = async (spec, root, options = {}) => {
2947
- const { text, warnings } = await readSpecText(spec, root, options);
2948
- const normalized = normalize2(text);
2949
- const { specification } = upgrade(normalized);
2950
- if (!isApiDocument(specification)) {
2951
- throw new InvalidSpecError(`${spec} is not a valid OpenAPI document (expected a YAML or JSON object).`);
2952
- }
2953
- return { document: specification, warnings };
2954
- };
2955
- var isAsyncApiDocument = (value) => typeof value === "object" && value !== null && ("asyncapi" in value) && typeof value.asyncapi === "string" && value.asyncapi !== "";
2956
- var parseAsyncApiSpec = async (spec, root, options = {}) => {
2957
- const { text, warnings } = await readSpecText(spec, root, options);
2958
- const normalized = normalize2(text);
2959
- if (!isAsyncApiDocument(normalized)) {
2960
- throw new InvalidSpecError(`${spec} is not a valid AsyncAPI document (expected a YAML or JSON object with an \`asyncapi\` version field).`);
2961
- }
2962
- const version = normalized.asyncapi;
2963
- let document = normalized;
2964
- if (!version.startsWith("3.")) {
2965
- const captured = [];
2966
- const originalWarn = console.warn;
2967
- console.warn = (...args) => {
2968
- captured.push(args.map(String).join(" "));
2969
- };
2970
- try {
2971
- document = convert(document, "3.0.0");
2972
- } catch (error) {
2973
- throw new InvalidSpecError(`${spec} could not be converted to AsyncAPI 3.0 (${error.message}).`);
2974
- } finally {
2975
- console.warn = originalWarn;
2976
- }
2977
- warnings.push(...captured.map((message) => `Converting ${spec} to AsyncAPI 3.0: ${message}`));
2978
- }
2979
- return { document: normalizeAsyncApiDocument(document), warnings };
2980
- };
2981
- var parseGraphqlSpec = async (spec, root, options = {}) => {
2982
- const { text, warnings } = await readSpecText(spec, root, options);
2983
- try {
2984
- return { document: buildGraphqlDocument(text), warnings };
2985
- } catch (error) {
2986
- throw new InvalidSpecError(`${spec} is not a valid GraphQL schema (${error.message.trim()})`);
2987
- }
2988
- };
2989
-
2990
- // src/openapi/render-mdx.ts
2991
- import { fromMarkdown } from "mdast-util-from-markdown";
2992
- import { toString as mdastToString } from "mdast-util-to-string";
2993
- import stringWidth2 from "string-width";
2994
-
2995
- // src/core/api-name.ts
2996
- var NAMES_AN_API = /\bAPIs?\b/iu;
2997
- var apiNamePhrase = (name) => {
2998
- const trimmed = name.trim();
2999
- return NAMES_AN_API.test(trimmed) ? trimmed : `${trimmed} API`;
3000
- };
3001
-
3002
- // src/core/text-width.ts
3003
- import stringWidth from "string-width";
3004
- var columnsPrefix = (text, max) => {
3005
- let end = 0;
3006
- let used = 0;
3007
- const graphemes = new Intl.Segmenter(undefined, { granularity: "grapheme" });
3008
- for (const { index, segment } of graphemes.segment(text)) {
3009
- const width = stringWidth(segment);
3010
- if (used + width > max) {
3011
- break;
1451
+ case "unordered-list": {
1452
+ return renderList(children(node), () => "-", options);
3012
1453
  }
3013
- used += width;
3014
- end = index + segment.length;
3015
- }
3016
- return text.slice(0, end);
3017
- };
3018
-
3019
- // src/openapi/signature.ts
3020
- var operationSignature = (spec, operation) => spec.kind === "graphql" ? graphqlSignature(operation) : `${operation.method.toUpperCase()} ${operation.path}`;
3021
-
3022
- // src/openapi/render-mdx.ts
3023
- var MDX_UNSAFE = /[<{}]/gu;
3024
- var ENTITIES = new Map([
3025
- ["<", "&lt;"],
3026
- ["{", "&#123;"],
3027
- ["}", "&#125;"]
3028
- ]);
3029
- var MDX_ESM_KEYWORD = /^(?<keyword>import|export)\b/gmu;
3030
- var escapeProse = (text) => text.replace(MDX_UNSAFE, (char) => ENTITIES.get(char) ?? char).replace(MDX_ESM_KEYWORD, (keyword) => `&#${keyword.codePointAt(0)};${keyword.slice(1)}`);
3031
- var codeSpans = (text) => {
3032
- const spans = [];
3033
- const collect = (node) => {
3034
- if (node.type === "inlineCode" || node.type === "code") {
3035
- const start = node.position?.start.offset ?? -1;
3036
- const end = node.position?.end.offset ?? -1;
3037
- const head = text.slice(Math.max(start, 0), Math.max(end, 0)).trimStart();
3038
- if (start >= 0 && (node.type === "inlineCode" || head.startsWith("`") || head.startsWith("~"))) {
3039
- spans.push([start, end]);
3040
- }
3041
- return;
1454
+ case "ordered-list": {
1455
+ return renderList(children(node), (index) => `${index + 1}.`, options);
3042
1456
  }
3043
- if ("children" in node) {
3044
- for (const child of node.children) {
3045
- collect(child);
3046
- }
1457
+ case "hr": {
1458
+ return "---";
3047
1459
  }
3048
- };
3049
- collect(fromMarkdown(text));
3050
- return spans;
3051
- };
3052
- var mdxSafe = (text) => {
3053
- let out = "";
3054
- let cursor = 0;
3055
- for (const [start, end] of codeSpans(text)) {
3056
- out += escapeProse(text.slice(cursor, start));
3057
- out += text.slice(start, end);
3058
- cursor = end;
3059
- }
3060
- return out + escapeProse(text.slice(cursor));
3061
- };
3062
- var META_DESCRIPTION_MAX = 160;
3063
- var WHITESPACE = /\s+/gu;
3064
- var TRAILING_WORD = /\s+\S*$/u;
3065
- var plainProse = (markdown) => {
3066
- const tree = fromMarkdown(markdown);
3067
- const first = tree.children.find((node) => node.type === "paragraph") ?? tree.children[0];
3068
- return first ? mdastToString(first).replace(WHITESPACE, " ").trim() : "";
3069
- };
3070
- var clip = (text, max) => {
3071
- if (max <= 0) {
3072
- return "";
3073
- }
3074
- if (stringWidth2(text) <= max) {
3075
- return text;
3076
- }
3077
- const head = columnsPrefix(text, max - 1);
3078
- const onWordBoundary = head.replace(TRAILING_WORD, "");
3079
- return `${stringWidth2(onWordBoundary) >= max / 2 ? onWordBoundary : head}…`;
3080
- };
3081
- var apiName = (spec) => spec.title || spec.label;
3082
- var apiPhrase = (spec) => apiNamePhrase(apiName(spec));
3083
- var GRAPHQL_MEMBER_PHRASES = {
3084
- enum: "enum type",
3085
- input: "input object type",
3086
- interface: "interface type",
3087
- mutation: "mutation",
3088
- object: "object type",
3089
- query: "query",
3090
- scalar: "scalar type",
3091
- subscription: "subscription",
3092
- union: "union type"
3093
- };
3094
- var operationDescription = (spec, operation, options) => {
3095
- if (!options.suffix) {
3096
- return clip(plainProse(operation.description || operation.summary) || options.title, META_DESCRIPTION_MAX);
3097
- }
3098
- let subject;
3099
- if (spec.kind === "asyncapi") {
3100
- subject = `${operation.method} operation on ${operation.path}`;
3101
- } else if (spec.kind === "graphql") {
3102
- subject = `${operation.path} ${GRAPHQL_MEMBER_PHRASES[operation.method]}`;
3103
- } else {
3104
- subject = `${operation.method.toUpperCase()} ${operation.path} endpoint`;
3105
- }
3106
- const suffix = `Reference for the ${subject} in the ${apiPhrase(spec)}.`;
3107
- const prose = clip(plainProse(operation.description || operation.summary), META_DESCRIPTION_MAX - stringWidth2(suffix) - 1);
3108
- return clip([prose, suffix].filter(Boolean).join(" "), META_DESCRIPTION_MAX);
3109
- };
3110
- var withDescription = (description, component) => description.trim() ? `${mdxSafe(description.trim())}
3111
-
3112
- ${component}` : component;
3113
- var operationMdx = (spec, operation, reference) => {
3114
- const method = operation.method.toUpperCase();
3115
- const graphql = spec.kind === "graphql";
3116
- const fallbackTitle = graphql ? operation.path : operationSignature(spec, operation);
3117
- const title = operation.summary || fallbackTitle;
3118
- const description = operation.description.trim() === operation.summary.trim() ? "" : operation.description;
3119
- const flags = {};
3120
- if (reference?.includeInLlms === false) {
3121
- flags.ai = { exclude: true };
3122
- }
3123
- if (operation.deprecated) {
3124
- flags.deprecated = true;
3125
- }
3126
- const searchFlags = {};
3127
- if (reference?.includeInSearch === false) {
3128
- searchFlags.exclude = true;
3129
- }
3130
- const seo = {
3131
- description: operationDescription(spec, operation, {
3132
- suffix: reference?.seoDescriptionSuffix !== false,
3133
- title
3134
- })
3135
- };
3136
- if (reference?.noindex) {
3137
- seo.noindex = true;
3138
- }
3139
- const sidebar = {
3140
- label: operation.summary || operation.path
3141
- };
3142
- if (!graphql || isGraphqlOperationKind(operation.method)) {
3143
- sidebar.badge = method;
3144
- }
3145
- const tags = graphql ? [operation.tag] : [operation.tag, method];
3146
- return {
3147
- body: withDescription(description, `<Operation source="${spec.slug}" id="${operation.key}" />`),
3148
- data: {
3149
- ...flags,
3150
- search: { ...searchFlags, tags },
3151
- seo,
3152
- sidebar,
3153
- title,
3154
- type: "openapi-operation"
1460
+ case "embedded-asset-block": {
1461
+ const asset = linkedAsset(node, options);
1462
+ return asset ? image(asset.description ?? asset.title ?? "", asset.url) : "";
3155
1463
  }
3156
- };
3157
- };
3158
- var overviewMdx = (spec, reference) => {
3159
- const operations = Object.values(spec.operations);
3160
- const sections = [];
3161
- const known = new Set;
3162
- for (const tag of spec.tags) {
3163
- if (!known.has(tag.slug)) {
3164
- known.add(tag.slug);
3165
- sections.push(tag);
3166
- }
3167
- }
3168
- for (const operation of operations) {
3169
- if (!known.has(operation.tagSlug)) {
3170
- known.add(operation.tagSlug);
3171
- sections.push({
3172
- description: "",
3173
- name: operation.tag,
3174
- slug: operation.tagSlug
3175
- });
1464
+ case "embedded-entry-block": {
1465
+ return embeddedEntry(node, options);
3176
1466
  }
3177
- }
3178
- const tagSections = [];
3179
- for (const tag of sections) {
3180
- if (!operations.some((operation) => operation.tagSlug === tag.slug)) {
3181
- continue;
1467
+ case "table": {
1468
+ return renderTable(node, options);
1469
+ }
1470
+ default: {
1471
+ return unsupported(`Contentful node: ${type}`, writesMdx(options.serializers));
3182
1472
  }
3183
- const description = tag.description.trim() ? [mdxSafe(tag.description.trim())] : [];
3184
- tagSections.push([
3185
- `## ${mdxSafe(tag.name)}`,
3186
- ...description,
3187
- `<ApiTagOperations source="${spec.slug}" tag="${tag.slug}" />`
3188
- ].join(`
3189
-
3190
- `));
3191
- }
3192
- const flags = {};
3193
- if (reference?.includeInLlms === false) {
3194
- flags.ai = { exclude: true };
3195
- }
3196
- if (reference?.includeInSearch === false) {
3197
- flags.search = { exclude: true };
3198
- }
3199
- const seo = {
3200
- description: clip(plainProse(spec.description), META_DESCRIPTION_MAX) || `${apiPhrase(spec)} reference.`
3201
- };
3202
- if (reference?.noindex) {
3203
- seo.noindex = true;
3204
1473
  }
3205
- return {
3206
- body: [
3207
- withDescription(spec.description, `<ApiOverview source="${spec.slug}" />`),
3208
- ...tagSections
3209
- ].join(`
1474
+ };
1475
+ var contentfulRichTextToMarkdown = (doc, options = {}) => markdownDocument(children(doc).map((node) => renderBlock(node, options)));
3210
1476
 
3211
- `),
3212
- data: {
3213
- ...flags,
3214
- seo,
3215
- sidebar: { label: "Overview" },
3216
- title: apiName(spec)
1477
+ // src/core/sources/remote.ts
1478
+ import { join } from "pathe";
1479
+ var remoteSource = (options, ctx) => {
1480
+ const cache = snapshotCache(ctx?.cacheDir ?? join(".blume", "cache", options.name));
1481
+ let snapshot = new Map;
1482
+ const load = async (refresh = ctx?.refresh ?? true) => {
1483
+ const result = await loadWithCache(options.name, cache, options.fetchEntries, refresh);
1484
+ snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
1485
+ return result;
1486
+ };
1487
+ const read = async (ref) => {
1488
+ const cached = snapshot.get(ref);
1489
+ if (cached) {
1490
+ return cached.raw ?? cached.body.text;
3217
1491
  }
1492
+ const all = await cache.read();
1493
+ return all.find((entry) => entry.ref === ref)?.raw ?? "";
1494
+ };
1495
+ return {
1496
+ load,
1497
+ name: options.name,
1498
+ prefix: options.prefix,
1499
+ read,
1500
+ staged: true,
1501
+ watch: options.pollInterval ? pollingWatch(() => load(true), options.pollInterval, () => load()) : undefined
3218
1502
  };
3219
1503
  };
3220
-
3221
- // src/openapi/source.ts
3222
- var isOpenApiSource = (source) => ("kind" in source) && source.kind === "openapi-source";
3223
- var routeToRef = (route) => route.replace(/^\/+/u, "");
3224
- var KIND_LABELS = {
3225
- asyncapi: "AsyncAPI",
3226
- graphql: "GraphQL",
3227
- openapi: "OpenAPI"
3228
- };
3229
- var CODE_PREFIXES = {
3230
- asyncapi: "BLUME_ASYNCAPI",
3231
- graphql: "BLUME_GRAPHQL",
3232
- openapi: "BLUME_OPENAPI"
3233
- };
3234
- var SPEC_WARNING_CODES = {
3235
- asyncapi: "BLUME_ASYNCAPI_SPEC_WARNING",
3236
- graphql: "BLUME_GRAPHQL_SPEC_WARNING",
3237
- openapi: "BLUME_OPENAPI_STALE"
3238
- };
3239
- var SKIPPED_CODES = {
3240
- asyncapi: "BLUME_ASYNCAPI_SKIPPED_OPERATION",
3241
- graphql: "BLUME_GRAPHQL_SKIPPED",
3242
- openapi: "BLUME_OPENAPI_REF_PATH_ITEM"
3243
- };
3244
- var EMPTY_SUGGESTIONS = {
3245
- asyncapi: "Check the spec points at an AsyncAPI document with `channels` and `operations`.",
3246
- graphql: "Check the spec points at a GraphQL schema (SDL or introspection JSON) whose root types declare fields.",
3247
- openapi: "Check the spec points at an OpenAPI document with operations under `paths`."
3248
- };
3249
- var INVALID_SUGGESTIONS = {
3250
- asyncapi: "Point the spec at an AsyncAPI document (a YAML or JSON file with an object at the top level).",
3251
- graphql: "Point the spec at a GraphQL schema — SDL text or an introspection JSON result.",
3252
- openapi: "Point the spec at an OpenAPI document (a YAML or JSON file with an object at the top level)."
3253
- };
3254
- var toEntry = (rendered, ref) => {
3255
- const raw = frontmatter_default.stringify(`${rendered.body}
3256
- `, rendered.data);
1504
+ var stagedEntry = (slug, data, markdown, lastModified, format = "md") => {
1505
+ const raw = frontmatter_default.stringify(markdown, data);
3257
1506
  return {
3258
- body: { format: "mdx", text: rendered.body },
3259
- data: { ...rendered.data },
1507
+ body: { format, text: markdown },
1508
+ data: { ...data },
3260
1509
  hash: hashText(raw),
1510
+ lastModified,
3261
1511
  raw,
3262
- ref
1512
+ ref: `${slug}.${format}`
3263
1513
  };
3264
1514
  };
3265
- var specEntries = (spec, operations, reference) => {
3266
- const entries = operations.map((operation) => toEntry(operationMdx(spec, operation, reference), `${routeToRef(operation.route)}.mdx`));
3267
- const base = routeToRef(spec.route);
3268
- entries.push(toEntry(overviewMdx(spec, reference), base ? `${base}/index.mdx` : "index.mdx"));
3269
- return entries;
3270
- };
3271
- var tagFolderMeta = (spec, tags) => {
3272
- const base = routeToRef(spec.route);
3273
- return Object.fromEntries(tags.map((tag) => [
3274
- base ? `${base}/${tag.slug}` : tag.slug,
3275
- { title: tag.name }
3276
- ]));
1515
+ var REMOTE_TIMEOUT_MS = 30000;
1516
+ var fetchJson = async (url, client) => {
1517
+ const doFetch = client.fetchImpl ?? globalThis.fetch;
1518
+ const res = await doFetch(url, {
1519
+ headers: { accept: "application/json", ...client.headers },
1520
+ signal: AbortSignal.timeout(REMOTE_TIMEOUT_MS)
1521
+ });
1522
+ if (!res.ok) {
1523
+ throw new Error(`${url} responded ${res.status} ${res.statusText}`.trim());
1524
+ }
1525
+ return await res.json();
3277
1526
  };
3278
- var parseReference = async (reference, ctx) => {
3279
- const options = { cacheDir: ctx.cacheDir, refresh: ctx.refresh };
3280
- if (reference.kind === "asyncapi") {
3281
- const { document, warnings } = await parseAsyncApiSpec(reference.spec, ctx.projectRoot, options);
3282
- const extracted = extractAsyncApiOperations(document, reference.route);
3283
- return {
3284
- document,
3285
- extractWarnings: extracted.warnings,
3286
- operations: extracted.operations,
3287
- tags: extracted.tags,
3288
- warnings
3289
- };
1527
+ var queryString = (params) => {
1528
+ const search = new URLSearchParams;
1529
+ for (const [key, value] of Object.entries(params)) {
1530
+ if (value !== undefined) {
1531
+ search.set(key, value);
1532
+ }
3290
1533
  }
3291
- if (reference.kind === "graphql") {
3292
- const { document, warnings } = await parseGraphqlSpec(reference.spec, ctx.projectRoot, options);
3293
- const extracted = extractGraphqlOperations(document, reference.route);
3294
- return {
3295
- document,
3296
- extractWarnings: extracted.warnings,
3297
- operations: extracted.operations,
3298
- tags: extracted.tags,
3299
- warnings
3300
- };
1534
+ return search.toString();
1535
+ };
1536
+ var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false) => {
1537
+ const slugValue = asString(getPath(doc, fields.slug)) ?? id;
1538
+ const slug = slugifyPath(slugValue) || slugify(id) || "untitled";
1539
+ const data = {};
1540
+ const title = asString(getPath(doc, fields.title));
1541
+ const description = asString(getPath(doc, fields.description));
1542
+ if (title) {
1543
+ data.title = title;
3301
1544
  }
3302
- const { document, warnings } = await parseSpec(reference.spec, ctx.projectRoot, options);
3303
- const extracted = extractOperations(document, reference.route);
3304
- return {
3305
- document,
3306
- extractWarnings: extracted.warnings,
3307
- operations: extracted.operations,
3308
- tags: extracted.tags,
3309
- warnings
3310
- };
1545
+ if (description) {
1546
+ data.description = description;
1547
+ }
1548
+ if (draft) {
1549
+ data.draft = true;
1550
+ }
1551
+ const body = getPath(doc, fields.body);
1552
+ let markdown = "";
1553
+ let format = "md";
1554
+ if (isStringValue2(body)) {
1555
+ markdown = `${body.trimEnd()}
1556
+ `;
1557
+ } else if (body !== undefined) {
1558
+ markdown = lower(body);
1559
+ format = lowersToMdx ? "mdx" : "md";
1560
+ }
1561
+ return stagedEntry(slug, data, markdown, asString(getPath(doc, fields.lastModified)), format);
3311
1562
  };
3312
- var openApiSource = (references, ctx) => {
3313
- let parsed = {};
3314
- const loadReference = async (reference) => {
3315
- const kindLabel = KIND_LABELS[reference.kind];
3316
- const codePrefix = CODE_PREFIXES[reference.kind];
3317
- try {
3318
- const { document, warnings, operations, tags, extractWarnings } = await parseReference(reference, ctx);
3319
- const info = document.info ?? { title: reference.label, version: "" };
3320
- const { proxy: configuredProxy } = reference.display.playground;
3321
- let proxy = false;
3322
- if (configuredProxy === true) {
3323
- proxy = withBasePath(reference.basePath, "/_api-proxy");
3324
- } else if (configuredProxy !== false && configuredProxy !== "") {
3325
- proxy = configuredProxy;
3326
- }
3327
- const spec = {
3328
- codeSamples: reference.display.codeSamples,
3329
- description: info.description ?? "",
3330
- document,
3331
- expandSchemas: reference.display.expandSchemas,
3332
- kind: reference.kind,
3333
- label: reference.label,
3334
- operations: Object.fromEntries(operations.map((operation) => [
3335
- operation.key,
3336
- {
3337
- ...operation,
3338
- route: withBasePath(reference.basePath, operation.route)
3339
- }
3340
- ])),
3341
- playground: { enabled: reference.display.playground.enabled, proxy },
3342
- route: reference.route,
3343
- slug: reference.slug,
3344
- tags,
3345
- title: info.title ?? reference.label,
3346
- version: info.version ?? ""
3347
- };
3348
- if (reference.endpoint !== undefined) {
3349
- spec.endpoint = reference.endpoint;
3350
- }
3351
- return {
3352
- diagnostics: [
3353
- ...warnings.map((message) => ({
3354
- code: SPEC_WARNING_CODES[reference.kind],
3355
- message,
3356
- severity: "warning"
3357
- })),
3358
- ...extractWarnings.map((message) => ({
3359
- code: SKIPPED_CODES[reference.kind],
3360
- message: `In ${kindLabel} spec "${reference.spec}": ${message}`,
3361
- severity: "warning"
3362
- })),
3363
- ...operations.length === 0 ? [
3364
- {
3365
- code: `${codePrefix}_EMPTY`,
3366
- message: `${kindLabel} spec "${reference.spec}" for ${reference.route} declares no operations; its API reference is empty.`,
3367
- severity: "warning",
3368
- suggestion: EMPTY_SUGGESTIONS[reference.kind]
3369
- }
3370
- ] : []
3371
- ],
3372
- entries: specEntries(spec, operations, reference),
3373
- folderMeta: tagFolderMeta(spec, tags),
3374
- slug: reference.slug,
3375
- spec
3376
- };
3377
- } catch (error) {
3378
- return {
3379
- code: `${codePrefix}_UNAVAILABLE`,
3380
- message: `Could not load ${kindLabel} spec "${reference.spec}" for ${reference.route} (${error.message}); its reference pages were skipped.`,
3381
- severity: ctx.mode === "build" ? "error" : "warning",
3382
- suggestion: error instanceof InvalidSpecError ? INVALID_SUGGESTIONS[reference.kind] : "Check the spec URL/path is reachable from the build environment; behind a proxy, set HTTP(S)_PROXY."
3383
- };
3384
- }
1563
+
1564
+ // src/core/sources/contentful.ts
1565
+ var DELIVERY_HOST = "https://cdn.contentful.com";
1566
+ var PREVIEW_HOST = "https://preview.contentful.com";
1567
+ var PAGE_SIZE = 100;
1568
+ var DEFAULT_FIELDS = {
1569
+ body: "body",
1570
+ description: "description",
1571
+ lastModified: "sys.updatedAt",
1572
+ slug: "slug",
1573
+ title: "title"
1574
+ };
1575
+ var byId = (objects) => new Map(objects.map((object) => [asString(getPath(object, "sys.id")) ?? "", object]));
1576
+ var contentfulSource = (options, ctx) => {
1577
+ const fields = { ...DEFAULT_FIELDS, ...options.fields };
1578
+ const toEntry = (item, assets, linked) => {
1579
+ const sys = asObject(item.sys) ?? {};
1580
+ const id = asString(sys.id) ?? "";
1581
+ const view = { ...asObject(item.fields), sys };
1582
+ return documentEntry(view, fields, id, (body) => isJsonObject(body) ? contentfulRichTextToMarkdown(body, {
1583
+ resolveAsset: (assetId) => {
1584
+ const asset = assets.get(assetId);
1585
+ return asset ? assetFromEntry(asset) : null;
1586
+ },
1587
+ resolveEntry: (entryId) => linked.get(entryId) ?? null,
1588
+ serializers: options.serializers
1589
+ }) : "", false, writesMdx(options.serializers));
3385
1590
  };
3386
- const load = async () => {
3387
- const results = await Promise.all(references.map(loadReference));
1591
+ const fetchEntries = async () => {
1592
+ const preview = ctx?.preview ?? false;
1593
+ const deliveryToken = options.token ?? process.env.CONTENTFUL_ACCESS_TOKEN;
1594
+ const previewToken = options.previewToken ?? process.env.CONTENTFUL_PREVIEW_TOKEN;
1595
+ if (preview && !previewToken) {
1596
+ throw new Error("Contentful --preview needs a Preview API token: set previewToken or CONTENTFUL_PREVIEW_TOKEN.");
1597
+ }
1598
+ const token = preview ? previewToken : deliveryToken;
1599
+ const client = {
1600
+ fetchImpl: options.fetchImpl,
1601
+ headers: token ? { authorization: `Bearer ${token}` } : {}
1602
+ };
1603
+ const host = preview ? PREVIEW_HOST : DELIVERY_HOST;
1604
+ const base = `${host}/spaces/${options.space}/environments/${options.environment ?? "master"}/entries`;
3388
1605
  const entries = [];
3389
- const diagnostics = references.flatMap((reference) => (reference.collisions ?? []).map((message) => ({
3390
- code: "BLUME_OPENAPI_ROUTE_COLLISION",
3391
- message,
3392
- severity: "warning"
3393
- })));
3394
- const data = {};
3395
- const folderMeta = {};
3396
- for (const result of results) {
3397
- if ("severity" in result) {
3398
- diagnostics.push(result);
3399
- continue;
1606
+ let skip = 0;
1607
+ let more = true;
1608
+ while (more) {
1609
+ const query = queryString({
1610
+ ...options.params,
1611
+ content_type: options.contentType,
1612
+ include: "2",
1613
+ limit: String(PAGE_SIZE),
1614
+ locale: options.locale,
1615
+ skip: String(skip)
1616
+ });
1617
+ const page = asObject(await fetchJson(`${base}?${query}`, client));
1618
+ if (!page) {
1619
+ throw new Error("Contentful returned a non-object response");
3400
1620
  }
3401
- data[result.slug] = result.spec;
3402
- entries.push(...result.entries);
3403
- Object.assign(folderMeta, result.folderMeta);
3404
- diagnostics.push(...result.diagnostics);
1621
+ const assets = byId(objectsIn(getPath(page, "includes.Asset")));
1622
+ const linked = byId(objectsIn(getPath(page, "includes.Entry")));
1623
+ const items = objectsIn(page.items);
1624
+ for (const item of items) {
1625
+ entries.push(toEntry(item, assets, linked));
1626
+ }
1627
+ skip += items.length;
1628
+ more = items.length > 0 && skip < (asNumber(page.total) ?? 0);
3405
1629
  }
3406
- parsed = data;
3407
- return { diagnostics, entries, folderMeta };
3408
- };
3409
- return {
3410
- kind: "openapi-source",
3411
- load,
3412
- name: "openapi",
3413
- openApiData: () => parsed,
3414
- staged: true
1630
+ return entries;
3415
1631
  };
1632
+ return remoteSource({
1633
+ fetchEntries,
1634
+ name: options.name,
1635
+ pollInterval: options.pollInterval,
1636
+ prefix: options.prefix
1637
+ }, ctx);
3416
1638
  };
3417
1639
 
3418
1640
  // src/core/sources/filesystem.ts
3419
- import { existsSync as existsSync2, watch as fsWatch } from "node:fs";
3420
- import { readFile as readFile4 } from "node:fs/promises";
3421
- import { extname as extname4, isAbsolute as isAbsolute2, join as join4, relative as relative4, resolve as resolve2 } from "pathe";
1641
+ import { existsSync, watch as fsWatch } from "node:fs";
1642
+ import { readFile } from "node:fs/promises";
1643
+ import { extname as extname2, isAbsolute, join as join2, relative as relative3, resolve } from "pathe";
3422
1644
  import { glob as glob2 } from "tinyglobby";
3423
-
3424
- // src/core/sources/watch.ts
3425
- var BLUME_IGNORE_DIRS = [
3426
- ".blume",
3427
- ".blume-verify",
3428
- ".cache",
3429
- ".git",
3430
- ".next",
3431
- ".turbo",
3432
- ".vercel",
3433
- "dist",
3434
- "node_modules"
3435
- ];
3436
- var baselineScanIgnore = () => BLUME_IGNORE_DIRS.map((dir) => `**/${dir}/**`);
3437
- var BLUME_WATCH_IGNORE_DIRS = BLUME_IGNORE_DIRS;
3438
- var excludeDirSegments = (patterns) => patterns.map((pattern) => /^(?<dir>[^*/]+)\/\*\*$/u.exec(pattern)?.groups?.dir).filter((dir) => dir !== undefined);
3439
- var ignoringWatchListener = (onChange, ignoreDirs = BLUME_WATCH_IGNORE_DIRS, ignoreSegment = () => false) => {
3440
- const ignore = new Set(ignoreDirs);
3441
- return (_event, filename) => {
3442
- if (filename !== null && filename.split(/[/\\]/u).some((segment) => ignore.has(segment) || ignoreSegment(segment))) {
3443
- return;
3444
- }
3445
- onChange();
3446
- };
3447
- };
3448
-
3449
- // src/core/sources/filesystem.ts
3450
1645
  var filesystemSource = (options) => {
3451
- const contentRoot = isAbsolute2(options.root) ? options.root : join4(resolve2(options.projectRoot), options.root);
1646
+ const contentRoot = isAbsolute(options.root) ? options.root : join2(resolve(options.projectRoot), options.root);
3452
1647
  const load = async () => {
3453
1648
  const files = await glob2(options.include, {
3454
1649
  absolute: true,
@@ -3458,22 +1653,22 @@ var filesystemSource = (options) => {
3458
1653
  });
3459
1654
  files.sort();
3460
1655
  const entries = await Promise.all(files.map(async (file) => {
3461
- const source = await readFile4(file, "utf-8");
3462
- const ext = extname4(file).toLowerCase();
1656
+ const source = await readFile(file, "utf-8");
1657
+ const ext = extname2(file).toLowerCase();
3463
1658
  const format = ext === ".mdx" ? "mdx" : "md";
3464
1659
  const parsed = frontmatter_default(source);
3465
1660
  return {
3466
1661
  body: { format, text: parsed.content },
3467
1662
  data: parsed.data,
3468
1663
  raw: source,
3469
- ref: relative4(contentRoot, file),
1664
+ ref: relative3(contentRoot, file),
3470
1665
  sourcePath: file
3471
1666
  };
3472
1667
  }));
3473
1668
  return { diagnostics: [], entries };
3474
1669
  };
3475
1670
  const validate = () => {
3476
- if (!existsSync2(contentRoot)) {
1671
+ if (!existsSync(contentRoot)) {
3477
1672
  throw new BlumeError({
3478
1673
  code: options.missingCode ?? "BLUME_CONTENT_ROOT_MISSING",
3479
1674
  file: contentRoot,
@@ -3488,7 +1683,7 @@ var filesystemSource = (options) => {
3488
1683
  ...excludeDirSegments(options.exclude)
3489
1684
  ]);
3490
1685
  const watch = (onChange) => {
3491
- if (!existsSync2(contentRoot)) {
1686
+ if (!existsSync(contentRoot)) {
3492
1687
  return () => {};
3493
1688
  }
3494
1689
  const watcher = fsWatch(contentRoot, { recursive: true }, ignoringWatchListener(onChange, watchIgnoreDirs));
@@ -3499,7 +1694,7 @@ var filesystemSource = (options) => {
3499
1694
  load,
3500
1695
  name: options.name,
3501
1696
  prefix: options.prefix,
3502
- read: (ref) => readFile4(join4(contentRoot, ref), "utf-8"),
1697
+ read: (ref) => readFile(join2(contentRoot, ref), "utf-8"),
3503
1698
  staged: false,
3504
1699
  validate,
3505
1700
  watch
@@ -3507,11 +1702,11 @@ var filesystemSource = (options) => {
3507
1702
  };
3508
1703
 
3509
1704
  // src/core/sources/github-releases.ts
3510
- import { fromMarkdown as fromMarkdown2 } from "mdast-util-from-markdown";
1705
+ import { fromMarkdown } from "mdast-util-from-markdown";
3511
1706
  import { gfmFromMarkdown } from "mdast-util-gfm";
3512
- import { toString as mdastToString2 } from "mdast-util-to-string";
1707
+ import { toString as mdastToString } from "mdast-util-to-string";
3513
1708
  import { gfm } from "micromark-extension-gfm";
3514
- import stringWidth3 from "string-width";
1709
+ import stringWidth from "string-width";
3515
1710
  var DEFAULT_LIMIT = 100;
3516
1711
  var PER_PAGE = 100;
3517
1712
  var LEADING_V = /^v/iu;
@@ -3520,26 +1715,54 @@ var EDGE_DASHES = /^-+|-+$/gu;
3520
1715
  var DESCRIPTION_MAX = 160;
3521
1716
  var DESCRIPTION_MIN = 110;
3522
1717
  var CHANGESET_HASH = /^(?<mark>\s*(?:[-*+]|\d+[.)])\s+)[0-9a-f]{7,40}:\s+/gmu;
3523
- var WHITESPACE2 = /\s+/gu;
1718
+ var WHITESPACE = /\s+/gu;
3524
1719
  var TRAILING_FRAGMENT = /[\s,;:.—–-]+$/u;
3525
1720
  var NON_PROSE = new Set(["code", "heading", "html", "thematicBreak"]);
3526
1721
  var releaseDescription = (body) => {
3527
- const tree = fromMarkdown2(body.replaceAll(CHANGESET_HASH, "$<mark>"), {
1722
+ const tree = fromMarkdown(body.replaceAll(CHANGESET_HASH, "$<mark>"), {
3528
1723
  extensions: [gfm()],
3529
1724
  mdastExtensions: [gfmFromMarkdown()]
3530
1725
  });
3531
- const text = tree.children.filter((node) => !NON_PROSE.has(node.type)).map((node) => mdastToString2(node, { includeHtml: false, includeImageAlt: false })).join(" ").replaceAll(WHITESPACE2, " ").trim();
1726
+ const text = tree.children.filter((node) => !NON_PROSE.has(node.type)).map((node) => mdastToString(node, { includeHtml: false, includeImageAlt: false })).join(" ").replaceAll(WHITESPACE, " ").trim();
3532
1727
  if (!text) {
3533
1728
  return;
3534
1729
  }
3535
- if (stringWidth3(text) <= DESCRIPTION_MAX) {
1730
+ if (stringWidth(text) <= DESCRIPTION_MAX) {
3536
1731
  return text;
3537
1732
  }
3538
1733
  const slice = columnsPrefix(text, DESCRIPTION_MAX - 1);
3539
1734
  const boundary = slice.lastIndexOf(" ");
3540
- const head = (boundary !== -1 && stringWidth3(slice.slice(0, boundary)) >= DESCRIPTION_MIN ? slice.slice(0, boundary) : slice).replace(TRAILING_FRAGMENT, "");
1735
+ const head = (boundary !== -1 && stringWidth(slice.slice(0, boundary)) >= DESCRIPTION_MIN ? slice.slice(0, boundary) : slice).replace(TRAILING_FRAGMENT, "");
3541
1736
  return `${head}…`;
3542
1737
  };
1738
+ var headingsIn = (node) => {
1739
+ if (node.type === "heading") {
1740
+ return [node];
1741
+ }
1742
+ const found = [];
1743
+ if ("children" in node) {
1744
+ for (const child of node.children) {
1745
+ found.push(...headingsIn(child));
1746
+ }
1747
+ }
1748
+ return found;
1749
+ };
1750
+ var liftHeadings = (body) => {
1751
+ const headings = headingsIn(fromMarkdown(body, {
1752
+ extensions: [gfm()],
1753
+ mdastExtensions: [gfmFromMarkdown()]
1754
+ }));
1755
+ const lift = Math.min(...headings.map((heading) => heading.depth)) - 2;
1756
+ if (headings.length === 0 || lift <= 0) {
1757
+ return body;
1758
+ }
1759
+ let lifted = body;
1760
+ for (const heading of headings.toReversed()) {
1761
+ const marker = lifted.indexOf("#", heading.position?.start.offset);
1762
+ lifted = lifted.slice(0, marker) + lifted.slice(marker + lift);
1763
+ }
1764
+ return lifted;
1765
+ };
3543
1766
  var slugifyTag = (tag) => tag.toLowerCase().replaceAll(NON_SLUG, "-").replaceAll(EDGE_DASHES, "");
3544
1767
  var githubHeaders = () => {
3545
1768
  const headers = new Headers({ Accept: "application/vnd.github+json" });
@@ -3554,9 +1777,9 @@ var releaseToEntry = (release) => {
3554
1777
  const title = release.name?.trim() || release.tag_name;
3555
1778
  const date = release.published_at ?? release.created_at;
3556
1779
  const category = release.prerelease ? "Prerelease" : "Release";
3557
- const body = (release.body ?? "").replaceAll(`\r
1780
+ const body = liftHeadings(neutralizeUnsafeLinks((release.body ?? "").replaceAll(`\r
3558
1781
  `, `
3559
- `).trim();
1782
+ `)).trim());
3560
1783
  const description = releaseDescription(body);
3561
1784
  const data = {
3562
1785
  changelog: { category, version },
@@ -3642,6 +1865,7 @@ var githubReleasesSource = (options, ctx) => {
3642
1865
  };
3643
1866
  return {
3644
1867
  load,
1868
+ monolingual: true,
3645
1869
  name: options.name,
3646
1870
  prefix: options.prefix,
3647
1871
  read,
@@ -3794,36 +2018,22 @@ var mdxRemoteSource = (options, ctx) => {
3794
2018
  };
3795
2019
 
3796
2020
  // src/core/sources/notion.ts
3797
- import { setTimeout as sleep2 } from "node:timers/promises";
2021
+ import { setTimeout as sleep } from "node:timers/promises";
3798
2022
  import pLimit from "p-limit";
3799
- import { join as join6 } from "pathe";
3800
-
3801
- // src/components/content/youtube.ts
3802
- var BARE_ID = /^[\w-]{11}$/u;
3803
- var URL_ID = /(?:youtu\.be\/|\/embed\/|\/shorts\/|\/live\/|[?&]v=)(?<id>[\w-]{11})/u;
3804
- var parseYouTubeId = (input) => {
3805
- const value = input.trim();
3806
- if (!value) {
3807
- return null;
3808
- }
3809
- if (BARE_ID.test(value)) {
3810
- return value;
3811
- }
3812
- return URL_ID.exec(value)?.groups?.id ?? null;
3813
- };
2023
+ import { join as join4 } from "pathe";
3814
2024
 
3815
2025
  // src/core/sources/assets.ts
3816
2026
  import { randomUUID } from "node:crypto";
3817
2027
  import {
3818
2028
  access,
3819
- mkdir as mkdir3,
2029
+ mkdir,
3820
2030
  readdir,
3821
2031
  rename,
3822
2032
  rm,
3823
- writeFile as writeFile3
2033
+ writeFile
3824
2034
  } from "node:fs/promises";
3825
- import { extname as extname5, join as join5 } from "pathe";
3826
- var MD_IMAGE2 = /!\[(?<alt>[^\]]*)\]\((?<url>[^)\s]+)\)/gu;
2035
+ import { extname as extname3, join as join3 } from "pathe";
2036
+ var MD_IMAGE = /!\[(?<alt>[^\]]*)\]\((?<url>[^)\s]+)\)/gu;
3827
2037
  var HTML_VIDEO_SRC = /(?<open><video\b[^>]*?\ssrc=")(?<url>[^"]+)(?<close>")/gu;
3828
2038
  var REMOTE = /^https?:\/\//u;
3829
2039
  var SAFE_EXT = /^\.[a-z0-9]+$/iu;
@@ -3847,7 +2057,7 @@ var PART_SUFFIX = ".part";
3847
2057
  var DEFAULT_TIMEOUT_MS = 120000;
3848
2058
  var extFromUrl = (url) => {
3849
2059
  const clean = url.split("?")[0] ?? url;
3850
- const ext = extname5(clean);
2060
+ const ext = extname3(clean);
3851
2061
  return SAFE_EXT.test(ext) ? ext.toLowerCase() : null;
3852
2062
  };
3853
2063
  var mediaType = (res) => res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() ?? "";
@@ -3872,6 +2082,7 @@ var completedFor = async (dir, stem) => {
3872
2082
  var materializeAssets = async (markdown, ctx) => {
3873
2083
  const doFetch = ctx.fetchImpl ?? globalThis.fetch;
3874
2084
  const limit = ctx.limit ?? ((task) => task());
2085
+ const inFlight = ctx.inFlight ?? new Map;
3875
2086
  const timeoutMs = ctx.timeoutMs ?? DEFAULT_TIMEOUT_MS;
3876
2087
  const diagnostics = [];
3877
2088
  const fences = [];
@@ -3880,7 +2091,7 @@ var materializeAssets = async (markdown, ctx) => {
3880
2091
  return `\x00blume-fence-${fences.length - 1}\x00`;
3881
2092
  });
3882
2093
  const urls = new Set;
3883
- for (const pattern of [MD_IMAGE2, HTML_VIDEO_SRC]) {
2094
+ for (const pattern of [MD_IMAGE, HTML_VIDEO_SRC]) {
3884
2095
  for (const match of masked.matchAll(pattern)) {
3885
2096
  const url = match.groups?.url;
3886
2097
  if (url && REMOTE.test(url)) {
@@ -3888,10 +2099,9 @@ var materializeAssets = async (markdown, ctx) => {
3888
2099
  }
3889
2100
  }
3890
2101
  }
3891
- const download = async (url) => {
3892
- const stem = hashText(url.split("?")[0] ?? url);
2102
+ const download = async (url, stem) => {
3893
2103
  const urlExt = extFromUrl(url);
3894
- if (urlExt && await exists(join5(ctx.assetsDir, `${stem}${urlExt}`))) {
2104
+ if (urlExt && await exists(join3(ctx.assetsDir, `${stem}${urlExt}`))) {
3895
2105
  return `${stem}${urlExt}`;
3896
2106
  }
3897
2107
  const completed = urlExt ? null : await completedFor(ctx.assetsDir, stem);
@@ -3910,11 +2120,11 @@ var materializeAssets = async (markdown, ctx) => {
3910
2120
  throw new Error("empty response body");
3911
2121
  }
3912
2122
  const file = `${stem}${urlExt ?? EXT_BY_MIME.get(type) ?? UNKNOWN_EXT}`;
3913
- const target = join5(ctx.assetsDir, file);
3914
- await mkdir3(ctx.assetsDir, { recursive: true });
2123
+ const target = join3(ctx.assetsDir, file);
2124
+ await mkdir(ctx.assetsDir, { recursive: true });
3915
2125
  const part = `${target}.${randomUUID()}${PART_SUFFIX}`;
3916
2126
  try {
3917
- await writeFile3(part, res.body);
2127
+ await writeFile(part, res.body);
3918
2128
  } catch (error) {
3919
2129
  await rm(part, { force: true });
3920
2130
  throw error;
@@ -3922,10 +2132,30 @@ var materializeAssets = async (markdown, ctx) => {
3922
2132
  await rename(part, target);
3923
2133
  return file;
3924
2134
  };
2135
+ const publish = async (url) => {
2136
+ const stem = hashText(url.split("?")[0] ?? url);
2137
+ const pending = inFlight.get(stem);
2138
+ if (pending) {
2139
+ try {
2140
+ return await pending;
2141
+ } catch {
2142
+ return await publish(url);
2143
+ }
2144
+ }
2145
+ const own = (async () => {
2146
+ try {
2147
+ return await limit(() => download(url, stem));
2148
+ } finally {
2149
+ inFlight.delete(stem);
2150
+ }
2151
+ })();
2152
+ inFlight.set(stem, own);
2153
+ return await own;
2154
+ };
3925
2155
  const rewrites = new Map;
3926
2156
  await Promise.all([...urls].map(async (url) => {
3927
2157
  try {
3928
- const file = await limit(() => download(url));
2158
+ const file = await publish(url);
3929
2159
  rewrites.set(url, `${ctx.assetsBaseUrl}/${file}`);
3930
2160
  } catch (error) {
3931
2161
  diagnostics.push({
@@ -3935,7 +2165,7 @@ var materializeAssets = async (markdown, ctx) => {
3935
2165
  });
3936
2166
  }
3937
2167
  }));
3938
- const rewritten = masked.replaceAll(MD_IMAGE2, (match, alt, url) => {
2168
+ const rewritten = masked.replaceAll(MD_IMAGE, (match, alt, url) => {
3939
2169
  const local = rewrites.get(url);
3940
2170
  return local ? `![${alt}](${local})` : match;
3941
2171
  }).replaceAll(HTML_VIDEO_SRC, (match, open, url, close) => {
@@ -3946,22 +2176,12 @@ var materializeAssets = async (markdown, ctx) => {
3946
2176
  };
3947
2177
 
3948
2178
  // src/core/sources/notion.ts
3949
- var richToMarkdown = (rich = []) => rich.map((node) => {
3950
- let text = node.plain_text;
3951
- if (node.annotations?.code) {
3952
- text = `\`${text}\``;
3953
- }
3954
- if (node.annotations?.bold) {
3955
- text = `**${text}**`;
3956
- }
3957
- if (node.annotations?.italic) {
3958
- text = `*${text}*`;
3959
- }
3960
- if (node.annotations?.strikethrough) {
3961
- text = `~~${text}~~`;
3962
- }
3963
- return node.href ? `[${text}](${node.href})` : text;
3964
- }).join("");
2179
+ var richToMarkdown = (rich = []) => rich.map(({ annotations = {}, href, plain_text: text }) => renderLink(renderInline(text, {
2180
+ bold: annotations.bold,
2181
+ code: annotations.code,
2182
+ italic: annotations.italic,
2183
+ strike: annotations.strikethrough
2184
+ }), href ?? undefined)).join("");
3965
2185
  var isBlockPayload = (value) => typeof value === "object";
3966
2186
  var payloadOf = (block) => {
3967
2187
  const value = block[block.type];
@@ -3971,7 +2191,7 @@ var blockField = (block) => payloadOf(block)?.rich_text ?? [];
3971
2191
  var RATE_LIMITED = 429;
3972
2192
  var MAX_RETRIES = 4;
3973
2193
  var BASE_DELAY_MS = 500;
3974
- var SECOND_MS2 = 1000;
2194
+ var SECOND_MS = 1000;
3975
2195
  var DEFAULT_CONCURRENCY = 3;
3976
2196
  var ASSET_DOWNLOAD_CONCURRENCY = 4;
3977
2197
  var withNotionRetry = async (call, attempt = 0) => {
@@ -3983,8 +2203,8 @@ var withNotionRetry = async (call, attempt = 0) => {
3983
2203
  throw error;
3984
2204
  }
3985
2205
  const retryAfter = Number(error.headers?.["retry-after"]);
3986
- const wait = retryAfter > 0 ? retryAfter * SECOND_MS2 : BASE_DELAY_MS * 2 ** attempt * (1 + Math.random());
3987
- await sleep2(wait);
2206
+ const wait = retryAfter > 0 ? retryAfter * SECOND_MS : BASE_DELAY_MS * 2 ** attempt * (1 + Math.random());
2207
+ await sleep(wait);
3988
2208
  return withNotionRetry(call, attempt + 1);
3989
2209
  }
3990
2210
  };
@@ -4050,13 +2270,11 @@ var renderLeaf = (block) => {
4050
2270
  return "---";
4051
2271
  }
4052
2272
  case "code": {
4053
- return `\`\`\`${data.language ?? ""}
4054
- ${text}
4055
- \`\`\``;
2273
+ return codeFence(richToPlain(blockField(block)), data.language ?? "");
4056
2274
  }
4057
2275
  case "image": {
4058
2276
  const url = data.external?.url ?? data.file?.url;
4059
- return url ? `![${richToMarkdown(data.caption)}](${url})` : "";
2277
+ return url ? image(richToPlain(data.caption), url) : "";
4060
2278
  }
4061
2279
  case "video": {
4062
2280
  return renderVideo(data);
@@ -4070,18 +2288,19 @@ var notionSource = (options, ctx) => {
4070
2288
  const props = options.properties ?? {};
4071
2289
  const limit = pLimit(Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY));
4072
2290
  const downloads = pLimit(ASSET_DOWNLOAD_CONCURRENCY);
2291
+ const inFlight = new Map;
4073
2292
  const notionCall = (call) => withNotionRetry(() => limit(call));
4074
- const cache = snapshotCache(ctx?.cacheDir ?? join6(".blume", "cache", options.name));
4075
- const assetsDir = ctx?.assetsDir ?? join6(".blume", "public", "blume-assets", options.name);
2293
+ const cache = snapshotCache(ctx?.cacheDir ?? join4(".blume", "cache", options.name));
2294
+ const assetsDir = ctx?.assetsDir ?? join4(".blume", "public", "blume-assets", options.name);
4076
2295
  const assetsBaseUrl = ctx?.assetsBaseUrl ?? `/blume-assets/${options.name}`;
4077
2296
  let snapshot = new Map;
4078
- const resolveClient = async () => {
2297
+ const resolveClient = () => {
4079
2298
  if (options.client) {
4080
2299
  return options.client;
4081
2300
  }
4082
2301
  let Client;
4083
2302
  try {
4084
- const sdk = await import("@notionhq/client");
2303
+ const sdk = nodeRequire("@notionhq/client");
4085
2304
  ({ Client } = sdk);
4086
2305
  } catch {
4087
2306
  throw new BlumeError({
@@ -4109,7 +2328,7 @@ ${body}
4109
2328
  </Callout>`;
4110
2329
  }
4111
2330
  if (block.type === "toggle") {
4112
- const title = jsxString(richToMarkdown(blockField(block)));
2331
+ const title = jsxString(richToPlain(blockField(block)));
4113
2332
  return `<Accordion>
4114
2333
  <AccordionItem title=${title}>
4115
2334
  ${await children(block)}
@@ -4185,11 +2404,11 @@ ${nested}`;
4185
2404
  };
4186
2405
  const frontmatter = (page) => {
4187
2406
  const data = {};
4188
- const title = richToMarkdown(titleProperty(page)?.title);
2407
+ const title = richToPlain(titleProperty(page)?.title);
4189
2408
  if (title) {
4190
2409
  data.title = title;
4191
2410
  }
4192
- const description = richToMarkdown(page.properties[props.description ?? "Description"]?.rich_text);
2411
+ const description = richToPlain(page.properties[props.description ?? "Description"]?.rich_text);
4193
2412
  if (description) {
4194
2413
  data.description = description;
4195
2414
  }
@@ -4200,7 +2419,7 @@ ${nested}`;
4200
2419
  if (order !== undefined) {
4201
2420
  data.sidebar = { order };
4202
2421
  }
4203
- const slugProp = richToMarkdown(page.properties[props.slug ?? "Slug"]?.rich_text);
2422
+ const slugProp = richToPlain(page.properties[props.slug ?? "Slug"]?.rich_text);
4204
2423
  const slug = slugifyPath(slugProp || title) || page.id;
4205
2424
  return { data, slug };
4206
2425
  };
@@ -4211,6 +2430,7 @@ ${nested}`;
4211
2430
  assetsBaseUrl,
4212
2431
  assetsDir,
4213
2432
  fetchImpl: options.fetchImpl,
2433
+ inFlight,
4214
2434
  limit: downloads
4215
2435
  });
4216
2436
  const raw = frontmatter_default.stringify(assets.markdown, data);
@@ -4245,7 +2465,7 @@ ${nested}`;
4245
2465
  const load = async (refresh = ctx?.refresh ?? true) => {
4246
2466
  const assetDiagnostics = [];
4247
2467
  const result = await loadWithCache(options.name, cache, async () => {
4248
- const client = await resolveClient();
2468
+ const client = resolveClient();
4249
2469
  const dataSourceId = await resolveDataSource(client);
4250
2470
  const pages = await collectAll((cursor) => queryDataSource(client, dataSourceId, cursor));
4251
2471
  const built = await Promise.all(pages.map((page) => toEntry(client, page)));
@@ -4279,9 +2499,9 @@ ${nested}`;
4279
2499
  };
4280
2500
 
4281
2501
  // src/core/sources/obsidian.ts
4282
- import { existsSync as existsSync3, watch as fsWatch2, statSync } from "node:fs";
4283
- import { readdir as readdir2, readFile as readFile5, stat } from "node:fs/promises";
4284
- import { basename as basename2, isAbsolute as isAbsolute3, join as join7, relative as relative5, resolve as resolve3 } from "pathe";
2502
+ import { existsSync as existsSync2, watch as fsWatch2, statSync } from "node:fs";
2503
+ import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
2504
+ import { basename as basename2, isAbsolute as isAbsolute2, join as join5, relative as relative4, resolve as resolve2 } from "pathe";
4285
2505
  var MARKDOWN_FILE = /\.md$/iu;
4286
2506
  var OBSIDIAN_COMMENT = /%%.*?%%/gu;
4287
2507
  var WIKILINK = /(?<embed>!)?\[\[(?<target>(?:[^\][|#\n][^\]|#\n]*?)?)(?:#(?<heading>[^\]|\n]+?))?(?:\\?\|(?<alias>[^\]\n]+))?\]\]/gu;
@@ -4429,7 +2649,7 @@ var transformChunk = (chunk, index, self, unresolved) => chunk.replaceAll(OBSIDI
4429
2649
  return `[${label}](${href}#${anchor})`;
4430
2650
  });
4431
2651
  var transformProse = (text, index, self, unresolved) => splitLiterals(text).map((chunk) => chunk.literal ? chunk.text : transformChunk(chunk.text, index, self, unresolved)).join("");
4432
- var INDENTED_CODE2 = /^(?: {4}|\t)/u;
2652
+ var INDENTED_CODE = /^(?: {4}|\t)/u;
4433
2653
  var LIST_ITEM = /^ {0,3}(?:[-+*]|\d{1,9}[.)])(?: |$)/u;
4434
2654
  var indentOf = (line) => line.length - line.trimStart().length;
4435
2655
  var listAfter = (line, inList) => {
@@ -4442,7 +2662,7 @@ var listAfter = (line, inList) => {
4442
2662
  return line.startsWith(" ") || line.startsWith("\t") ? inList : false;
4443
2663
  };
4444
2664
  var fenceAfter = (line, fence, fenceIndent, inList) => {
4445
- const canDelimit = fence === null ? inList || !INDENTED_CODE2.test(line) : indentOf(line) <= fenceIndent + 3;
2665
+ const canDelimit = fence === null ? inList || !INDENTED_CODE.test(line) : indentOf(line) <= fenceIndent + 3;
4446
2666
  return canDelimit ? nextFenceState(line, fence) : fence;
4447
2667
  };
4448
2668
  var transformBody = (body, index, self, unresolved) => {
@@ -4464,7 +2684,7 @@ var transformBody = (body, index, self, unresolved) => {
4464
2684
  `)) {
4465
2685
  const blank = line.trim() === "";
4466
2686
  if (fence === null && indentedCode) {
4467
- if (blank || INDENTED_CODE2.test(line)) {
2687
+ if (blank || INDENTED_CODE.test(line)) {
4468
2688
  out.push(line);
4469
2689
  afterBlank = blank;
4470
2690
  continue;
@@ -4472,7 +2692,7 @@ var transformBody = (body, index, self, unresolved) => {
4472
2692
  indentedCode = false;
4473
2693
  }
4474
2694
  inList = listAfter(line, inList);
4475
- if (fence === null && afterBlank && !(blank || inList) && INDENTED_CODE2.test(line)) {
2695
+ if (fence === null && afterBlank && !(blank || inList) && INDENTED_CODE.test(line)) {
4476
2696
  flush();
4477
2697
  indentedCode = true;
4478
2698
  out.push(line);
@@ -4596,9 +2816,9 @@ var noteToEntry = (pair, index, options, unresolved) => {
4596
2816
  };
4597
2817
  };
4598
2818
  var readNote = async (vaultDir, rel, options) => {
4599
- const absPath = join7(vaultDir, rel);
2819
+ const absPath = join5(vaultDir, rel);
4600
2820
  try {
4601
- const { content, data } = frontmatter_default(await readFile5(absPath, "utf-8"));
2821
+ const { content, data } = frontmatter_default(await readFile2(absPath, "utf-8"));
4602
2822
  return {
4603
2823
  absPath,
4604
2824
  content,
@@ -4623,7 +2843,7 @@ var classify = async (dir, entry) => {
4623
2843
  };
4624
2844
  }
4625
2845
  try {
4626
- const target = await stat(join7(dir, entry.name));
2846
+ const target = await stat(join5(dir, entry.name));
4627
2847
  return {
4628
2848
  directory: target.isDirectory(),
4629
2849
  file: target.isFile(),
@@ -4643,11 +2863,11 @@ var walkVault = async (dir, root, exclude) => {
4643
2863
  const listed = await readdir2(dir, { withFileTypes: true });
4644
2864
  const entries = await Promise.all(listed.filter((entry) => !(isHidden(entry.name) || exclude.has(entry.name))).map((entry) => classify(dir, entry)));
4645
2865
  for (const entry of entries.toSorted(byVaultOrder)) {
4646
- const full = join7(dir, entry.name);
2866
+ const full = join5(dir, entry.name);
4647
2867
  if (entry.directory) {
4648
2868
  found.push(...await walkVault(full, root, exclude));
4649
2869
  } else if (entry.file && MARKDOWN_FILE.test(entry.name)) {
4650
- found.push(relative5(root, full));
2870
+ found.push(relative4(root, full));
4651
2871
  }
4652
2872
  }
4653
2873
  return found;
@@ -4683,7 +2903,7 @@ var unresolvedDiagnostics = (name, unresolved) => {
4683
2903
  return diagnostics;
4684
2904
  };
4685
2905
  var obsidianSource = (options, ctx) => {
4686
- const vaultDir = resolve3(ctx.projectRoot, options.vault);
2906
+ const vaultDir = resolve2(ctx.projectRoot, options.vault);
4687
2907
  const exclude = new Set([...BLUME_IGNORE_DIRS, ...options.exclude ?? []]);
4688
2908
  const load = async () => {
4689
2909
  const files = await walkVault(vaultDir, vaultDir, exclude);
@@ -4698,9 +2918,9 @@ var obsidianSource = (options, ctx) => {
4698
2918
  };
4699
2919
  };
4700
2920
  const read = async (ref) => {
4701
- const absPath = resolve3(vaultDir, ref);
4702
- const rel = relative5(vaultDir, absPath);
4703
- if (rel.startsWith("..") || isAbsolute3(rel)) {
2921
+ const absPath = resolve2(vaultDir, ref);
2922
+ const rel = relative4(vaultDir, absPath);
2923
+ if (rel.startsWith("..") || isAbsolute2(rel)) {
4704
2924
  throw new BlumeError({
4705
2925
  code: "BLUME_SOURCE_MISCONFIGURED",
4706
2926
  file: absPath,
@@ -4709,7 +2929,7 @@ var obsidianSource = (options, ctx) => {
4709
2929
  suggestion: "Reference notes by their vault-relative path."
4710
2930
  });
4711
2931
  }
4712
- return await readFile5(absPath, "utf-8");
2932
+ return await readFile2(absPath, "utf-8");
4713
2933
  };
4714
2934
  const validate = () => {
4715
2935
  const stats = statSync(vaultDir, { throwIfNoEntry: false });
@@ -4726,7 +2946,7 @@ var obsidianSource = (options, ctx) => {
4726
2946
  });
4727
2947
  };
4728
2948
  const watch = (onChange) => {
4729
- if (!existsSync3(vaultDir)) {
2949
+ if (!existsSync2(vaultDir)) {
4730
2950
  return () => {};
4731
2951
  }
4732
2952
  const watcher = fsWatch2(vaultDir, { recursive: true }, ignoringWatchListener(onChange, exclude, isHidden));
@@ -4744,8 +2964,193 @@ var obsidianSource = (options, ctx) => {
4744
2964
  };
4745
2965
  };
4746
2966
 
2967
+ // src/core/sources/lexical.ts
2968
+ var IS_BOLD = 1;
2969
+ var IS_ITALIC = 2;
2970
+ var IS_STRIKETHROUGH = 4;
2971
+ var IS_CODE = 16;
2972
+ var hasFlag = (format, flag) => Math.floor(format / flag) % 2 === 1;
2973
+ var marksOf2 = (node) => {
2974
+ const format = asNumber(node.format) ?? 0;
2975
+ return {
2976
+ bold: hasFlag(format, IS_BOLD),
2977
+ code: hasFlag(format, IS_CODE),
2978
+ italic: hasFlag(format, IS_ITALIC),
2979
+ strike: hasFlag(format, IS_STRIKETHROUGH)
2980
+ };
2981
+ };
2982
+ var children2 = (node) => objectsIn(node.children);
2983
+ var typeOf = (node) => asString(node.type) ?? "";
2984
+ var customBlock = (node, options) => {
2985
+ const fields = asObject(node.fields);
2986
+ const blockType = fields ? asString(fields.blockType) : undefined;
2987
+ const serializer = blockType ? options.serializers?.[blockType] : undefined;
2988
+ if (fields && serializer) {
2989
+ return serializer(fields);
2990
+ }
2991
+ return unsupported(`Lexical block${blockType ? `: ${blockType}` : ""}`, writesMdx(options.serializers));
2992
+ };
2993
+ var renderInlines2 = (nodes, options) => nodes.map((node) => renderInlineNode2(node, options)).join("");
2994
+ var renderInlineNode2 = (node, options) => {
2995
+ switch (typeOf(node)) {
2996
+ case "text": {
2997
+ return renderInline(asString(node.text) ?? "", marksOf2(node));
2998
+ }
2999
+ case "linebreak": {
3000
+ return `
3001
+ `;
3002
+ }
3003
+ case "tab": {
3004
+ return "\t";
3005
+ }
3006
+ case "link":
3007
+ case "autolink": {
3008
+ return renderLink(renderInlines2(children2(node), options), asString(getPath(node, "fields.url")) ?? asString(node.url));
3009
+ }
3010
+ case "inlineBlock": {
3011
+ return customBlock(node, options);
3012
+ }
3013
+ default: {
3014
+ return renderInlines2(children2(node), options);
3015
+ }
3016
+ }
3017
+ };
3018
+ var renderUpload = (node, options) => {
3019
+ const value = asObject(node.value);
3020
+ const url = value ? asString(value.url) : undefined;
3021
+ if (!(value && url)) {
3022
+ return unsupported("Lexical upload (fetch with depth 1 to populate it)", writesMdx(options.serializers));
3023
+ }
3024
+ const alt = asString(value.alt) ?? asString(value.filename) ?? "";
3025
+ return image(alt, absoluteUrl(url, options.baseUrl));
3026
+ };
3027
+ var renderList2 = (node, options) => {
3028
+ const listType = asString(node.listType) ?? "bullet";
3029
+ const start = asNumber(node.start) ?? 1;
3030
+ const items = [];
3031
+ let lastMarker = "-";
3032
+ let index = 0;
3033
+ for (const item of children2(node)) {
3034
+ const kids = children2(item);
3035
+ const nested = kids.filter((kid) => typeOf(kid) === "list");
3036
+ const inline = kids.filter((kid) => typeOf(kid) !== "list");
3037
+ const sub = nested.map((list) => renderList2(list, options)).join(`
3038
+ `);
3039
+ if (inline.length === 0 && nested.length > 0) {
3040
+ items.push(items.length > 0 ? indent(sub, lastMarker.length + 1) : sub);
3041
+ continue;
3042
+ }
3043
+ lastMarker = listType === "number" ? `${start + index}.` : "-";
3044
+ let check = "";
3045
+ if (listType === "check") {
3046
+ check = item.checked === true ? "[x] " : "[ ] ";
3047
+ }
3048
+ const line = `${check}${renderInlines2(inline, options)}`;
3049
+ items.push(listItem(lastMarker, sub ? `${line}
3050
+ ${sub}` : line));
3051
+ index += 1;
3052
+ }
3053
+ return items.join(`
3054
+ `);
3055
+ };
3056
+ var renderBlock2 = (node, options) => {
3057
+ switch (typeOf(node)) {
3058
+ case "paragraph": {
3059
+ return renderInlines2(children2(node), options);
3060
+ }
3061
+ case "heading": {
3062
+ const level = Number((asString(node.tag) ?? "h1").slice(1));
3063
+ return `${headingPrefix(level)}${renderInlines2(children2(node), options)}`;
3064
+ }
3065
+ case "quote": {
3066
+ return blockquote(renderInlines2(children2(node), options));
3067
+ }
3068
+ case "list": {
3069
+ return renderList2(node, options);
3070
+ }
3071
+ case "horizontalrule": {
3072
+ return "---";
3073
+ }
3074
+ case "upload": {
3075
+ return renderUpload(node, options);
3076
+ }
3077
+ case "block": {
3078
+ return customBlock(node, options);
3079
+ }
3080
+ default: {
3081
+ return unsupported(`Lexical node: ${typeOf(node)}`, writesMdx(options.serializers));
3082
+ }
3083
+ }
3084
+ };
3085
+ var lexicalToMarkdown = (state, options = {}) => {
3086
+ const root = asObject(state.root) ?? state;
3087
+ return markdownDocument(children2(root).map((node) => renderBlock2(node, options)));
3088
+ };
3089
+
3090
+ // src/core/sources/payload.ts
3091
+ var PAGE_SIZE2 = 100;
3092
+ var DEFAULT_FIELDS2 = {
3093
+ body: "content",
3094
+ description: "description",
3095
+ lastModified: "updatedAt",
3096
+ slug: "slug",
3097
+ title: "title"
3098
+ };
3099
+ var payloadSource = (options, ctx) => {
3100
+ const fields = { ...DEFAULT_FIELDS2, ...options.fields };
3101
+ const origin = options.url.replace(/\/+$/u, "");
3102
+ const lower = (body) => lexicalToMarkdown(body, {
3103
+ baseUrl: origin,
3104
+ serializers: options.serializers
3105
+ });
3106
+ const fetchEntries = async () => {
3107
+ const preview = ctx?.preview ?? false;
3108
+ const token = options.token ?? process.env.PAYLOAD_API_KEY;
3109
+ const client = {
3110
+ fetchImpl: options.fetchImpl,
3111
+ headers: token ? {
3112
+ authorization: `${options.authCollection ?? "users"} API-Key ${token}`
3113
+ } : {}
3114
+ };
3115
+ const base = `${origin}/api/${options.collection}`;
3116
+ const entries = [];
3117
+ let page = 1;
3118
+ let more = true;
3119
+ while (more) {
3120
+ const query = queryString({
3121
+ ...options.params,
3122
+ depth: String(options.depth ?? 1),
3123
+ draft: preview ? "true" : undefined,
3124
+ limit: String(PAGE_SIZE2),
3125
+ page: String(page)
3126
+ });
3127
+ const result = asObject(await fetchJson(`${base}?${query}`, client));
3128
+ if (!result) {
3129
+ throw new Error("Payload returned a non-object response");
3130
+ }
3131
+ for (const doc of objectsIn(result.docs)) {
3132
+ const draft = asString(doc._status) === "draft";
3133
+ if (draft && !preview) {
3134
+ continue;
3135
+ }
3136
+ const id = asString(doc.id) ?? String(doc.id ?? "");
3137
+ entries.push(documentEntry(doc, fields, id, (body) => isJsonObject(body) ? lower(body) : "", draft, writesMdx(options.serializers)));
3138
+ }
3139
+ more = result.hasNextPage === true;
3140
+ page += 1;
3141
+ }
3142
+ return entries;
3143
+ };
3144
+ return remoteSource({
3145
+ fetchEntries,
3146
+ name: options.name,
3147
+ pollInterval: options.pollInterval,
3148
+ prefix: options.prefix
3149
+ }, ctx);
3150
+ };
3151
+
4747
3152
  // src/core/sources/sanity.ts
4748
- import { join as join8 } from "pathe";
3153
+ import { join as join6 } from "pathe";
4749
3154
 
4750
3155
  // src/core/sources/portable-text.ts
4751
3156
  var HEADING_STYLES = new Map([
@@ -4756,11 +3161,9 @@ var HEADING_STYLES = new Map([
4756
3161
  ["h5", "##### "],
4757
3162
  ["h6", "###### "]
4758
3163
  ]);
4759
- var MARKDOWN_SPECIALS = /[\\`*_[\]~<]/gu;
4760
- var escapeText = (text) => text.replaceAll(MARKDOWN_SPECIALS, String.raw`\$&`);
4761
3164
  var renderSpan = (span, defs) => {
4762
3165
  const isCode = span.marks?.includes("code") ?? false;
4763
- let text = isCode ? span.text ?? "" : escapeText(span.text ?? "");
3166
+ let text = isCode ? span.text ?? "" : escapeMarkdownText(span.text ?? "");
4764
3167
  if (!span.marks || span.marks.length === 0) {
4765
3168
  return text;
4766
3169
  }
@@ -4798,7 +3201,7 @@ var renderChildren = (block) => {
4798
3201
  return (block.children ?? []).map((span) => renderSpan(span, defs)).join("");
4799
3202
  };
4800
3203
  var isAltText = (value) => typeof value === "string";
4801
- var renderBlock = (block, options) => {
3204
+ var renderBlock3 = (block, options) => {
4802
3205
  const custom = options.serializers?.[block._type];
4803
3206
  if (custom) {
4804
3207
  return custom(block);
@@ -4806,10 +3209,10 @@ var renderBlock = (block, options) => {
4806
3209
  if (block._type === "image") {
4807
3210
  const url = options.imageUrl?.(block);
4808
3211
  const alt = isAltText(block.alt) ? block.alt : "";
4809
- return url ? `![${alt}](${url})` : "";
3212
+ return url ? image(alt, url) : "";
4810
3213
  }
4811
3214
  if (block._type !== "block") {
4812
- return `<!-- unsupported Portable Text block: ${block._type} -->`;
3215
+ return unsupported(`Portable Text block: ${block._type}`, writesMdx(options.serializers));
4813
3216
  }
4814
3217
  const inline = renderChildren(block);
4815
3218
  if (block.listItem) {
@@ -4823,7 +3226,7 @@ var renderBlock = (block, options) => {
4823
3226
  return `${HEADING_STYLES.get(block.style ?? "normal") ?? ""}${inline}`;
4824
3227
  };
4825
3228
  var portableTextToMarkdown = (blocks, options = {}) => {
4826
- const lines = blocks.map((block) => renderBlock(block, options));
3229
+ const lines = blocks.map((block) => renderBlock3(block, options));
4827
3230
  const out = [];
4828
3231
  for (let i = 0;i < lines.length; i += 1) {
4829
3232
  const line = lines[i] ?? "";
@@ -4843,20 +3246,6 @@ var portableTextToMarkdown = (blocks, options = {}) => {
4843
3246
 
4844
3247
  // src/core/sources/sanity.ts
4845
3248
  var IMAGE_REF = /^image-(?<id>[a-f0-9]+)-(?<dims>\d+x\d+)-(?<ext>\w+)$/u;
4846
- var isTraversable = (value) => typeof value === "object" && value !== null;
4847
- var getPath = (doc, path) => {
4848
- let current = doc;
4849
- for (const key of path.split(".")) {
4850
- if (isTraversable(current)) {
4851
- current = current[key];
4852
- } else {
4853
- return;
4854
- }
4855
- }
4856
- return current;
4857
- };
4858
- var isStringValue2 = (value) => typeof value === "string";
4859
- var asString = (value) => isStringValue2(value) ? value : undefined;
4860
3249
  var imageUrlFromRef = (ref, projectId, dataset) => {
4861
3250
  const match = ref.match(IMAGE_REF);
4862
3251
  if (!match?.groups) {
@@ -4865,13 +3254,13 @@ var imageUrlFromRef = (ref, projectId, dataset) => {
4865
3254
  const { id, dims, ext } = match.groups;
4866
3255
  return `https://cdn.sanity.io/images/${projectId}/${dataset}/${id}-${dims}.${ext}`;
4867
3256
  };
4868
- var resolveClient = async (options, preview) => {
3257
+ var resolveClient = (options, preview) => {
4869
3258
  if (options.client) {
4870
3259
  return options.client;
4871
3260
  }
4872
3261
  let createClient;
4873
3262
  try {
4874
- ({ createClient } = await import("@sanity/client"));
3263
+ ({ createClient } = nodeRequire("@sanity/client"));
4875
3264
  } catch {
4876
3265
  throw new BlumeError({
4877
3266
  code: "BLUME_SOURCE_SDK_MISSING",
@@ -4890,11 +3279,11 @@ var resolveClient = async (options, preview) => {
4890
3279
  };
4891
3280
  var sanitySource = (options, ctx) => {
4892
3281
  const fields = options.fields ?? {};
4893
- const cache = snapshotCache(ctx?.cacheDir ?? join8(".blume", "cache", options.name));
3282
+ const cache = snapshotCache(ctx?.cacheDir ?? join6(".blume", "cache", options.name));
4894
3283
  let snapshot = new Map;
4895
3284
  const toEntry = (doc) => {
4896
3285
  const slugValue = asString(getPath(doc, fields.slug ?? "slug.current")) ?? asString(doc._id) ?? "untitled";
4897
- const slug = slugifyPath(slugValue) || slugify2(asString(doc._id) ?? "") || "untitled";
3286
+ const slug = slugifyPath(slugValue) || slugify(asString(doc._id) ?? "") || "untitled";
4898
3287
  const data = {};
4899
3288
  const title = asString(getPath(doc, fields.title ?? "title"));
4900
3289
  const description = asString(getPath(doc, fields.description ?? "description"));
@@ -4912,19 +3301,20 @@ var sanitySource = (options, ctx) => {
4912
3301
  },
4913
3302
  serializers: options.serializers
4914
3303
  }) : "";
3304
+ const format = writesMdx(options.serializers) ? "mdx" : "md";
4915
3305
  const raw = frontmatter_default.stringify(markdown, data);
4916
3306
  return {
4917
- body: { format: "md", text: markdown },
3307
+ body: { format, text: markdown },
4918
3308
  data: { ...data },
4919
3309
  hash: hashText(raw),
4920
3310
  lastModified: asString(getPath(doc, fields.lastModified ?? "_updatedAt")),
4921
3311
  raw,
4922
- ref: `${slug}.md`
3312
+ ref: `${slug}.${format}`
4923
3313
  };
4924
3314
  };
4925
3315
  const load = async (refresh = ctx?.refresh ?? true) => {
4926
3316
  const result = await loadWithCache(options.name, cache, async () => {
4927
- const client = await resolveClient(options, ctx?.preview ?? false);
3317
+ const client = resolveClient(options, ctx?.preview ?? false);
4928
3318
  const docs = await client.fetch(options.query);
4929
3319
  return docs.map(toEntry);
4930
3320
  }, refresh);
@@ -4949,6 +3339,162 @@ var sanitySource = (options, ctx) => {
4949
3339
  };
4950
3340
  };
4951
3341
 
3342
+ // src/core/sources/strapi-blocks.ts
3343
+ var children3 = (node) => objectsIn(node.children);
3344
+ var typeOf2 = (node) => asString(node.type) ?? "";
3345
+ var renderInlines3 = (nodes) => nodes.map((node) => renderInlineNode3(node)).join("");
3346
+ var renderInlineNode3 = (node) => {
3347
+ switch (typeOf2(node)) {
3348
+ case "text": {
3349
+ return renderInline(asString(node.text) ?? "", {
3350
+ bold: node.bold === true,
3351
+ code: node.code === true,
3352
+ italic: node.italic === true,
3353
+ strike: node.strikethrough === true
3354
+ });
3355
+ }
3356
+ case "link": {
3357
+ return renderLink(renderInlines3(children3(node)), asString(node.url));
3358
+ }
3359
+ default: {
3360
+ return renderInlines3(children3(node));
3361
+ }
3362
+ }
3363
+ };
3364
+ var renderList3 = (node) => {
3365
+ const ordered = asString(node.format) === "ordered";
3366
+ return children3(node).map((item, index) => {
3367
+ const kids = children3(item);
3368
+ const line = renderInlines3(kids.filter((kid) => typeOf2(kid) !== "list"));
3369
+ const sub = kids.filter((kid) => typeOf2(kid) === "list").map((list) => renderList3(list)).join(`
3370
+ `);
3371
+ const body = sub ? `${line}
3372
+ ${sub}` : line;
3373
+ return listItem(ordered ? `${index + 1}.` : "-", body);
3374
+ }).join(`
3375
+ `);
3376
+ };
3377
+ var renderImage = (node, options) => {
3378
+ const file = asObject(node.image);
3379
+ const url = file ? asString(file.url) : undefined;
3380
+ if (!(file && url)) {
3381
+ return "";
3382
+ }
3383
+ return image(asString(file.alternativeText) ?? asString(file.name) ?? "", absoluteUrl(url, options.baseUrl));
3384
+ };
3385
+ var renderBlock4 = (node, options) => {
3386
+ switch (typeOf2(node)) {
3387
+ case "paragraph": {
3388
+ return renderInlines3(children3(node));
3389
+ }
3390
+ case "heading": {
3391
+ return `${headingPrefix(asNumber(node.level) ?? 1)}${renderInlines3(children3(node))}`;
3392
+ }
3393
+ case "quote": {
3394
+ return blockquote(renderInlines3(children3(node)));
3395
+ }
3396
+ case "list": {
3397
+ return renderList3(node);
3398
+ }
3399
+ case "code": {
3400
+ const code = children3(node).map((child) => asString(child.text) ?? "").join("");
3401
+ return codeFence(code, asString(node.language) ?? "");
3402
+ }
3403
+ case "image": {
3404
+ return renderImage(node, options);
3405
+ }
3406
+ default: {
3407
+ return unsupported(`Strapi block: ${typeOf2(node)}`);
3408
+ }
3409
+ }
3410
+ };
3411
+ var strapiBlocksToMarkdown = (blocks, options = {}) => markdownDocument(objectsIn(blocks).map((node) => renderBlock4(node, options)));
3412
+
3413
+ // src/core/sources/strapi.ts
3414
+ var PAGE_SIZE3 = 100;
3415
+ var DEFAULT_FIELDS3 = {
3416
+ body: "content",
3417
+ description: "description",
3418
+ lastModified: "updatedAt",
3419
+ slug: "slug",
3420
+ title: "title"
3421
+ };
3422
+ var flatten = (item) => {
3423
+ const attributes = asObject(item.attributes);
3424
+ return attributes ? { ...attributes, id: item.id ?? null } : item;
3425
+ };
3426
+ var idOf = (doc) => {
3427
+ const raw = doc.documentId ?? doc.id;
3428
+ return asString(raw) ?? String(asNumber(raw) ?? "");
3429
+ };
3430
+ var unpublished = (doc) => doc.publishedAt === null;
3431
+ var strapiSource = (options, ctx) => {
3432
+ const fields = { ...DEFAULT_FIELDS3, ...options.fields };
3433
+ const origin = options.url.replace(/\/+$/u, "");
3434
+ const fetchEntries = async () => {
3435
+ const token = options.token ?? process.env.STRAPI_API_TOKEN;
3436
+ const client = {
3437
+ fetchImpl: options.fetchImpl,
3438
+ headers: token ? { authorization: `Bearer ${token}` } : {}
3439
+ };
3440
+ const base = `${origin}/api/${options.contentType}`;
3441
+ const list = async (publication) => {
3442
+ const listing = { docs: [], v4: false };
3443
+ let page = 1;
3444
+ let more = true;
3445
+ while (more) {
3446
+ const query = queryString({
3447
+ ...options.params,
3448
+ locale: options.locale,
3449
+ "pagination[pageSize]": String(PAGE_SIZE3),
3450
+ "pagination[page]": String(page),
3451
+ populate: options.populate ?? "*",
3452
+ ...publication
3453
+ });
3454
+ const result = asObject(await fetchJson(`${base}?${query}`, client));
3455
+ if (!result) {
3456
+ throw new Error("Strapi returned a non-object response");
3457
+ }
3458
+ const items = objectsIn(result.data);
3459
+ for (const item of items) {
3460
+ listing.v4 ||= asObject(item.attributes) !== undefined;
3461
+ listing.docs.push(flatten(item));
3462
+ }
3463
+ const pageCount = asNumber(getPath(result, "meta.pagination.pageCount")) ?? 1;
3464
+ more = items.length > 0 && page < pageCount;
3465
+ page += 1;
3466
+ }
3467
+ return listing;
3468
+ };
3469
+ const toEntries = (docs, isDraft) => docs.map((doc) => documentEntry(doc, fields, idOf(doc), (body) => strapiBlocksToMarkdown(body, { baseUrl: origin }), isDraft(doc)));
3470
+ const published = await list({});
3471
+ if (!ctx?.preview) {
3472
+ return toEntries(published.docs, () => false);
3473
+ }
3474
+ if (published.v4) {
3475
+ const previewed = await list({ publicationState: "preview" });
3476
+ return toEntries(previewed.docs, unpublished);
3477
+ }
3478
+ const drafts = await list({ status: "draft" });
3479
+ if (drafts.docs.length === 0 && published.docs.length === 0) {
3480
+ const previewed = await list({ publicationState: "preview" }).catch(() => null);
3481
+ return previewed ? toEntries(previewed.docs, unpublished) : [];
3482
+ }
3483
+ const publishedIds = new Set(published.docs.map(idOf));
3484
+ const draftIds = new Set(drafts.docs.map(idOf));
3485
+ return [
3486
+ ...toEntries(drafts.docs, (doc) => !publishedIds.has(idOf(doc))),
3487
+ ...toEntries(published.docs.filter((doc) => !draftIds.has(idOf(doc))), () => false)
3488
+ ];
3489
+ };
3490
+ return remoteSource({
3491
+ fetchEntries,
3492
+ name: options.name,
3493
+ pollInterval: options.pollInterval,
3494
+ prefix: options.prefix
3495
+ }, ctx);
3496
+ };
3497
+
4952
3498
  // src/core/sources/resolve.ts
4953
3499
  var uniqueNamer = () => {
4954
3500
  const used = new Set;
@@ -4965,8 +3511,8 @@ var uniqueNamer = () => {
4965
3511
  };
4966
3512
  var sourceContext = (context, name, runtime) => ({
4967
3513
  assetsBaseUrl: `/blume-assets/${name}`,
4968
- assetsDir: join9(context.outDir, "public", "blume-assets", name),
4969
- cacheDir: join9(context.outDir, "cache", name),
3514
+ assetsDir: join7(context.outDir, "public", "blume-assets", name),
3515
+ cacheDir: join7(context.outDir, "cache", name),
4970
3516
  mode: runtime.mode,
4971
3517
  preview: runtime.preview,
4972
3518
  projectRoot: context.root,
@@ -4976,119 +3522,61 @@ var typeFrontmatterKeys = (config) => Object.fromEntries(Object.entries(config.c
4976
3522
  type,
4977
3523
  Object.keys(def.frontmatter)
4978
3524
  ]));
4979
- var buildSource = (def, name, config, context, runtime) => {
4980
- if (def.type === "filesystem") {
4981
- return filesystemSource({
4982
- exclude: def.exclude,
4983
- include: def.include,
4984
- name,
4985
- prefix: def.prefix,
4986
- projectRoot: context.root,
4987
- root: def.root
4988
- });
4989
- }
4990
- if (def.type === "custom") {
4991
- return def.source.name === name ? def.source : { ...def.source, name };
4992
- }
4993
- if (def.type === "sanity") {
4994
- return sanitySource({
4995
- apiVersion: def.apiVersion,
4996
- dataset: def.dataset,
4997
- fields: def.fields,
4998
- name,
4999
- pollInterval: def.pollInterval,
5000
- prefix: def.prefix,
5001
- projectId: def.projectId,
5002
- query: def.query
5003
- }, sourceContext(context, name, runtime));
5004
- }
5005
- if (def.type === "notion") {
5006
- return notionSource({
5007
- concurrency: def.concurrency,
5008
- database: def.database,
5009
- name,
5010
- pollInterval: def.pollInterval,
5011
- prefix: def.prefix,
5012
- properties: def.properties,
5013
- publishedValue: def.publishedValue
5014
- }, sourceContext(context, name, runtime));
5015
- }
5016
- if (def.type === "obsidian") {
5017
- return obsidianSource({
5018
- defaultType: config.content.defaultType,
5019
- exclude: def.exclude,
5020
- frontmatterKeys: Object.keys(config.frontmatter.extend),
5021
- i18n: config.i18n,
5022
- name,
5023
- prefix: def.prefix,
5024
- typeFrontmatterKeys: typeFrontmatterKeys(config),
5025
- vault: def.vault,
5026
- versions: config.versions
5027
- }, sourceContext(context, name, runtime));
5028
- }
5029
- if (def.type === "github-releases") {
5030
- return githubReleasesSource({
5031
- drafts: def.drafts,
5032
- limit: def.limit,
5033
- name,
5034
- owner: def.owner,
5035
- pollInterval: def.pollInterval,
5036
- prefix: def.prefix,
5037
- prereleases: def.prereleases,
5038
- repo: def.repo
5039
- }, sourceContext(context, name, runtime));
5040
- }
5041
- return mdxRemoteSource({
5042
- files: def.files,
5043
- github: def.github,
5044
- include: def.include,
5045
- name,
5046
- pollInterval: def.pollInterval,
5047
- prefix: def.prefix,
5048
- url: def.url
5049
- }, sourceContext(context, name, runtime));
5050
- };
5051
- var resolveRoot = (projectRoot, root) => isAbsolute4(root) ? root : join9(resolve4(projectRoot), root);
5052
- var resolveDocsCollection = (config, context) => {
5053
- const filesystem = (config.content.sources ?? []).filter((def) => def.type === "filesystem");
5054
- const only = filesystem.length === 1 ? filesystem[0] : undefined;
5055
- if (only) {
5056
- return {
5057
- base: resolveRoot(context.root, only.root),
5058
- exclude: only.exclude,
5059
- include: only.include
5060
- };
5061
- }
5062
- return {
5063
- base: context.contentRoot,
5064
- exclude: config.content.exclude,
5065
- include: config.content.include
5066
- };
5067
- };
5068
- var baseName = (def) => {
5069
- if (def.type === "custom") {
5070
- return def.source.name;
3525
+ var buildSource = (adapter, name, config, context, runtime) => {
3526
+ switch (adapter.kind) {
3527
+ case "filesystem": {
3528
+ const { pollInterval: _ignored, ...options } = adapter.options;
3529
+ return filesystemSource({ ...options, name, projectRoot: context.root });
3530
+ }
3531
+ case "custom": {
3532
+ const source = adapter.options;
3533
+ return source.name === name ? source : { ...source, name };
3534
+ }
3535
+ case "sanity": {
3536
+ return sanitySource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3537
+ }
3538
+ case "notion": {
3539
+ return notionSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3540
+ }
3541
+ case "contentful": {
3542
+ return contentfulSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3543
+ }
3544
+ case "payload": {
3545
+ return payloadSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3546
+ }
3547
+ case "strapi": {
3548
+ return strapiSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3549
+ }
3550
+ case "obsidian": {
3551
+ const { pollInterval: _ignored, ...options } = adapter.options;
3552
+ return obsidianSource({
3553
+ ...options,
3554
+ defaultType: config.content.defaultType,
3555
+ frontmatterKeys: Object.keys(config.frontmatter.extend),
3556
+ i18n: config.i18n,
3557
+ name,
3558
+ typeFrontmatterKeys: typeFrontmatterKeys(config),
3559
+ versions: config.versions
3560
+ }, sourceContext(context, name, runtime));
3561
+ }
3562
+ case "github-releases": {
3563
+ return githubReleasesSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
3564
+ }
3565
+ default: {
3566
+ break;
3567
+ }
5071
3568
  }
5072
- return def.prefix ?? def.type;
3569
+ return mdxRemoteSource({ ...adapter.options, name }, sourceContext(context, name, runtime));
5073
3570
  };
5074
- var contentSources = (config, context, runtime) => {
5075
- const defs = config.content.sources;
5076
- if (!defs || defs.length === 0) {
5077
- return [
5078
- filesystemSource({
5079
- exclude: config.content.exclude,
5080
- include: config.content.include,
5081
- name: "filesystem",
5082
- projectRoot: context.root,
5083
- root: config.content.root
5084
- })
5085
- ];
3571
+ var baseName = (adapter) => {
3572
+ if (adapter.kind === "custom") {
3573
+ return adapter.options.name;
5086
3574
  }
5087
- const nameFor = uniqueNamer();
5088
- return defs.map((def) => buildSource(def, nameFor(baseName(def)), config, context, runtime));
3575
+ return adapter.options.prefix ?? adapter.kind;
5089
3576
  };
5090
3577
  var resolveSources = (config, context, runtime) => {
5091
- const sources = contentSources(config, context, runtime);
3578
+ const nameFor = uniqueNamer();
3579
+ const sources = config.content.sources.map((adapter) => buildSource(adapter, nameFor(baseName(adapter)), config, context, runtime));
5092
3580
  const references = blumeReferences(config);
5093
3581
  if (references.length > 0) {
5094
3582
  sources.push(openApiSource(references, sourceContext(context, "openapi", runtime)));
@@ -5098,25 +3586,18 @@ var resolveSources = (config, context, runtime) => {
5098
3586
 
5099
3587
  // src/core/project-graph.ts
5100
3588
  var isWithin = (dir, path) => {
5101
- const rel = relative6(dir, path);
5102
- return !(rel.startsWith("..") || isAbsolute5(rel));
3589
+ const rel = relative5(dir, path);
3590
+ return !(rel.startsWith("..") || isAbsolute3(rel));
5103
3591
  };
5104
3592
  var applyConfigOverrides = (config, overrides) => {
5105
3593
  if (!overrides) {
5106
3594
  return config;
5107
3595
  }
3596
+ const { contentRoot } = overrides;
3597
+ const sources = contentRoot ? config.content.sources.map((source) => source.kind === "filesystem" ? { ...source, options: { ...source.options, root: contentRoot } } : source) : config.content.sources;
5108
3598
  return {
5109
3599
  ...config,
5110
- content: {
5111
- ...config.content,
5112
- root: overrides.contentRoot ?? config.content.root
5113
- },
5114
- deployment: {
5115
- ...config.deployment,
5116
- adapter: overrides.adapter ?? config.deployment.adapter,
5117
- base: overrides.base ?? config.deployment.base,
5118
- output: overrides.output ?? config.deployment.output
5119
- }
3600
+ content: { ...config.content, sources }
5120
3601
  };
5121
3602
  };
5122
3603
  var entryIdDiagnostics = (pages, collectionBase) => {
@@ -5127,7 +3608,7 @@ var entryIdDiagnostics = (pages, collectionBase) => {
5127
3608
  continue;
5128
3609
  }
5129
3610
  seen.add(page.sourcePath);
5130
- const expected = relative6(collectionBase, page.sourcePath).split("\\").join("/");
3611
+ const expected = relative5(collectionBase, page.sourcePath).split("\\").join("/");
5131
3612
  const entryId = page.entryId ?? page.source.ref;
5132
3613
  if (expected !== entryId) {
5133
3614
  diagnostics.push({
@@ -5135,7 +3616,7 @@ var entryIdDiagnostics = (pages, collectionBase) => {
5135
3616
  file: page.sourcePath,
5136
3617
  message: `Content source "${page.source.name}" is rooted outside the docs collection base, so ${page.route} resolves entry id "${entryId}" but the collection would generate "${expected}" — the page would 404 at runtime.`,
5137
3618
  severity: "error",
5138
- suggestion: "Use a single filesystem source (the docs collection roots at it), or root every filesystem source at content.root and partition them with include globs — a root at a subdirectory of content.root still mismatches."
3619
+ suggestion: "Use a single filesystem() source (the docs collection roots at it), or give every filesystem() source the same root and partition them with include globs — a root at a subdirectory of the first source's root still mismatches."
5139
3620
  });
5140
3621
  }
5141
3622
  }
@@ -5157,6 +3638,7 @@ var normalizeLoadedEntries = (loaded, config) => {
5157
3638
  frontmatterExtend,
5158
3639
  i18n: config.i18n,
5159
3640
  source: {
3641
+ monolingual: source.monolingual,
5160
3642
  name: source.name,
5161
3643
  prefix: source.prefix,
5162
3644
  staged: source.staged
@@ -5173,13 +3655,15 @@ var normalizeLoadedEntries = (loaded, config) => {
5173
3655
  }
5174
3656
  return { diagnostics: allDiagnostics, droppedPages, pages };
5175
3657
  };
3658
+ var isLogoShorthand = (logo) => typeof logo === "string";
3659
+ var logoHref = (logo) => logo === undefined || isLogoShorthand(logo) ? "/" : logo.href ?? "/";
5176
3660
  var scanProject = async (root, options = {}) => {
5177
3661
  const mode = options.mode ?? "dev";
5178
3662
  const preview = options.preview ?? false;
5179
3663
  const configResult = await loadConfig(root, {
5180
3664
  devServerUrl: options.devServerUrl
5181
3665
  });
5182
- const config = applyDeploymentEnv(applyConfigOverrides(configResult.config, options.overrides));
3666
+ const config = applyConfigOverrides(configResult.config, options.overrides);
5183
3667
  const context = resolveProjectContext(root, config, {
5184
3668
  runtimeDir: options.runtimeDir
5185
3669
  });
@@ -5243,8 +3727,14 @@ var scanProject = async (root, options = {}) => {
5243
3727
  const undated = pages.filter((page) => page.sourcePath && !page.lastModified && contentRoots.some((dir) => isWithin(dir, page.sourcePath ?? ""))).length;
5244
3728
  lastModifiedWarnings.push(...lastModifiedShallowWarning(context.root, undated));
5245
3729
  }
3730
+ const customPages = context.pagesRoot ? await discoverPages(context.pagesRoot) : [];
5246
3731
  const graph = buildContentGraph(pages, {
5247
3732
  basePath: config.basePath,
3733
+ brandHref: logoHref(config.logo),
3734
+ extraRoutes: new Set([
3735
+ ...customStaticRoutes(customPages),
3736
+ ...hasChangelogIndex(pages, config) ? [CHANGELOG_INDEX_ROUTE] : []
3737
+ ]),
5248
3738
  folderMeta: folderMeta.meta,
5249
3739
  i18n: config.i18n,
5250
3740
  navigation: config.navigation,
@@ -5261,7 +3751,7 @@ var scanProject = async (root, options = {}) => {
5261
3751
  ...contentDiagnostics,
5262
3752
  ...includeDiagnostics,
5263
3753
  ...folderMeta.diagnostics,
5264
- ...entryIdDiagnostics(pages, resolveDocsCollection(config, context).base),
3754
+ ...entryIdDiagnostics(pages, resolveDocsCollection(config, context.root).base),
5265
3755
  ...graph.diagnostics,
5266
3756
  ...i18nWarnings,
5267
3757
  ...versionWarnings,
@@ -5276,7 +3766,7 @@ var scanProject = async (root, options = {}) => {
5276
3766
  };
5277
3767
  };
5278
3768
 
5279
- export { YAML_SCHEMA, frontmatter_default, TOC_HIDDEN_KEY, parseHeadingMarkers, BLUME_IGNORE_DIRS, i18nEnabled, localeCodes, resolveFallbackLocale, localePrefix, localizeRoute, localeTargetPath, validateSearchPopularIcons, validateNavTargets, VERSION_LIKE, versionizeRoute, nextFenceState, HTML_COMMENT, INLINE_CODE, scanBody, MD_IMAGE, targetOffsetIn, hasIncludeStatements, expandIncludes, buildIncludeGraph, contentIndexable, buildManifest, hashText, specOf, operationOf, specAddresses, apiNamePhrase, operationSignature, isOpenApiSource, parseYouTubeId, resolveDocsCollection, isWithin, scanProject };
3769
+ export { validateSearchPopularIcons, validateNavTargets, contentIndexable, buildManifest, isWithin, scanProject };
5280
3770
 
5281
- //# debugId=FDD686362F0089E964756E2164756E21
5282
- //# sourceMappingURL=chunk-hdm2dkd2.js.map
3771
+ //# debugId=EBFFBF92D39DE54464756E2164756E21
3772
+ //# sourceMappingURL=chunk-zg2gtj10.js.map