blume 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  3. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  4. package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
  5. package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
  6. package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
  7. package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
  8. package/dist/cli/chunk-124fvmzq.js.map +220 -0
  9. package/dist/cli/chunk-150jk7km.js +52 -0
  10. package/dist/cli/chunk-150jk7km.js.map +10 -0
  11. package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
  12. package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
  13. package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
  14. package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
  15. package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
  16. package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
  17. package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
  18. package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
  19. package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
  20. package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
  21. package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
  22. package/dist/cli/chunk-60hz265r.js.map +10 -0
  23. package/dist/cli/chunk-74wfhv79.js +65 -0
  24. package/dist/cli/chunk-74wfhv79.js.map +10 -0
  25. package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
  26. package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
  27. package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
  28. package/dist/cli/chunk-ave1wcbr.js.map +14 -0
  29. package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
  30. package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
  31. package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
  32. package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
  33. package/dist/cli/chunk-ckrcm08h.js +324 -0
  34. package/dist/cli/chunk-ckrcm08h.js.map +14 -0
  35. package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
  36. package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
  37. package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
  38. package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
  39. package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
  40. package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
  41. package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
  42. package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
  43. package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
  44. package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
  45. package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
  46. package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
  47. package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
  48. package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
  49. package/dist/cli/chunk-j1x92mx9.js.map +10 -0
  50. package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
  51. package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
  52. package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
  53. package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
  54. package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
  55. package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
  56. package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
  57. package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
  58. package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
  59. package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
  60. package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
  61. package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
  62. package/dist/cli/chunk-twy7psdz.js.map +10 -0
  63. package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
  64. package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
  65. package/dist/cli/index.js +24 -19
  66. package/dist/cli/index.js.map +3 -3
  67. package/dist/types/ai/ask-context.d.ts +6 -2
  68. package/dist/types/ai/ask.d.ts +24 -5
  69. package/dist/types/ai/component-markdown.d.ts +2 -0
  70. package/dist/types/ai/openapi-components.d.ts +4 -1
  71. package/dist/types/analytics/clics.d.ts +37 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +18 -0
  74. package/dist/types/astro/integration.d.ts +11 -4
  75. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  76. package/dist/types/astro/pages.d.ts +10 -0
  77. package/dist/types/cli/init/scaffold.d.ts +1 -1
  78. package/dist/types/components/content/api-field.d.ts +4 -1
  79. package/dist/types/components/content/api-page.d.ts +6 -1
  80. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  81. package/dist/types/components/openapi/helpers.d.ts +22 -1
  82. package/dist/types/components/openapi/security.d.ts +10 -0
  83. package/dist/types/components/openapi/snippets.d.ts +60 -0
  84. package/dist/types/core/changelog-index.d.ts +8 -0
  85. package/dist/types/core/config-input.d.ts +37 -6
  86. package/dist/types/core/data.d.ts +4 -0
  87. package/dist/types/core/i18n-ui.d.ts +2 -0
  88. package/dist/types/core/links.d.ts +8 -0
  89. package/dist/types/core/powered-by.d.ts +12 -0
  90. package/dist/types/core/probe.d.ts +2 -0
  91. package/dist/types/core/project-graph.d.ts +6 -0
  92. package/dist/types/core/redirect-patterns.d.ts +11 -3
  93. package/dist/types/core/schema.d.ts +228 -3
  94. package/dist/types/core/sources/cache.d.ts +3 -0
  95. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  96. package/dist/types/core/sources/contentful.d.ts +17 -1
  97. package/dist/types/core/sources/lexical.d.ts +5 -0
  98. package/dist/types/core/sources/lower.d.ts +10 -2
  99. package/dist/types/core/sources/normalize.d.ts +20 -1
  100. package/dist/types/core/sources/notion.d.ts +8 -0
  101. package/dist/types/core/sources/remote.d.ts +37 -10
  102. package/dist/types/core/sources/sanity.d.ts +1 -1
  103. package/dist/types/core/types.d.ts +17 -0
  104. package/dist/types/deploy/adapter-output.d.ts +2 -1
  105. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  106. package/dist/types/deploy/adapters/node.d.ts +58 -23
  107. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  108. package/dist/types/deploy/adapters/types.d.ts +3 -2
  109. package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
  110. package/dist/types/deploy/node-headers.d.ts +12 -4
  111. package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
  112. package/dist/types/deploy/platforms/node.d.ts +3 -1
  113. package/dist/types/deploy/platforms/types.d.ts +18 -7
  114. package/dist/types/deploy/platforms/vercel.d.ts +21 -2
  115. package/dist/types/deploy/redirects.d.ts +17 -1
  116. package/dist/types/deploy/sitemap.d.ts +2 -1
  117. package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
  118. package/dist/types/narration/build.d.ts +34 -12
  119. package/dist/types/narration/provider.d.ts +96 -8
  120. package/dist/types/openapi/checks.d.ts +27 -0
  121. package/dist/types/openapi/graphql.d.ts +6 -0
  122. package/dist/types/openapi/model.d.ts +15 -4
  123. package/dist/types/openapi/parse.d.ts +3 -0
  124. package/dist/types/openapi/references.d.ts +16 -0
  125. package/dist/types/openapi/sentence.d.ts +11 -1
  126. package/dist/types/ratelimit/index.d.ts +1 -1
  127. package/dist/types/ratelimit/schema.d.ts +8 -0
  128. package/dist/types/ratelimit/upstash.d.ts +39 -14
  129. package/dist/types/reference/graphql.d.ts +76 -1
  130. package/dist/types/reference/index.d.ts +1 -1
  131. package/dist/types/reference/openapi.d.ts +2 -2
  132. package/dist/types/reference/options.d.ts +37 -1
  133. package/dist/types/reference/schema.d.ts +84 -0
  134. package/dist/types/search/adapters/registry.d.ts +3 -0
  135. package/dist/types/search/adapters/typesense.d.ts +14 -4
  136. package/dist/types/search/build.d.ts +3 -1
  137. package/dist/types/search/orama-index.d.ts +9 -9
  138. package/dist/types/search/sync/algolia.d.ts +5 -2
  139. package/dist/types/search/sync/index.d.ts +3 -1
  140. package/dist/types/search/sync/typesense.d.ts +11 -7
  141. package/dist/types/seo/jsonld.d.ts +11 -6
  142. package/dist/types/sources/contentful.d.ts +16 -0
  143. package/dist/types/sources/github-releases.d.ts +8 -1
  144. package/dist/types/sources/notion.d.ts +8 -0
  145. package/dist/types/sources/payload.d.ts +2 -0
  146. package/dist/types/sources/registry.d.ts +63 -0
  147. package/dist/types/sources/shared.d.ts +3 -0
  148. package/dist/types/sources/strapi.d.ts +2 -0
  149. package/docs/01-quickstart.mdx +4 -2
  150. package/docs/02-deployment.mdx +59 -13
  151. package/docs/03-upgrading.mdx +2 -1
  152. package/docs/04-migrating.mdx +9 -9
  153. package/docs/08-faq.mdx +12 -12
  154. package/docs/advanced/changelog.mdx +30 -2
  155. package/docs/advanced/custom-pages.mdx +1 -1
  156. package/docs/advanced/skills.mdx +1 -1
  157. package/docs/cli/audit.mdx +30 -7
  158. package/docs/cli/doctor.mdx +6 -2
  159. package/docs/cli/evals.mdx +4 -4
  160. package/docs/cli/translate.mdx +1 -0
  161. package/docs/cli/validate.mdx +4 -3
  162. package/docs/configuration/analytics.mdx +19 -1
  163. package/docs/configuration/assistant.mdx +6 -2
  164. package/docs/configuration/customization.mdx +4 -2
  165. package/docs/configuration/export.mdx +5 -3
  166. package/docs/configuration/index.mdx +4 -2
  167. package/docs/configuration/narration.mdx +43 -6
  168. package/docs/configuration/rate-limiting.mdx +23 -2
  169. package/docs/configuration/search.mdx +37 -14
  170. package/docs/content/components.mdx +2 -2
  171. package/docs/content/frontmatter.mdx +5 -3
  172. package/docs/content/i18n.mdx +2 -2
  173. package/docs/content/includes.mdx +1 -1
  174. package/docs/content/navigation.mdx +8 -6
  175. package/docs/content/sources/contentful.mdx +9 -4
  176. package/docs/content/sources/custom.mdx +7 -3
  177. package/docs/content/sources/github-releases.mdx +12 -2
  178. package/docs/content/sources/index.mdx +3 -3
  179. package/docs/content/sources/notion.mdx +17 -2
  180. package/docs/content/sources/obsidian.mdx +3 -1
  181. package/docs/content/sources/payload.mdx +5 -2
  182. package/docs/content/sources/remote-mdx.mdx +6 -0
  183. package/docs/content/sources/sanity.mdx +4 -2
  184. package/docs/content/sources/strapi.mdx +5 -2
  185. package/docs/content/syntax.mdx +2 -2
  186. package/docs/discoverability/agent-discovery.mdx +1 -1
  187. package/docs/discoverability/index.mdx +1 -1
  188. package/docs/discoverability/json-api.mdx +2 -0
  189. package/docs/discoverability/llms-txt.mdx +1 -1
  190. package/docs/discoverability/markdown.mdx +3 -1
  191. package/docs/discoverability/mcp.mdx +6 -0
  192. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  193. package/docs/discoverability/structured-data.mdx +8 -6
  194. package/docs/references/api-pages.mdx +1 -1
  195. package/docs/references/asyncapi.mdx +5 -1
  196. package/docs/references/graphql.mdx +15 -1
  197. package/docs/references/openapi.mdx +64 -8
  198. package/package.json +9 -9
  199. package/skills/blume/SKILL.md +1 -1
  200. package/skills/blume-migrate/SKILL.md +2 -2
  201. package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
  202. package/skills/blume-migrate/references/docusaurus.md +4 -4
  203. package/skills/blume-migrate/references/fumadocs.md +17 -13
  204. package/skills/blume-migrate/references/monorepo.md +5 -5
  205. package/skills/blume-migrate/references/nextra.md +8 -5
  206. package/skills/blume-migrate/references/starlight.md +9 -7
  207. package/src/ai/ask-context.ts +33 -3
  208. package/src/ai/ask-limits.ts +13 -0
  209. package/src/ai/ask.ts +24 -6
  210. package/src/ai/changelog-markdown.ts +11 -6
  211. package/src/ai/component-markdown.ts +1 -1
  212. package/src/ai/cors.ts +12 -3
  213. package/src/ai/mcp/query.ts +14 -6
  214. package/src/ai/openapi-components.ts +28 -4
  215. package/src/ai/relative-links.ts +4 -38
  216. package/src/analytics/clics.ts +58 -0
  217. package/src/analytics/head.ts +5 -0
  218. package/src/analytics/index.ts +2 -0
  219. package/src/analytics/schema.ts +2 -0
  220. package/src/astro/generate.ts +91 -56
  221. package/src/astro/integration.ts +72 -22
  222. package/src/astro/markdown-negotiation.ts +3 -0
  223. package/src/astro/module-types.ts +3 -1
  224. package/src/astro/pages.ts +23 -0
  225. package/src/astro/render-deps.ts +10 -5
  226. package/src/astro/runtime-deps.ts +23 -2
  227. package/src/astro/templates.ts +129 -81
  228. package/src/audit/catalog.ts +19 -3
  229. package/src/audit/checks/links.ts +14 -1
  230. package/src/audit/checks/network.ts +110 -6
  231. package/src/audit/checks/redirects.ts +6 -4
  232. package/src/audit/checks/robots.ts +58 -2
  233. package/src/audit/run.ts +3 -0
  234. package/src/audit/types.ts +2 -0
  235. package/src/audit/url.ts +14 -5
  236. package/src/blume-modules.d.ts +5 -1
  237. package/src/cli/args.ts +51 -0
  238. package/src/cli/build-failure.ts +23 -0
  239. package/src/cli/commands/audit.ts +10 -1
  240. package/src/cli/commands/dev.ts +18 -2
  241. package/src/cli/commands/doctor.ts +45 -12
  242. package/src/cli/commands/eval.ts +36 -4
  243. package/src/cli/commands/init.ts +6 -4
  244. package/src/cli/commands/validate.ts +10 -1
  245. package/src/cli/init/scaffold.ts +9 -6
  246. package/src/cli/prepare.ts +6 -3
  247. package/src/cli/required-secrets.ts +3 -2
  248. package/src/components/content/ApiPlayground.astro +6 -2
  249. package/src/components/content/api-field.ts +4 -1
  250. package/src/components/content/api-page.ts +31 -7
  251. package/src/components/content/mermaid-element.ts +15 -1
  252. package/src/components/content/view-switcher.ts +7 -4
  253. package/src/components/islands/hooks.ts +27 -1
  254. package/src/components/layout/NavTree.astro +49 -5
  255. package/src/components/layout/NavTreeScript.astro +18 -0
  256. package/src/components/layout/PageActions.astro +139 -24
  257. package/src/components/layout/PageLayout.astro +13 -3
  258. package/src/components/layout/RootLayout.astro +27 -14
  259. package/src/components/layout/Search.astro +17 -3
  260. package/src/components/layout/TableOfContents.astro +24 -17
  261. package/src/components/layout/analytics-client.ts +8 -8
  262. package/src/components/layout/head-scripts.ts +6 -1
  263. package/src/components/layout/search/algolia.ts +10 -1
  264. package/src/components/layout/search/analytics.ts +42 -14
  265. package/src/components/layout/search/endpoint.ts +48 -5
  266. package/src/components/layout/search/pagefind.ts +114 -5
  267. package/src/components/layout/search/types.ts +12 -0
  268. package/src/components/layout/search/typesense.ts +18 -6
  269. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  270. package/src/components/openapi/Authorization.astro +10 -0
  271. package/src/components/openapi/GraphqlOperation.astro +5 -0
  272. package/src/components/openapi/Operation.astro +24 -1
  273. package/src/components/openapi/ParametersTable.astro +8 -4
  274. package/src/components/openapi/Playground.astro +7 -1
  275. package/src/components/openapi/RequestBody.astro +19 -2
  276. package/src/components/openapi/Responses.astro +1 -0
  277. package/src/components/openapi/SchemaTable.astro +15 -3
  278. package/src/components/openapi/async-snippets.ts +61 -5
  279. package/src/components/openapi/async.ts +123 -29
  280. package/src/components/openapi/graphql-helpers.ts +35 -12
  281. package/src/components/openapi/helpers.ts +36 -2
  282. package/src/components/openapi/message-model.ts +54 -11
  283. package/src/components/openapi/message.ts +14 -4
  284. package/src/components/openapi/operation-model.ts +16 -7
  285. package/src/components/openapi/playground-client.ts +77 -58
  286. package/src/components/openapi/playground-schema.ts +1 -1
  287. package/src/components/openapi/schema-tree.ts +44 -14
  288. package/src/components/openapi/security.ts +10 -0
  289. package/src/components/openapi/snippets.ts +5 -3
  290. package/src/components/openapi/validate-json.ts +4 -3
  291. package/src/components/powered-by-middleware.ts +33 -0
  292. package/src/core/changelog-index.ts +31 -1
  293. package/src/core/component-diagnostics.ts +53 -0
  294. package/src/core/component-overrides.ts +32 -0
  295. package/src/core/config-input.ts +42 -6
  296. package/src/core/data.ts +2 -0
  297. package/src/core/diagnostics.ts +5 -0
  298. package/src/core/i18n-ui.ts +2 -0
  299. package/src/core/includes.ts +1 -1
  300. package/src/core/links.ts +47 -17
  301. package/src/core/powered-by.ts +12 -0
  302. package/src/core/probe.ts +26 -6
  303. package/src/core/project-graph.ts +59 -18
  304. package/src/core/redirect-patterns.ts +36 -7
  305. package/src/core/schema.ts +26 -6
  306. package/src/core/sources/cache.ts +6 -0
  307. package/src/core/sources/contentful-rich-text.ts +29 -6
  308. package/src/core/sources/contentful.ts +135 -25
  309. package/src/core/sources/filesystem.ts +26 -4
  310. package/src/core/sources/github-releases.ts +12 -1
  311. package/src/core/sources/lexical.ts +15 -10
  312. package/src/core/sources/lower.ts +19 -3
  313. package/src/core/sources/mdx-remote.ts +62 -10
  314. package/src/core/sources/normalize.ts +227 -41
  315. package/src/core/sources/notion.ts +34 -10
  316. package/src/core/sources/obsidian.ts +16 -3
  317. package/src/core/sources/payload.ts +38 -15
  318. package/src/core/sources/remote.ts +72 -15
  319. package/src/core/sources/resolve.ts +11 -1
  320. package/src/core/sources/sanity.ts +43 -25
  321. package/src/core/sources/strapi.ts +2 -2
  322. package/src/core/types.ts +18 -0
  323. package/src/core/ui-packs/ar.ts +1 -0
  324. package/src/core/ui-packs/bg.ts +1 -0
  325. package/src/core/ui-packs/bn.ts +1 -0
  326. package/src/core/ui-packs/ca.ts +1 -0
  327. package/src/core/ui-packs/cs.ts +1 -0
  328. package/src/core/ui-packs/da.ts +1 -0
  329. package/src/core/ui-packs/de.ts +1 -0
  330. package/src/core/ui-packs/el.ts +1 -0
  331. package/src/core/ui-packs/es.ts +1 -0
  332. package/src/core/ui-packs/fa.ts +1 -0
  333. package/src/core/ui-packs/fi.ts +1 -0
  334. package/src/core/ui-packs/fr.ts +1 -0
  335. package/src/core/ui-packs/he.ts +1 -0
  336. package/src/core/ui-packs/hi.ts +1 -0
  337. package/src/core/ui-packs/hr.ts +1 -0
  338. package/src/core/ui-packs/hu.ts +1 -0
  339. package/src/core/ui-packs/id.ts +1 -0
  340. package/src/core/ui-packs/it.ts +1 -0
  341. package/src/core/ui-packs/ja.ts +1 -0
  342. package/src/core/ui-packs/ko.ts +1 -0
  343. package/src/core/ui-packs/nl.ts +1 -0
  344. package/src/core/ui-packs/no.ts +1 -0
  345. package/src/core/ui-packs/pl.ts +1 -0
  346. package/src/core/ui-packs/pt-br.ts +1 -0
  347. package/src/core/ui-packs/pt.ts +1 -0
  348. package/src/core/ui-packs/ro.ts +1 -0
  349. package/src/core/ui-packs/ru.ts +1 -0
  350. package/src/core/ui-packs/sk.ts +1 -0
  351. package/src/core/ui-packs/sr.ts +1 -0
  352. package/src/core/ui-packs/sv.ts +1 -0
  353. package/src/core/ui-packs/th.ts +1 -0
  354. package/src/core/ui-packs/tr.ts +1 -0
  355. package/src/core/ui-packs/uk.ts +1 -0
  356. package/src/core/ui-packs/vi.ts +1 -0
  357. package/src/core/ui-packs/zh-tw.ts +1 -0
  358. package/src/core/ui-packs/zh.ts +1 -0
  359. package/src/deploy/adapter-output.ts +7 -2
  360. package/src/deploy/adapters/cloudflare.ts +7 -5
  361. package/src/deploy/adapters/node.ts +47 -9
  362. package/src/deploy/adapters/types.ts +7 -3
  363. package/src/deploy/artifacts.ts +2 -1
  364. package/src/deploy/cloudflare-negotiation.ts +36 -7
  365. package/src/deploy/headers.ts +15 -1
  366. package/src/deploy/node-headers.ts +32 -20
  367. package/src/deploy/platforms/cloudflare.ts +14 -9
  368. package/src/deploy/platforms/netlify.ts +2 -1
  369. package/src/deploy/platforms/node.ts +12 -2
  370. package/src/deploy/platforms/types.ts +21 -7
  371. package/src/deploy/platforms/vercel.ts +115 -17
  372. package/src/deploy/redirects.ts +66 -6
  373. package/src/deploy/rss.ts +3 -1
  374. package/src/deploy/sitemap.ts +39 -5
  375. package/src/deploy/vercel-negotiation.ts +325 -103
  376. package/src/eval/agents.ts +18 -1
  377. package/src/eval/prompts.ts +6 -3
  378. package/src/eval/report.ts +34 -13
  379. package/src/eval/run.ts +5 -0
  380. package/src/markdown/base-links.ts +15 -14
  381. package/src/markdown/package-commands.ts +9 -8
  382. package/src/markdown/relative-links.ts +3 -2
  383. package/src/narration/build.ts +218 -40
  384. package/src/narration/provider.ts +89 -9
  385. package/src/openapi/checks.ts +273 -0
  386. package/src/openapi/graphql-build.ts +21 -0
  387. package/src/openapi/graphql.ts +11 -0
  388. package/src/openapi/model.ts +15 -3
  389. package/src/openapi/parse.ts +127 -2
  390. package/src/openapi/references.ts +43 -3
  391. package/src/openapi/render-mdx.ts +68 -8
  392. package/src/openapi/sentence.ts +29 -2
  393. package/src/openapi/source.ts +39 -14
  394. package/src/ratelimit/index.ts +1 -1
  395. package/src/ratelimit/runtime.ts +37 -19
  396. package/src/ratelimit/upstash.ts +40 -10
  397. package/src/reference/graphql.ts +15 -1
  398. package/src/reference/index.ts +1 -0
  399. package/src/reference/openapi.ts +2 -2
  400. package/src/reference/options.ts +29 -1
  401. package/src/registry/eject.ts +8 -2
  402. package/src/search/adapters/locale-scope.ts +30 -0
  403. package/src/search/adapters/typesense.ts +14 -4
  404. package/src/search/build.ts +21 -3
  405. package/src/search/orama-index.ts +163 -31
  406. package/src/search/source-pages.ts +29 -0
  407. package/src/search/sync/algolia.ts +99 -9
  408. package/src/search/sync/index.ts +52 -13
  409. package/src/search/sync/typesense.ts +72 -27
  410. package/src/seo/jsonld.ts +38 -20
  411. package/src/sources/contentful.ts +12 -0
  412. package/src/sources/github-releases.ts +7 -1
  413. package/src/sources/notion.ts +7 -0
  414. package/src/sources/shared.ts +3 -0
  415. package/src/theme/color.ts +9 -10
  416. package/src/theme/entry.ts +43 -6
  417. package/src/translate/validate.ts +23 -1
  418. package/dist/cli/chunk-0gz36akr.js.map +0 -12
  419. package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
  420. package/dist/cli/chunk-43zcf4gz.js.map +0 -36
  421. package/dist/cli/chunk-4ejxbzee.js +0 -39
  422. package/dist/cli/chunk-4ejxbzee.js.map +0 -10
  423. package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
  424. package/dist/cli/chunk-713mngdq.js.map +0 -14
  425. package/dist/cli/chunk-9xr4dgee.js.map +0 -10
  426. package/dist/cli/chunk-amrb6hgh.js +0 -145
  427. package/dist/cli/chunk-amrb6hgh.js.map +0 -11
  428. package/dist/cli/chunk-nd1821sk.js.map +0 -10
  429. package/dist/cli/chunk-rekqz4qg.js.map +0 -10
  430. package/dist/cli/chunk-ta27feev.js.map +0 -10
  431. package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
  432. package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
  433. package/dist/cli/chunk-zf748avv.js.map +0 -37
  434. /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
  435. /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
  436. /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
  437. /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
  438. /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
  439. /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
  440. /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
  441. /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
  442. /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
  443. /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
  444. /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
  445. /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
  446. /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
  447. /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
  448. /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
  449. /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
  450. /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
@@ -6,6 +6,7 @@ import {
6
6
  i18nDiagnostics,
7
7
  blumeReferences,
8
8
  frontmatter_default,
9
+ resolveLocalizable,
9
10
  resolveDocsCollection,
10
11
  CHANGELOG_INDEX_ROUTE,
11
12
  hasChangelogIndex,
@@ -22,6 +23,7 @@ import {
22
23
  pagesByRef,
23
24
  buildNavigation,
24
25
  PUBLIC_API_URL,
26
+ isPatternPath,
25
27
  pathsUnderPattern,
26
28
  hasVariables,
27
29
  substituteVariables,
@@ -33,6 +35,7 @@ import {
33
35
  slugify,
34
36
  slugifyPath,
35
37
  unloadablePathDiagnostic,
38
+ frontmatterYamlDiagnostic,
36
39
  extractHeadings,
37
40
  strippedLineOffset,
38
41
  isStringValue,
@@ -47,6 +50,7 @@ import {
47
50
  escapeMarkdownText,
48
51
  guardBlockStart,
49
52
  renderInline,
53
+ renderInlineMdx,
50
54
  destination,
51
55
  linkParts,
52
56
  headingPrefix,
@@ -73,7 +77,7 @@ import {
73
77
  createModuleLoader,
74
78
  resolveProjectContext,
75
79
  loadConfig
76
- } from "./chunk-xkz5xbtv.js";
80
+ } from "./chunk-124fvmzq.js";
77
81
  import {
78
82
  getBlumeVersion,
79
83
  BlumeError,
@@ -81,6 +85,7 @@ import {
81
85
  } from "./index.js";
82
86
  import {
83
87
  trimChar,
88
+ normalizePath,
84
89
  isInternalPath,
85
90
  withBasePath,
86
91
  mountBasePath
@@ -93,6 +98,232 @@ import {
93
98
  ignoringWatchListener
94
99
  } from "./chunk-kwrg63tm.js";
95
100
 
101
+ // src/theme/icon-kind.ts
102
+ var IMAGE_ICON = /^(?:https?:\/\/|data:image\/|\/|\.{1,2}\/)|\.(?:avif|gif|jpe?g|png|svg|webp)$/iu;
103
+ var INLINE_SVG = /^\s*<svg[\s\S]*<\/svg>\s*$/u;
104
+ var isImageIcon = (value) => IMAGE_ICON.test(value);
105
+ var isInlineSvg = (value) => INLINE_SVG.test(value);
106
+ var isAssetIcon = (value) => isInlineSvg(value) || isImageIcon(value);
107
+
108
+ // src/theme/icons.ts
109
+ import { createRequire } from "node:module";
110
+ import { getIconData, iconToSVG } from "@iconify/utils";
111
+ var requireJson = createRequire(import.meta.url);
112
+ var loadSet = (pkg) => requireJson(pkg);
113
+ var SETS = {
114
+ lucide: loadSet("@iconify-json/lucide/icons.json")
115
+ };
116
+ var DEFAULT_SET = "lucide";
117
+ var PREFIX_SETS = {
118
+ lucide: "lucide"
119
+ };
120
+ var ownEntry = (map, key) => Object.hasOwn(map, key) ? map[key] : undefined;
121
+ var normalize = (name) => name.trim().toLowerCase().replaceAll(/[\s_]+/gu, "-");
122
+ var fromSet = (setName, iconName) => {
123
+ const set = ownEntry(SETS, setName);
124
+ const data = set && getIconData(set, iconName);
125
+ if (!data) {
126
+ return null;
127
+ }
128
+ const { attributes, body } = iconToSVG(data, { height: "auto" });
129
+ return { body, name: iconName, viewBox: attributes.viewBox };
130
+ };
131
+ var resolveIcon = (name) => {
132
+ const normalized = normalize(name);
133
+ const colon = normalized.indexOf(":");
134
+ if (colon > 0) {
135
+ const setName = ownEntry(PREFIX_SETS, normalized.slice(0, colon));
136
+ return setName ? fromSet(setName, normalized.slice(colon + 1)) : null;
137
+ }
138
+ return fromSet(DEFAULT_SET, normalized);
139
+ };
140
+ var hasIcon = (name) => resolveIcon(name) !== null;
141
+
142
+ // src/core/nav-diagnostics.ts
143
+ var ICON_FORMAT_HINT = "Use a built-in icon name, an image path/URL, or inline SVG markup.";
144
+ var flattenNodes = (nodes) => nodes.flatMap((node) => node.kind === "group" ? [node, ...flattenNodes(node.children)] : [node]);
145
+ var collectIcons = (navigation) => {
146
+ const icons = [];
147
+ const push = (icon, where) => {
148
+ if (icon) {
149
+ icons.push({ icon, where });
150
+ }
151
+ };
152
+ for (const tab of navigation.tabs) {
153
+ push(tab.icon, `tab "${tab.label}"`);
154
+ for (const item of tab.items ?? []) {
155
+ push(item.icon, `tab item "${item.label}"`);
156
+ }
157
+ }
158
+ for (const selector of navigation.selectors) {
159
+ for (const item of selector.items) {
160
+ push(item.icon, `selector "${item.label}"`);
161
+ }
162
+ }
163
+ for (const link of navigation.featured) {
164
+ push(link.icon, `featured link "${link.label}"`);
165
+ }
166
+ const sidebars = [navigation.sidebar];
167
+ for (const sidebar of sidebars) {
168
+ for (const node of flattenNodes(sidebar)) {
169
+ push(node.icon, `"${node.label}"`);
170
+ }
171
+ }
172
+ return icons;
173
+ };
174
+ var unknownIconDiagnostics = (icons, suggestion) => {
175
+ const seen = new Set;
176
+ const diagnostics = [];
177
+ for (const { icon, where } of icons) {
178
+ if (isAssetIcon(icon) || hasIcon(icon) || seen.has(icon)) {
179
+ continue;
180
+ }
181
+ seen.add(icon);
182
+ const message = icon.trimStart().startsWith("<") ? `Icon markup "${icon}" (${where}) isn't a complete inline <svg> element, so it won't render.` : `Unknown icon "${icon}" (${where}) — it isn't in Blume's icon set.`;
183
+ diagnostics.push({
184
+ code: "BLUME_UNKNOWN_ICON",
185
+ message,
186
+ severity: "warning",
187
+ suggestion
188
+ });
189
+ }
190
+ return diagnostics;
191
+ };
192
+ var validateNavIcons = (navigation) => unknownIconDiagnostics(collectIcons(navigation), ICON_FORMAT_HINT);
193
+ var validateSearchPopularIcons = (popular) => {
194
+ const icons = popular.flatMap((link) => link.icon ? [{ icon: link.icon, where: `popular link "${link.label}"` }] : []);
195
+ return unknownIconDiagnostics(icons, ICON_FORMAT_HINT);
196
+ };
197
+ var resolvesToPages = (routes, path) => routes.has(path) || [...routes].some((route) => route.startsWith(`${path}/`));
198
+ var validateNavTargets = (navigation, routes) => {
199
+ const targets = [
200
+ ...navigation.tabs.map((tab) => ({ label: tab.label, path: tab.path })),
201
+ ...navigation.selectors.flatMap((selector) => selector.items.map((item) => ({ label: item.label, path: item.path }))),
202
+ ...navigation.featured.map((link) => ({
203
+ label: link.label,
204
+ path: link.href
205
+ })),
206
+ ...(navigation.actions ?? []).map((action) => ({
207
+ label: action.label,
208
+ path: action.href
209
+ })),
210
+ ...navigation.cta ? [{ label: navigation.cta.label, path: navigation.cta.href }] : []
211
+ ];
212
+ const diagnostics = [];
213
+ const seen = new Set;
214
+ for (const { label, path } of targets) {
215
+ if (!isInternalPath(path) || path.startsWith("/#") || seen.has(path)) {
216
+ continue;
217
+ }
218
+ if (!resolvesToPages(routes, path.split(/[?#]/u)[0] ?? path)) {
219
+ seen.add(path);
220
+ diagnostics.push({
221
+ code: "BLUME_NAV_MISSING_PAGE",
222
+ message: `Navigation entry "${label}" points to ${path}, but no page matches it.`,
223
+ severity: "warning",
224
+ suggestion: "Fix the path, or add a page at that route."
225
+ });
226
+ }
227
+ }
228
+ return diagnostics;
229
+ };
230
+ var validateSidebarItems = (items, resolves) => {
231
+ const diagnostics = [];
232
+ const walk = (list) => {
233
+ for (const item of list) {
234
+ if (isPageRef(item)) {
235
+ if (!resolves(normalizeRef(item))) {
236
+ diagnostics.push({
237
+ code: "BLUME_NAV_MISSING_PAGE",
238
+ message: `Sidebar entry "${item}" matches no page, so it's left out of the sidebar.`,
239
+ severity: "warning",
240
+ suggestion: "Fix the route, or add a page at it."
241
+ });
242
+ }
243
+ continue;
244
+ }
245
+ if (item.root && !resolves(normalizeRef(item.root))) {
246
+ diagnostics.push({
247
+ code: "BLUME_NAV_MISSING_PAGE",
248
+ message: `Sidebar entry "${item.label}" has root "${item.root}", but no page matches it, so its link leads nowhere.`,
249
+ severity: "warning",
250
+ suggestion: "Fix the root, or add a page at that route."
251
+ });
252
+ }
253
+ if (item.items) {
254
+ walk(item.items);
255
+ } else if (!(item.root || item.href)) {
256
+ diagnostics.push({
257
+ code: "BLUME_NAV_EMPTY_ITEM",
258
+ message: `Sidebar entry "${item.label}" has no page, href, root, or items, so it's left out of the sidebar.`,
259
+ severity: "warning",
260
+ suggestion: "Give it an href or a root to link somewhere, or items to make it a group."
261
+ });
262
+ }
263
+ }
264
+ };
265
+ walk(items);
266
+ return diagnostics;
267
+ };
268
+ var duplicateLabelDiagnostics = (navigation) => {
269
+ const diagnostics = [];
270
+ const checkLevel = (nodes, where) => {
271
+ const counts = new Map;
272
+ for (const node of nodes) {
273
+ counts.set(node.label, (counts.get(node.label) ?? 0) + 1);
274
+ }
275
+ for (const [label, count] of counts) {
276
+ if (count > 1) {
277
+ diagnostics.push({
278
+ code: "BLUME_NAV_DUPLICATE_LABEL",
279
+ message: `Duplicate sidebar label "${label}" appears ${count} times ${where}.`,
280
+ severity: "warning",
281
+ suggestion: "Give the entries distinct titles."
282
+ });
283
+ }
284
+ }
285
+ for (const node of nodes) {
286
+ if (node.kind === "group") {
287
+ checkLevel(node.children, `under "${node.label}"`);
288
+ }
289
+ }
290
+ };
291
+ const sidebars = [
292
+ { nodes: navigation.sidebar, where: "at the top level" }
293
+ ];
294
+ for (const { nodes, where } of sidebars) {
295
+ checkLevel(nodes, where);
296
+ }
297
+ return diagnostics;
298
+ };
299
+ var hiddenInSidebarDiagnostics = (navigation, pages) => {
300
+ const hidden = new Set(pages.flatMap((page) => page.meta.sidebar.hidden ? [page.id] : []));
301
+ if (hidden.size === 0) {
302
+ return [];
303
+ }
304
+ const sidebars = [navigation.sidebar];
305
+ const diagnostics = [];
306
+ const seen = new Set;
307
+ for (const sidebar of sidebars) {
308
+ for (const node of flattenNodes(sidebar)) {
309
+ if (node.kind === "page" && hidden.has(node.pageId) && !seen.has(node.pageId)) {
310
+ seen.add(node.pageId);
311
+ diagnostics.push({
312
+ code: "BLUME_NAV_HIDDEN_IN_SIDEBAR",
313
+ message: `Page "${node.label}" is marked hidden but appears in the sidebar (and its pagination).`,
314
+ severity: "warning",
315
+ suggestion: "Remove it from the navigation config, or unset sidebar.hidden."
316
+ });
317
+ }
318
+ }
319
+ }
320
+ return diagnostics;
321
+ };
322
+ var validateNavStructure = (navigation, pages) => [
323
+ ...duplicateLabelDiagnostics(navigation),
324
+ ...hiddenInSidebarDiagnostics(navigation, pages)
325
+ ];
326
+
96
327
  // src/core/manifest.ts
97
328
  var versionAlternateKey = (versionKey, locale) => `${versionKey}\x00${locale}`;
98
329
  var versionAlternatesFor = (byKey, versionKey, locale) => {
@@ -251,304 +482,69 @@ var LITERAL_MARKERS = new Map([
251
482
  var labelSource = (label) => label.length > 0 ? `[${mdastToString(label, { includeImageAlt: false })}]` : "";
252
483
  var attributeSource = (node) => {
253
484
  const attributes = Object.entries(node.attributes ?? {}).map(([key, value]) => value ? `${key}="${value}"` : key);
254
- return attributes.length > 0 ? `{${attributes.join(" ")}}` : "";
255
- };
256
- var spannedSource = (node, source) => {
257
- const start = node.position?.start?.offset;
258
- const end = node.position?.end?.offset;
259
- return start !== undefined && end !== undefined ? source.slice(start, end) : null;
260
- };
261
- var directiveSource = (node, marker, source) => {
262
- const opening = `${marker}${node.name}`;
263
- const slice = spannedSource(node, source);
264
- if (slice?.startsWith(opening)) {
265
- return slice;
266
- }
267
- return `${opening}${labelSource(node.children ?? [])}${attributeSource(node)}`;
268
- };
269
-
270
- // src/core/directive-diagnostics.ts
271
- var CONTAINER_OPENING = /^[\t >]*:{3,}(?<name>[a-z][\w-]*)/gimu;
272
- var codeList = (names) => [...names].map((name) => `\`${name}\``).join(", ");
273
- var CALLOUT_NAMES = `${codeList(CALLOUT_TYPES)} (or the aliases ${codeList(Object.keys(CALLOUT_ALIASES))})`;
274
- var unknownContainers = (text) => {
275
- const named = [...text.matchAll(CONTAINER_OPENING)].some((match) => calloutTypeFor(match.groups?.name ?? "") === null);
276
- if (!named) {
277
- return [];
278
- }
279
- let tree;
280
- try {
281
- tree = mdxToMdast(text, { features: MDX_BODY_FEATURES });
282
- } catch {
283
- return [];
284
- }
285
- const found = [];
286
- const walk = (node) => {
287
- if (node.type === "containerDirective" && calloutTypeFor(node.name) === null) {
288
- found.push({ line: node.position?.start.line ?? 1, name: node.name });
289
- }
290
- if ("children" in node) {
291
- for (const child of node.children) {
292
- walk(child);
293
- }
294
- }
295
- };
296
- walk(tree);
297
- return found;
298
- };
299
- var unknownDirectiveDiagnostics = (entry, sourceName) => {
300
- if (entry.body.format !== "mdx") {
301
- return [];
302
- }
303
- const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
304
- const offset = entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text);
305
- return unknownContainers(entry.expanded?.text ?? entry.body.text).map(({ line, name }) => {
306
- const origin = entry.expanded?.origins[line - 1];
307
- return {
308
- code: "BLUME_UNKNOWN_DIRECTIVE",
309
- file: origin?.file ?? page,
310
- line: origin?.line ?? line + offset,
311
- message: `\`:::${name}\` isn't a callout type, so the page shows its \`:::\` lines as written around its content.`,
312
- severity: "warning",
313
- suggestion: `Use a callout type — ${CALLOUT_NAMES} — or remove the \`:::\` lines to keep the content as plain prose.`
314
- };
315
- });
316
- };
317
-
318
- // src/core/localizable.ts
319
- var isSingleLabel = (label) => typeof label === "string";
320
- var resolveLocalizable = (label, locale, defaultLocale) => {
321
- if (isSingleLabel(label)) {
322
- return label;
323
- }
324
- return (locale === undefined ? undefined : label[locale]) ?? (defaultLocale === undefined ? undefined : label[defaultLocale]) ?? Object.values(label)[0] ?? "";
325
- };
326
-
327
- // src/theme/icon-kind.ts
328
- var IMAGE_ICON = /^(?:https?:\/\/|data:image\/|\/|\.{1,2}\/)|\.(?:avif|gif|jpe?g|png|svg|webp)$/iu;
329
- var INLINE_SVG = /^\s*<svg[\s\S]*<\/svg>\s*$/u;
330
- var isImageIcon = (value) => IMAGE_ICON.test(value);
331
- var isInlineSvg = (value) => INLINE_SVG.test(value);
332
- var isAssetIcon = (value) => isInlineSvg(value) || isImageIcon(value);
333
-
334
- // src/theme/icons.ts
335
- import { createRequire } from "node:module";
336
- import { getIconData, iconToSVG } from "@iconify/utils";
337
- var requireJson = createRequire(import.meta.url);
338
- var loadSet = (pkg) => requireJson(pkg);
339
- var SETS = {
340
- lucide: loadSet("@iconify-json/lucide/icons.json")
341
- };
342
- var DEFAULT_SET = "lucide";
343
- var PREFIX_SETS = {
344
- lucide: "lucide"
345
- };
346
- var ownEntry = (map, key) => Object.hasOwn(map, key) ? map[key] : undefined;
347
- var normalize = (name) => name.trim().toLowerCase().replaceAll(/[\s_]+/gu, "-");
348
- var fromSet = (setName, iconName) => {
349
- const set = ownEntry(SETS, setName);
350
- const data = set && getIconData(set, iconName);
351
- if (!data) {
352
- return null;
353
- }
354
- const { attributes, body } = iconToSVG(data, { height: "auto" });
355
- return { body, name: iconName, viewBox: attributes.viewBox };
356
- };
357
- var resolveIcon = (name) => {
358
- const normalized = normalize(name);
359
- const colon = normalized.indexOf(":");
360
- if (colon > 0) {
361
- const setName = ownEntry(PREFIX_SETS, normalized.slice(0, colon));
362
- return setName ? fromSet(setName, normalized.slice(colon + 1)) : null;
363
- }
364
- return fromSet(DEFAULT_SET, normalized);
365
- };
366
- var hasIcon = (name) => resolveIcon(name) !== null;
367
-
368
- // src/core/nav-diagnostics.ts
369
- var ICON_FORMAT_HINT = "Use a built-in icon name, an image path/URL, or inline SVG markup.";
370
- var flattenNodes = (nodes) => nodes.flatMap((node) => node.kind === "group" ? [node, ...flattenNodes(node.children)] : [node]);
371
- var collectIcons = (navigation) => {
372
- const icons = [];
373
- const push = (icon, where) => {
374
- if (icon) {
375
- icons.push({ icon, where });
376
- }
377
- };
378
- for (const tab of navigation.tabs) {
379
- push(tab.icon, `tab "${tab.label}"`);
380
- for (const item of tab.items ?? []) {
381
- push(item.icon, `tab item "${item.label}"`);
382
- }
383
- }
384
- for (const selector of navigation.selectors) {
385
- for (const item of selector.items) {
386
- push(item.icon, `selector "${item.label}"`);
387
- }
388
- }
389
- for (const link of navigation.featured) {
390
- push(link.icon, `featured link "${link.label}"`);
391
- }
392
- const sidebars = [navigation.sidebar];
393
- for (const sidebar of sidebars) {
394
- for (const node of flattenNodes(sidebar)) {
395
- push(node.icon, `"${node.label}"`);
396
- }
397
- }
398
- return icons;
399
- };
400
- var unknownIconDiagnostics = (icons, suggestion) => {
401
- const seen = new Set;
402
- const diagnostics = [];
403
- for (const { icon, where } of icons) {
404
- if (isAssetIcon(icon) || hasIcon(icon) || seen.has(icon)) {
405
- continue;
406
- }
407
- seen.add(icon);
408
- const message = icon.trimStart().startsWith("<") ? `Icon markup "${icon}" (${where}) isn't a complete inline <svg> element, so it won't render.` : `Unknown icon "${icon}" (${where}) — it isn't in Blume's icon set.`;
409
- diagnostics.push({
410
- code: "BLUME_UNKNOWN_ICON",
411
- message,
412
- severity: "warning",
413
- suggestion
414
- });
415
- }
416
- return diagnostics;
485
+ return attributes.length > 0 ? `{${attributes.join(" ")}}` : "";
417
486
  };
418
- var validateNavIcons = (navigation) => unknownIconDiagnostics(collectIcons(navigation), ICON_FORMAT_HINT);
419
- var validateSearchPopularIcons = (popular) => {
420
- const icons = popular.flatMap((link) => link.icon ? [{ icon: link.icon, where: `popular link "${link.label}"` }] : []);
421
- return unknownIconDiagnostics(icons, ICON_FORMAT_HINT);
487
+ var spannedSource = (node, source) => {
488
+ const start = node.position?.start?.offset;
489
+ const end = node.position?.end?.offset;
490
+ return start !== undefined && end !== undefined ? source.slice(start, end) : null;
422
491
  };
423
- var resolvesToPages = (routes, path) => routes.has(path) || [...routes].some((route) => route.startsWith(`${path}/`));
424
- var validateNavTargets = (navigation, routes) => {
425
- const targets = [
426
- ...navigation.tabs.map((tab) => ({ label: tab.label, path: tab.path })),
427
- ...navigation.selectors.flatMap((selector) => selector.items.map((item) => ({ label: item.label, path: item.path }))),
428
- ...navigation.featured.map((link) => ({
429
- label: link.label,
430
- path: link.href
431
- })),
432
- ...(navigation.actions ?? []).map((action) => ({
433
- label: action.label,
434
- path: action.href
435
- })),
436
- ...navigation.cta ? [{ label: navigation.cta.label, path: navigation.cta.href }] : []
437
- ];
438
- const diagnostics = [];
439
- const seen = new Set;
440
- for (const { label, path } of targets) {
441
- if (!isInternalPath(path) || path.startsWith("/#") || seen.has(path)) {
442
- continue;
443
- }
444
- if (!resolvesToPages(routes, path.split(/[?#]/u)[0] ?? path)) {
445
- seen.add(path);
446
- diagnostics.push({
447
- code: "BLUME_NAV_MISSING_PAGE",
448
- message: `Navigation entry "${label}" points to ${path}, but no page matches it.`,
449
- severity: "warning",
450
- suggestion: "Fix the path, or add a page at that route."
451
- });
452
- }
492
+ var directiveSource = (node, marker, source) => {
493
+ const opening = `${marker}${node.name}`;
494
+ const slice = spannedSource(node, source);
495
+ if (slice?.startsWith(opening)) {
496
+ return slice;
453
497
  }
454
- return diagnostics;
455
- };
456
- var validateSidebarItems = (items, resolves) => {
457
- const diagnostics = [];
458
- const walk = (list) => {
459
- for (const item of list) {
460
- if (isPageRef(item)) {
461
- if (!resolves(normalizeRef(item))) {
462
- diagnostics.push({
463
- code: "BLUME_NAV_MISSING_PAGE",
464
- message: `Sidebar entry "${item}" matches no page, so it's left out of the sidebar.`,
465
- severity: "warning",
466
- suggestion: "Fix the route, or add a page at it."
467
- });
468
- }
469
- continue;
470
- }
471
- if (item.root && !resolves(normalizeRef(item.root))) {
472
- diagnostics.push({
473
- code: "BLUME_NAV_MISSING_PAGE",
474
- message: `Sidebar entry "${item.label}" has root "${item.root}", but no page matches it, so its link leads nowhere.`,
475
- severity: "warning",
476
- suggestion: "Fix the root, or add a page at that route."
477
- });
478
- }
479
- if (item.items) {
480
- walk(item.items);
481
- } else if (!(item.root || item.href)) {
482
- diagnostics.push({
483
- code: "BLUME_NAV_EMPTY_ITEM",
484
- message: `Sidebar entry "${item.label}" has no page, href, root, or items, so it's left out of the sidebar.`,
485
- severity: "warning",
486
- suggestion: "Give it an href or a root to link somewhere, or items to make it a group."
487
- });
488
- }
489
- }
490
- };
491
- walk(items);
492
- return diagnostics;
498
+ return `${opening}${labelSource(node.children ?? [])}${attributeSource(node)}`;
493
499
  };
494
- var duplicateLabelDiagnostics = (navigation) => {
495
- const diagnostics = [];
496
- const checkLevel = (nodes, where) => {
497
- const counts = new Map;
498
- for (const node of nodes) {
499
- counts.set(node.label, (counts.get(node.label) ?? 0) + 1);
500
- }
501
- for (const [label, count] of counts) {
502
- if (count > 1) {
503
- diagnostics.push({
504
- code: "BLUME_NAV_DUPLICATE_LABEL",
505
- message: `Duplicate sidebar label "${label}" appears ${count} times ${where}.`,
506
- severity: "warning",
507
- suggestion: "Give the entries distinct titles."
508
- });
509
- }
500
+
501
+ // src/core/directive-diagnostics.ts
502
+ var CONTAINER_OPENING = /^[\t >]*:{3,}(?<name>[a-z][\w-]*)/gimu;
503
+ var codeList = (names) => [...names].map((name) => `\`${name}\``).join(", ");
504
+ var CALLOUT_NAMES = `${codeList(CALLOUT_TYPES)} (or the aliases ${codeList(Object.keys(CALLOUT_ALIASES))})`;
505
+ var unknownContainers = (text) => {
506
+ const named = [...text.matchAll(CONTAINER_OPENING)].some((match) => calloutTypeFor(match.groups?.name ?? "") === null);
507
+ if (!named) {
508
+ return [];
509
+ }
510
+ let tree;
511
+ try {
512
+ tree = mdxToMdast(text, { features: MDX_BODY_FEATURES });
513
+ } catch {
514
+ return [];
515
+ }
516
+ const found = [];
517
+ const walk = (node) => {
518
+ if (node.type === "containerDirective" && calloutTypeFor(node.name) === null) {
519
+ found.push({ line: node.position?.start.line ?? 1, name: node.name });
510
520
  }
511
- for (const node of nodes) {
512
- if (node.kind === "group") {
513
- checkLevel(node.children, `under "${node.label}"`);
521
+ if ("children" in node) {
522
+ for (const child of node.children) {
523
+ walk(child);
514
524
  }
515
525
  }
516
526
  };
517
- const sidebars = [
518
- { nodes: navigation.sidebar, where: "at the top level" }
519
- ];
520
- for (const { nodes, where } of sidebars) {
521
- checkLevel(nodes, where);
522
- }
523
- return diagnostics;
527
+ walk(tree);
528
+ return found;
524
529
  };
525
- var hiddenInSidebarDiagnostics = (navigation, pages) => {
526
- const hidden = new Set(pages.flatMap((page) => page.meta.sidebar.hidden ? [page.id] : []));
527
- if (hidden.size === 0) {
530
+ var unknownDirectiveDiagnostics = (entry, sourceName) => {
531
+ if (entry.body.format !== "mdx") {
528
532
  return [];
529
533
  }
530
- const sidebars = [navigation.sidebar];
531
- const diagnostics = [];
532
- const seen = new Set;
533
- for (const sidebar of sidebars) {
534
- for (const node of flattenNodes(sidebar)) {
535
- if (node.kind === "page" && hidden.has(node.pageId) && !seen.has(node.pageId)) {
536
- seen.add(node.pageId);
537
- diagnostics.push({
538
- code: "BLUME_NAV_HIDDEN_IN_SIDEBAR",
539
- message: `Page "${node.label}" is marked hidden but appears in the sidebar (and its pagination).`,
540
- severity: "warning",
541
- suggestion: "Remove it from the navigation config, or unset sidebar.hidden."
542
- });
543
- }
544
- }
545
- }
546
- return diagnostics;
534
+ const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
535
+ const offset = entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text);
536
+ return unknownContainers(entry.expanded?.text ?? entry.body.text).map(({ line, name }) => {
537
+ const origin = entry.expanded?.origins[line - 1];
538
+ return {
539
+ code: "BLUME_UNKNOWN_DIRECTIVE",
540
+ file: origin?.file ?? page,
541
+ line: origin?.line ?? line + offset,
542
+ message: `\`:::${name}\` isn't a callout type, so the page shows its \`:::\` lines as written around its content.`,
543
+ severity: "warning",
544
+ suggestion: `Use a callout type — ${CALLOUT_NAMES} — or remove the \`:::\` lines to keep the content as plain prose.`
545
+ };
546
+ });
547
547
  };
548
- var validateNavStructure = (navigation, pages) => [
549
- ...duplicateLabelDiagnostics(navigation),
550
- ...hiddenInSidebarDiagnostics(navigation, pages)
551
- ];
552
548
 
553
549
  // src/core/graph.ts
554
550
  var collectRoutes = (pages) => {
@@ -1016,7 +1012,9 @@ var assetFromEntry = (asset) => {
1016
1012
  return null;
1017
1013
  }
1018
1014
  return {
1015
+ contentType: asString(getPath(asset, "fields.file.contentType")),
1019
1016
  description: asString(getPath(asset, "fields.description")),
1017
+ fileName: asString(getPath(asset, "fields.file.fileName")),
1020
1018
  title: asString(getPath(asset, "fields.title")),
1021
1019
  url: absoluteUrl(url)
1022
1020
  };
@@ -1084,6 +1082,11 @@ var inlineNodeParts = (node, options) => {
1084
1082
  case "asset-hyperlink": {
1085
1083
  return linkParts(inlineParts(children(node), options), linkedAsset(node, options)?.url);
1086
1084
  }
1085
+ case "entry-hyperlink": {
1086
+ const entry = linkedTarget(node, options.resolveEntry);
1087
+ const href = entry ? options.entryHref?.(entry) : undefined;
1088
+ return linkParts(inlineParts(children(node), options), href);
1089
+ }
1087
1090
  case "embedded-entry-inline": {
1088
1091
  return [{ markdown: embeddedEntry(node, options) }];
1089
1092
  }
@@ -1135,7 +1138,14 @@ var renderBlock = (node, options) => {
1135
1138
  }
1136
1139
  case "embedded-asset-block": {
1137
1140
  const asset = linkedAsset(node, options);
1138
- return asset ? image(asset.description ?? asset.title ?? "", asset.url) : "";
1141
+ if (!asset) {
1142
+ return "";
1143
+ }
1144
+ if (asset.contentType && !asset.contentType.startsWith("image/")) {
1145
+ const label = asset.title ?? asset.fileName ?? asset.url;
1146
+ return renderInline(linkParts([{ marks: {}, text: label }], asset.url));
1147
+ }
1148
+ return image(asset.description ?? asset.title ?? "", asset.url);
1139
1149
  }
1140
1150
  case "embedded-entry-block": {
1141
1151
  return embeddedEntry(node, options);
@@ -1157,9 +1167,17 @@ var remoteSource = (options, ctx) => {
1157
1167
  let snapshot = new Map;
1158
1168
  const load = async (refresh = ctx?.refresh ?? true) => {
1159
1169
  options.assertConfigured?.();
1160
- const result = await loadWithCache(options.name, cache, options.fetchEntries, refresh);
1170
+ let warnings = [];
1171
+ const result = await loadWithCache(options.name, cache, async () => {
1172
+ const found = [];
1173
+ const entries = await options.fetchEntries((diagnostic) => {
1174
+ found.push(diagnostic);
1175
+ });
1176
+ warnings = found;
1177
+ return entries;
1178
+ }, refresh);
1161
1179
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
1162
- return result;
1180
+ return { ...result, diagnostics: [...result.diagnostics, ...warnings] };
1163
1181
  };
1164
1182
  const read = async (ref) => {
1165
1183
  const cached = snapshot.get(ref);
@@ -1190,6 +1208,12 @@ var stagedEntry = (slug, data, markdown, lastModified, format = "md") => {
1190
1208
  ref: `${slug}.${format}`
1191
1209
  };
1192
1210
  };
1211
+ var missingSecretError = (name, env) => new BlumeError({
1212
+ code: "BLUME_MISSING_SECRET",
1213
+ message: `Source "${name}" needs ${env}, which is not set.`,
1214
+ severity: "error",
1215
+ suggestion: `Set ${env} in .env.local for local dev, or in your host's environment for production.`
1216
+ });
1193
1217
  var REMOTE_TIMEOUT_MS = 30000;
1194
1218
  var fetchJson = async (url, client) => {
1195
1219
  const doFetch = client.fetchImpl ?? globalThis.fetch;
@@ -1211,9 +1235,9 @@ var queryString = (params) => {
1211
1235
  }
1212
1236
  return search.toString();
1213
1237
  };
1238
+ var documentSlug = (doc, slugField, id) => slugifyPath(asString(getPath(doc, slugField)) ?? id) || slugify(id) || "untitled";
1214
1239
  var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false) => {
1215
- const slugValue = asString(getPath(doc, fields.slug)) ?? id;
1216
- const slug = slugifyPath(slugValue) || slugify(id) || "untitled";
1240
+ const slug = documentSlug(doc, fields.slug, id);
1217
1241
  const data = {};
1218
1242
  const title = asString(getPath(doc, fields.title));
1219
1243
  const description = asString(getPath(doc, fields.description));
@@ -1226,6 +1250,10 @@ var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false)
1226
1250
  if (draft) {
1227
1251
  data.draft = true;
1228
1252
  }
1253
+ const order = fields.order ? asNumber(getPath(doc, fields.order)) : undefined;
1254
+ if (order !== undefined) {
1255
+ data.sidebar = { order };
1256
+ }
1229
1257
  const body = getPath(doc, fields.body);
1230
1258
  let markdown = "";
1231
1259
  let format = "md";
@@ -1240,8 +1268,8 @@ var documentEntry = (doc, fields, id, lower, draft = false, lowersToMdx = false)
1240
1268
  };
1241
1269
 
1242
1270
  // src/core/sources/contentful.ts
1243
- var DELIVERY_HOST = "https://cdn.contentful.com";
1244
- var PREVIEW_HOST = "https://preview.contentful.com";
1271
+ var DELIVERY_HOST = "cdn.contentful.com";
1272
+ var PREVIEW_HOST = "preview.contentful.com";
1245
1273
  var PAGE_SIZE = 100;
1246
1274
  var DEFAULT_FIELDS = {
1247
1275
  body: "body",
@@ -1251,20 +1279,50 @@ var DEFAULT_FIELDS = {
1251
1279
  title: "title"
1252
1280
  };
1253
1281
  var byId = (objects) => new Map(objects.map((object) => [asString(getPath(object, "sys.id")) ?? "", object]));
1282
+ var entryView = (item) => {
1283
+ const sys = asObject(item.sys) ?? {};
1284
+ return {
1285
+ id: asString(sys.id) ?? "",
1286
+ view: { ...asObject(item.fields), sys }
1287
+ };
1288
+ };
1254
1289
  var contentfulSource = (options, ctx) => {
1255
1290
  const fields = { ...DEFAULT_FIELDS, ...options.fields };
1256
- const toEntry = (item, assets, linked) => {
1257
- const sys = asObject(item.sys) ?? {};
1258
- const id = asString(sys.id) ?? "";
1259
- const view = { ...asObject(item.fields), sys };
1260
- return documentEntry(view, fields, id, (body) => isJsonObject(body) ? contentfulRichTextToMarkdown(body, {
1291
+ const routeOf = (view, id) => {
1292
+ const route = resolveEntryRoute({ ref: `${documentSlug(view, fields.slug, id)}.md` }, ".md", undefined, { i18n: options.i18n, prefix: options.prefix, versions: options.versions });
1293
+ return localizedRoute(route.logicalRoute, route.locales[0] ?? "", options.i18n);
1294
+ };
1295
+ const toEntry = (item, { assets, linked }, routes, warn) => {
1296
+ const { id, view } = entryView(item);
1297
+ const missing = new Set;
1298
+ const entry = documentEntry(view, fields, id, (body) => isJsonObject(body) ? contentfulRichTextToMarkdown(body, {
1299
+ entryHref: (target) => routes.get(asString(getPath(target, "sys.id")) ?? ""),
1261
1300
  resolveAsset: (assetId) => {
1262
1301
  const asset = assets.get(assetId);
1263
- return asset ? assetFromEntry(asset) : null;
1302
+ if (!asset) {
1303
+ missing.add(`asset "${assetId}"`);
1304
+ return null;
1305
+ }
1306
+ return assetFromEntry(asset);
1307
+ },
1308
+ resolveEntry: (entryId) => {
1309
+ const target = linked.get(entryId);
1310
+ if (!target) {
1311
+ missing.add(`entry "${entryId}"`);
1312
+ }
1313
+ return target ?? null;
1264
1314
  },
1265
- resolveEntry: (entryId) => linked.get(entryId) ?? null,
1266
1315
  serializers: options.serializers
1267
1316
  }) : "", false, writesMdx(options.serializers));
1317
+ for (const link of missing) {
1318
+ warn({
1319
+ code: "BLUME_SOURCE_UNRESOLVED_LINK",
1320
+ message: `Source "${options.name}": "${entry.ref}" links to Contentful ${link}, which the response didn't include, so the page renders without it.`,
1321
+ severity: "warning",
1322
+ suggestion: "Publish the linked asset or entry, or remove the link: the Delivery API returns only published content."
1323
+ });
1324
+ }
1325
+ return entry;
1268
1326
  };
1269
1327
  const preview = ctx?.preview ?? false;
1270
1328
  const previewToken = () => options.previewToken ?? process.env.CONTENTFUL_PREVIEW_TOKEN;
@@ -1278,15 +1336,18 @@ var contentfulSource = (options, ctx) => {
1278
1336
  });
1279
1337
  }
1280
1338
  };
1281
- const fetchEntries = async () => {
1339
+ const fetchEntries = async (warn) => {
1282
1340
  const token = preview ? previewToken() : options.token ?? process.env.CONTENTFUL_ACCESS_TOKEN;
1341
+ if (!token) {
1342
+ throw missingSecretError(options.name, "CONTENTFUL_ACCESS_TOKEN");
1343
+ }
1283
1344
  const client = {
1284
1345
  fetchImpl: options.fetchImpl,
1285
- headers: token ? { authorization: `Bearer ${token}` } : {}
1346
+ headers: { authorization: `Bearer ${token}` }
1286
1347
  };
1287
- const host = preview ? PREVIEW_HOST : DELIVERY_HOST;
1288
- const base = `${host}/spaces/${options.space}/environments/${options.environment ?? "master"}/entries`;
1289
- const entries = [];
1348
+ const host = preview ? options.previewHost ?? PREVIEW_HOST : options.host ?? DELIVERY_HOST;
1349
+ const base = `https://${host}/spaces/${options.space}/environments/${options.environment ?? "master"}/entries`;
1350
+ const pages = [];
1290
1351
  let skip = 0;
1291
1352
  let more = true;
1292
1353
  while (more) {
@@ -1302,16 +1363,20 @@ var contentfulSource = (options, ctx) => {
1302
1363
  if (!page) {
1303
1364
  throw new Error("Contentful returned a non-object response");
1304
1365
  }
1305
- const assets = byId(objectsIn(getPath(page, "includes.Asset")));
1306
- const linked = byId(objectsIn(getPath(page, "includes.Entry")));
1307
1366
  const items = objectsIn(page.items);
1308
- for (const item of items) {
1309
- entries.push(toEntry(item, assets, linked));
1310
- }
1367
+ pages.push({
1368
+ assets: byId(objectsIn(getPath(page, "includes.Asset"))),
1369
+ items,
1370
+ linked: byId([...objectsIn(getPath(page, "includes.Entry")), ...items])
1371
+ });
1311
1372
  skip += items.length;
1312
1373
  more = items.length > 0 && skip < (asNumber(page.total) ?? 0);
1313
1374
  }
1314
- return entries;
1375
+ const routes = new Map(pages.flatMap(({ items }) => items.map((item) => {
1376
+ const { id, view } = entryView(item);
1377
+ return [id, routeOf(view, id)];
1378
+ })));
1379
+ return pages.flatMap((page) => page.items.map((item) => toEntry(item, page, routes, warn)));
1315
1380
  };
1316
1381
  return remoteSource({
1317
1382
  assertConfigured,
@@ -1326,6 +1391,7 @@ var contentfulSource = (options, ctx) => {
1326
1391
  // src/core/sources/filesystem.ts
1327
1392
  import { existsSync, watch as fsWatch } from "node:fs";
1328
1393
  import { readFile } from "node:fs/promises";
1394
+ import { YAMLException } from "js-yaml";
1329
1395
  import { extname, isAbsolute, join as join2, relative as relative3, resolve } from "pathe";
1330
1396
  import { glob as glob2 } from "tinyglobby";
1331
1397
  var filesystemSource = (options) => {
@@ -1348,11 +1414,19 @@ var filesystemSource = (options) => {
1348
1414
  loadable.push(file);
1349
1415
  }
1350
1416
  }
1351
- const entries = await Promise.all(loadable.map(async (file) => {
1417
+ const read = await Promise.all(loadable.map(async (file) => {
1352
1418
  const source = await readFile(file, "utf-8");
1353
1419
  const ext = extname(file).toLowerCase();
1354
1420
  const format = ext === ".mdx" ? "mdx" : "md";
1355
- const parsed = frontmatter_default(source);
1421
+ let parsed;
1422
+ try {
1423
+ parsed = frontmatter_default(source);
1424
+ } catch (error) {
1425
+ if (error instanceof YAMLException) {
1426
+ return frontmatterYamlDiagnostic(error, file);
1427
+ }
1428
+ throw error;
1429
+ }
1356
1430
  return {
1357
1431
  body: { format, text: parsed.content },
1358
1432
  data: parsed.data,
@@ -1361,6 +1435,14 @@ var filesystemSource = (options) => {
1361
1435
  sourcePath: file
1362
1436
  };
1363
1437
  }));
1438
+ const entries = [];
1439
+ for (const item of read) {
1440
+ if ("body" in item) {
1441
+ entries.push(item);
1442
+ } else {
1443
+ diagnostics.push(item);
1444
+ }
1445
+ }
1364
1446
  return { diagnostics, entries };
1365
1447
  };
1366
1448
  const validate = () => {
@@ -1526,11 +1608,17 @@ var githubHeaders = () => {
1526
1608
  }
1527
1609
  return headers;
1528
1610
  };
1611
+ var releaseCategory = (release) => {
1612
+ if (release.draft) {
1613
+ return "Draft";
1614
+ }
1615
+ return release.prerelease ? "Prerelease" : "Release";
1616
+ };
1529
1617
  var releaseToEntry = (release, site) => {
1530
1618
  const version = release.tag_name.replace(LEADING_V, "");
1531
1619
  const title = release.name?.trim() || release.tag_name;
1532
1620
  const date = release.published_at ?? release.created_at;
1533
- const category = release.prerelease ? "Prerelease" : "Release";
1621
+ const category = releaseCategory(release);
1534
1622
  const body = liftHeadings(relativizeOwnLinks(neutralizeUnsafeLinks((release.body ?? "").replaceAll(`\r
1535
1623
  `, `
1536
1624
  `)).trim(), site));
@@ -1641,6 +1729,11 @@ var githubReleasesSource = (options, ctx) => {
1641
1729
 
1642
1730
  // src/core/sources/mdx-remote.ts
1643
1731
  import picomatch from "picomatch";
1732
+ var includeMatcher = (include) => {
1733
+ const included = picomatch(include.filter((pattern) => !pattern.startsWith("!")));
1734
+ const excluded = picomatch(include.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1)));
1735
+ return (ref) => included(ref) && !excluded(ref);
1736
+ };
1644
1737
  var GITHUB_HOSTS = new Set(["api.github.com", "raw.githubusercontent.com"]);
1645
1738
  var githubHeaders2 = (url) => {
1646
1739
  const token = process.env.GITHUB_TOKEN;
@@ -1679,7 +1772,7 @@ var enumerateGithub = async (github, include, doFetch) => {
1679
1772
  const text = await fetchText(treeUrl, doFetch);
1680
1773
  const body = JSON.parse(text);
1681
1774
  const prefix = base ? `${base}/` : "";
1682
- const included = picomatch(include);
1775
+ const included = includeMatcher(include);
1683
1776
  const refs = (body.tree ?? []).flatMap((node) => {
1684
1777
  if (!(node.type === "blob" && node.path.startsWith(prefix))) {
1685
1778
  return [];
@@ -1696,7 +1789,13 @@ var enumerateGithub = async (github, include, doFetch) => {
1696
1789
  }
1697
1790
  ];
1698
1791
  });
1699
- return { refs, truncated: body.truncated === true };
1792
+ const truncated = body.truncated === true;
1793
+ const found = prefix === "" || truncated || (body.tree ?? []).some((node) => node.path.startsWith(prefix));
1794
+ return {
1795
+ missing: found ? undefined : `"${base}" in ${owner}/${repo} at ${ref}`,
1796
+ refs,
1797
+ truncated
1798
+ };
1700
1799
  };
1701
1800
  var mdxRemoteSource = (options, ctx) => {
1702
1801
  const doFetch = options.fetchImpl ?? globalThis.fetch;
@@ -1717,7 +1816,7 @@ var mdxRemoteSource = (options, ctx) => {
1717
1816
  return await enumerateGithub(options.github, options.include, doFetch);
1718
1817
  }
1719
1818
  const base = (options.url ?? "").replace(/\/$/u, "");
1720
- const included = picomatch(options.include);
1819
+ const included = includeMatcher(options.include);
1721
1820
  const refs = (options.files ?? []).flatMap((ref) => included(ref) ? [{ editUrl: `${base}/${ref}`, fetchUrl: `${base}/${ref}`, ref }] : []);
1722
1821
  return { refs, truncated: false };
1723
1822
  };
@@ -1738,12 +1837,21 @@ var mdxRemoteSource = (options, ctx) => {
1738
1837
  assertConfigured();
1739
1838
  const skipped = [];
1740
1839
  const result = await loadWithCache(options.name, cache, async () => {
1741
- const { refs, truncated } = await enumerate();
1840
+ const { missing, refs, truncated } = await enumerate();
1841
+ if (missing) {
1842
+ skipped.push({
1843
+ code: "BLUME_SOURCE_PATH_MISSING",
1844
+ message: `Source "${options.name}" read no files: there is no folder ${missing}.`,
1845
+ severity: "warning",
1846
+ suggestion: "Check github.path and github.ref against the repository."
1847
+ });
1848
+ }
1742
1849
  if (truncated) {
1743
1850
  skipped.push({
1744
1851
  code: "BLUME_SOURCE_TRUNCATED",
1745
- message: `Source "${options.name}" hit GitHub's tree listing limit; some files were not enumerated. Narrow the source path or split the repo.`,
1746
- severity: "warning"
1852
+ message: `Source "${options.name}" hit GitHub's tree listing limit for the whole repository; some files were not enumerated.`,
1853
+ severity: "warning",
1854
+ suggestion: "List the files to read with { url, files } instead, or split the repository."
1747
1855
  });
1748
1856
  }
1749
1857
  const reasons = [];
@@ -1965,7 +2073,7 @@ var materializeAssets = async (markdown, ctx) => {
1965
2073
  };
1966
2074
 
1967
2075
  // src/core/sources/notion.ts
1968
- var richToMarkdown = (rich = []) => {
2076
+ var richToMarkdown = (rich = [], mdx = false) => {
1969
2077
  const linked = [];
1970
2078
  for (const { annotations = {}, href, plain_text: text } of rich) {
1971
2079
  const run = {
@@ -1984,8 +2092,10 @@ var richToMarkdown = (rich = []) => {
1984
2092
  linked.push({ href: href ?? undefined, runs: [run] });
1985
2093
  }
1986
2094
  }
1987
- return renderInline(linked.flatMap(({ href, runs }) => linkParts(runs, href)));
2095
+ const parts = linked.flatMap(({ href, runs }) => linkParts(runs, href, mdx));
2096
+ return mdx ? renderInlineMdx(parts) : renderInline(parts);
1988
2097
  };
2098
+ var blockText2 = (rich, mdx) => mdx ? richToMarkdown(rich, true) : guardBlockStart(richToMarkdown(rich));
1989
2099
  var isBlockPayload = (value) => typeof value === "object";
1990
2100
  var payloadOf = (block) => {
1991
2101
  const value = block[block.type];
@@ -2044,9 +2154,9 @@ var renderVideo = (data) => {
2044
2154
  ${media}
2045
2155
  </Frame>` : media;
2046
2156
  };
2047
- var renderLeaf = (block) => {
2157
+ var renderLeaf = (block, mdx) => {
2048
2158
  const data = payloadOf(block) ?? {};
2049
- const text = guardBlockStart(richToMarkdown(blockField(block)));
2159
+ const text = blockText2(blockField(block), mdx);
2050
2160
  switch (block.type) {
2051
2161
  case "paragraph": {
2052
2162
  return text;
@@ -2092,6 +2202,7 @@ var renderLeaf = (block) => {
2092
2202
  };
2093
2203
  var notionSource = (options, ctx) => {
2094
2204
  const props = options.properties ?? {};
2205
+ const textAsMdx = options.mdx ?? false;
2095
2206
  const limit = pLimit(Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY));
2096
2207
  const downloads = pLimit(ASSET_DOWNLOAD_CONCURRENCY);
2097
2208
  const inFlight = new Map;
@@ -2115,7 +2226,11 @@ var notionSource = (options, ctx) => {
2115
2226
  severity: "error"
2116
2227
  });
2117
2228
  }
2118
- return new Client({ auth: options.token ?? process.env.NOTION_TOKEN });
2229
+ const auth = options.token ?? process.env.NOTION_TOKEN;
2230
+ if (!auth) {
2231
+ throw missingSecretError(options.name, "NOTION_TOKEN");
2232
+ }
2233
+ return new Client({ auth });
2119
2234
  };
2120
2235
  const childrenOf = (client, blockId) => collectAll((cursor) => notionCall(() => client.blocks.children.list({ block_id: blockId, start_cursor: cursor })));
2121
2236
  const renderContainer = async (client, block, render) => {
@@ -2127,7 +2242,7 @@ var notionSource = (options, ctx) => {
2127
2242
  };
2128
2243
  if (block.type === "callout") {
2129
2244
  const body = [
2130
- guardBlockStart(richToMarkdown(blockField(block))),
2245
+ blockText2(blockField(block), textAsMdx),
2131
2246
  await children(block)
2132
2247
  ].filter(Boolean).join(`
2133
2248
 
@@ -2158,7 +2273,7 @@ ${rendered.join(`
2158
2273
  };
2159
2274
  const renderBlocks = async (client, blocks) => {
2160
2275
  const parts = await Promise.all(blocks.map(async (block) => {
2161
- const leaf = renderLeaf(block);
2276
+ const leaf = renderLeaf(block, textAsMdx);
2162
2277
  if (leaf === null) {
2163
2278
  return renderContainer(client, block, renderBlocks);
2164
2279
  }
@@ -2311,6 +2426,7 @@ ${nested}`;
2311
2426
  // src/core/sources/obsidian.ts
2312
2427
  import { existsSync as existsSync2, watch as fsWatch2, statSync } from "node:fs";
2313
2428
  import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
2429
+ import { YAMLException as YAMLException2 } from "js-yaml";
2314
2430
  import { basename as basename2, isAbsolute as isAbsolute2, join as join5, relative as relative4, resolve as resolve2 } from "pathe";
2315
2431
  var MARKDOWN_FILE = /\.md$/iu;
2316
2432
  var OBSIDIAN_COMMENT = /%%.*?%%/gu;
@@ -2643,6 +2759,9 @@ var readNote = async (vaultDir, rel, options) => {
2643
2759
  rel
2644
2760
  };
2645
2761
  } catch (error) {
2762
+ if (error instanceof YAMLException2) {
2763
+ return frontmatterYamlDiagnostic(error, absPath);
2764
+ }
2646
2765
  if (error.code === "ENOENT") {
2647
2766
  return null;
2648
2767
  }
@@ -2733,7 +2852,8 @@ var obsidianSource = (options, ctx) => {
2733
2852
  }
2734
2853
  }
2735
2854
  const read = await Promise.all(files.map((rel) => readNote(vaultDir, rel, options)));
2736
- const notes = read.filter((note) => note !== null);
2855
+ const notes = read.filter((note) => note !== null && ("rel" in note));
2856
+ skipped.push(...read.filter((note) => note !== null && ("code" in note)));
2737
2857
  const { index, pairs } = buildLinkIndex(notes, options);
2738
2858
  const unresolved = noTargets();
2739
2859
  const entries = pairs.map((pair) => noteToEntry(pair, index, options, unresolved));
@@ -2809,6 +2929,10 @@ var marksOf2 = (node) => {
2809
2929
  };
2810
2930
  };
2811
2931
  var children2 = (node) => objectsIn(node.children);
2932
+ var leftOut = (what, options) => {
2933
+ options.onUnsupported?.(what);
2934
+ return unsupported(what, writesMdx(options.serializers));
2935
+ };
2812
2936
  var typeOf = (node) => asString(node.type) ?? "";
2813
2937
  var customBlock = (node, options) => {
2814
2938
  const fields = asObject(node.fields);
@@ -2817,7 +2941,7 @@ var customBlock = (node, options) => {
2817
2941
  if (fields && serializer) {
2818
2942
  return serializer(fields);
2819
2943
  }
2820
- return unsupported(`Lexical block${blockType ? `: ${blockType}` : ""}`, writesMdx(options.serializers));
2944
+ return leftOut(`Lexical block${blockType ? `: ${blockType}` : ""}`, options);
2821
2945
  };
2822
2946
  var inlineParts2 = (nodes, options) => nodes.flatMap((node) => inlineNodeParts2(node, options));
2823
2947
  var inlineNodeParts2 = (node, options) => {
@@ -2845,12 +2969,12 @@ var inlineNodeParts2 = (node, options) => {
2845
2969
  }
2846
2970
  };
2847
2971
  var inlineText = (nodes, options) => guardBlockStart(renderInline(inlineParts2(nodes, options)));
2848
- var blockText2 = (node, options) => inlineText(children2(node), options);
2972
+ var blockText3 = (node, options) => inlineText(children2(node), options);
2849
2973
  var renderUpload = (node, options) => {
2850
2974
  const value = asObject(node.value);
2851
2975
  const url = value ? asString(value.url) : undefined;
2852
2976
  if (!(value && url)) {
2853
- return unsupported("Lexical upload (fetch with depth 1 to populate it)", writesMdx(options.serializers));
2977
+ return leftOut("Lexical upload (fetch with depth 1 to populate it)", options);
2854
2978
  }
2855
2979
  const alt = asString(value.alt) ?? asString(value.filename) ?? "";
2856
2980
  return image(alt, absoluteUrl(url, options.baseUrl));
@@ -2887,14 +3011,14 @@ ${sub}` : line));
2887
3011
  var renderBlock2 = (node, options) => {
2888
3012
  switch (typeOf(node)) {
2889
3013
  case "paragraph": {
2890
- return blockText2(node, options);
3014
+ return blockText3(node, options);
2891
3015
  }
2892
3016
  case "heading": {
2893
3017
  const level = Number((asString(node.tag) ?? "h1").slice(1));
2894
- return `${headingPrefix(level)}${blockText2(node, options)}`;
3018
+ return `${headingPrefix(level)}${blockText3(node, options)}`;
2895
3019
  }
2896
3020
  case "quote": {
2897
- return blockquote(blockText2(node, options));
3021
+ return blockquote(blockText3(node, options));
2898
3022
  }
2899
3023
  case "list": {
2900
3024
  return renderList2(node, options);
@@ -2909,7 +3033,7 @@ var renderBlock2 = (node, options) => {
2909
3033
  return customBlock(node, options);
2910
3034
  }
2911
3035
  default: {
2912
- return unsupported(`Lexical node: ${typeOf(node)}`, writesMdx(options.serializers));
3036
+ return leftOut(`Lexical node: ${typeOf(node)}`, options);
2913
3037
  }
2914
3038
  }
2915
3039
  };
@@ -2930,11 +3054,26 @@ var DEFAULT_FIELDS2 = {
2930
3054
  var payloadSource = (options, ctx) => {
2931
3055
  const fields = { ...DEFAULT_FIELDS2, ...options.fields };
2932
3056
  const origin = options.url.replace(/\/+$/u, "");
2933
- const lower = (body) => lexicalToMarkdown(body, {
3057
+ const lower = (body, leftOut) => lexicalToMarkdown(body, {
2934
3058
  baseUrl: origin,
3059
+ onUnsupported: (what) => leftOut.add(what),
2935
3060
  serializers: options.serializers
2936
3061
  });
2937
- const fetchEntries = async () => {
3062
+ const toEntry = (doc, draft, warn) => {
3063
+ const leftOut = new Set;
3064
+ const id = asString(doc.id) ?? String(doc.id ?? "");
3065
+ const entry = documentEntry(doc, fields, id, (body) => isJsonObject(body) ? lower(body, leftOut) : "", draft, writesMdx(options.serializers));
3066
+ for (const what of leftOut) {
3067
+ warn({
3068
+ code: "BLUME_SOURCE_UNSUPPORTED_NODE",
3069
+ message: `Source "${options.name}": "${entry.ref}" has content Blume has no Markdown for (${what}), left out as a comment.`,
3070
+ severity: "warning",
3071
+ suggestion: "Map a block type to a component with a serializer on payloadSource, passed to custom(); other nodes have no Markdown equivalent."
3072
+ });
3073
+ }
3074
+ return entry;
3075
+ };
3076
+ const fetchEntries = async (warn) => {
2938
3077
  const preview = ctx?.preview ?? false;
2939
3078
  const token = options.token ?? process.env.PAYLOAD_API_KEY;
2940
3079
  const client = {
@@ -2964,8 +3103,7 @@ var payloadSource = (options, ctx) => {
2964
3103
  if (draft && !preview) {
2965
3104
  continue;
2966
3105
  }
2967
- const id = asString(doc.id) ?? String(doc.id ?? "");
2968
- entries.push(documentEntry(doc, fields, id, (body) => isJsonObject(body) ? lower(body) : "", draft, writesMdx(options.serializers)));
3106
+ entries.push(toEntry(doc, draft, warn));
2969
3107
  }
2970
3108
  more = result.hasNextPage === true;
2971
3109
  page += 1;
@@ -3126,15 +3264,22 @@ var sanitySource = (options, ctx) => {
3126
3264
  if (description) {
3127
3265
  data.description = description;
3128
3266
  }
3129
- const blocks = getPath(doc, fields.body ?? "body") ?? [];
3130
- const markdown = Array.isArray(blocks) ? portableTextToMarkdown(blocks, {
3131
- imageUrl: (block) => {
3132
- const ref = block.asset?._ref;
3133
- return ref ? imageUrlFromRef(ref, options.projectId, options.dataset) : null;
3134
- },
3135
- serializers: options.serializers
3136
- }) : "";
3137
- const format = writesMdx(options.serializers) ? "mdx" : "md";
3267
+ const body = getPath(doc, fields.body ?? "body");
3268
+ let markdown = "";
3269
+ let format = writesMdx(options.serializers) ? "mdx" : "md";
3270
+ if (isStringValue2(body)) {
3271
+ markdown = neutralizeUnsafeLinks(`${body.trimEnd()}
3272
+ `);
3273
+ format = "md";
3274
+ } else if (Array.isArray(body)) {
3275
+ markdown = portableTextToMarkdown(body, {
3276
+ imageUrl: (block) => {
3277
+ const ref = block.asset?._ref;
3278
+ return ref ? imageUrlFromRef(ref, options.projectId, options.dataset) : null;
3279
+ },
3280
+ serializers: options.serializers
3281
+ });
3282
+ }
3138
3283
  const raw = frontmatter_default.stringify(markdown, data);
3139
3284
  return {
3140
3285
  body: { format, text: markdown },
@@ -3146,13 +3291,22 @@ var sanitySource = (options, ctx) => {
3146
3291
  };
3147
3292
  };
3148
3293
  const load = async (refresh = ctx?.refresh ?? true) => {
3294
+ const empty = [];
3149
3295
  const result = await loadWithCache(options.name, cache, async () => {
3150
3296
  const client = resolveClient(options, ctx?.preview ?? false);
3151
3297
  const docs = await client.fetch(options.query);
3298
+ if (docs.length === 0 && !(options.token ?? process.env.SANITY_TOKEN)) {
3299
+ empty.push({
3300
+ code: "BLUME_MISSING_SECRET",
3301
+ message: `Source "${options.name}" found no documents, and SANITY_TOKEN is not set. A private dataset returns nothing to a query without a token.`,
3302
+ severity: "warning",
3303
+ suggestion: "Set SANITY_TOKEN to a token with read access if the dataset is private."
3304
+ });
3305
+ }
3152
3306
  return docs.map(toEntry);
3153
3307
  }, refresh);
3154
3308
  snapshot = new Map(result.entries.map((entry) => [entry.ref, entry]));
3155
- return result;
3309
+ return { ...result, diagnostics: [...result.diagnostics, ...empty] };
3156
3310
  };
3157
3311
  const read = async (ref) => {
3158
3312
  const cached = snapshot.get(ref);
@@ -3200,12 +3354,12 @@ var inlineNodeParts3 = (node) => {
3200
3354
  }
3201
3355
  }
3202
3356
  };
3203
- var blockText3 = (nodes) => guardBlockStart(renderInline(inlineParts3(nodes)));
3357
+ var blockText4 = (nodes) => guardBlockStart(renderInline(inlineParts3(nodes)));
3204
3358
  var renderList3 = (node) => {
3205
3359
  const ordered = asString(node.format) === "ordered";
3206
3360
  return children3(node).map((item, index) => {
3207
3361
  const kids = children3(item);
3208
- const line = blockText3(kids.filter((kid) => typeOf2(kid) !== "list"));
3362
+ const line = blockText4(kids.filter((kid) => typeOf2(kid) !== "list"));
3209
3363
  const sub = kids.filter((kid) => typeOf2(kid) === "list").map((list) => renderList3(list)).join(`
3210
3364
  `);
3211
3365
  const body = sub ? `${line}
@@ -3225,13 +3379,13 @@ var renderImage = (node, options) => {
3225
3379
  var renderBlock4 = (node, options) => {
3226
3380
  switch (typeOf2(node)) {
3227
3381
  case "paragraph": {
3228
- return blockText3(children3(node));
3382
+ return blockText4(children3(node));
3229
3383
  }
3230
3384
  case "heading": {
3231
- return `${headingPrefix(asNumber(node.level) ?? 1)}${blockText3(children3(node))}`;
3385
+ return `${headingPrefix(asNumber(node.level) ?? 1)}${blockText4(children3(node))}`;
3232
3386
  }
3233
3387
  case "quote": {
3234
- return blockquote(blockText3(children3(node)));
3388
+ return blockquote(blockText4(children3(node)));
3235
3389
  }
3236
3390
  case "list": {
3237
3391
  return renderList3(node);
@@ -3389,7 +3543,12 @@ var buildSource = (adapter, name, config, context, runtime) => {
3389
3543
  return notionSource({ ...adapter.options, name }, ctx());
3390
3544
  }
3391
3545
  case "contentful": {
3392
- return contentfulSource({ ...adapter.options, name }, ctx());
3546
+ return contentfulSource({
3547
+ ...adapter.options,
3548
+ i18n: config.i18n,
3549
+ name,
3550
+ versions: config.versions
3551
+ }, ctx());
3393
3552
  }
3394
3553
  case "payload": {
3395
3554
  return payloadSource({ ...adapter.options, name }, ctx());
@@ -3543,7 +3702,20 @@ var substituteEntryVariables = (entry, variables) => {
3543
3702
  };
3544
3703
  var substituteLoadedVariables = (loaded, variables) => hasVariables(variables) ? loaded.flatMap(({ entries }) => entries.flatMap((entry) => substituteEntryVariables(entry, variables))) : [];
3545
3704
  var redirectPageDiagnostics = (config, pages, configFile) => config.redirects.flatMap((redirect) => {
3546
- const matched = pathsUnderPattern(withBasePath(config.basePath, redirect.from), pages);
3705
+ const from = withBasePath(config.basePath, redirect.from);
3706
+ if (!isPatternPath(redirect.from)) {
3707
+ const page = normalizePath(from);
3708
+ return pages.includes(page) ? [
3709
+ {
3710
+ code: "BLUME_REDIRECT_MATCHES_PAGE",
3711
+ file: configFile ?? undefined,
3712
+ message: `The redirect from ${redirect.from} is also the page ${page}, which never publishes: its URL redirects to ${redirect.to} instead.`,
3713
+ severity: "warning",
3714
+ suggestion: "If the page moved, delete it or give it a new slug; otherwise remove the redirect."
3715
+ }
3716
+ ] : [];
3717
+ }
3718
+ const matched = pathsUnderPattern(from, pages);
3547
3719
  const [first] = matched;
3548
3720
  if (first === undefined) {
3549
3721
  return [];
@@ -3560,6 +3732,14 @@ var redirectPageDiagnostics = (config, pages, configFile) => config.redirects.fl
3560
3732
  ];
3561
3733
  });
3562
3734
  var bannerLinkHref = (banner) => banner === undefined || isBannerShorthand(banner) ? undefined : banner.link?.href;
3735
+ var validatedSources = (config, context, runtime, beforeSources) => {
3736
+ beforeSources?.(config);
3737
+ const sources = resolveSources(config, context, runtime);
3738
+ for (const source of sources) {
3739
+ source.validate?.();
3740
+ }
3741
+ return sources;
3742
+ };
3563
3743
  var scanProject = async (root, options = {}) => {
3564
3744
  const mode = options.mode ?? "dev";
3565
3745
  const preview = options.preview ?? false;
@@ -3570,14 +3750,7 @@ var scanProject = async (root, options = {}) => {
3570
3750
  const context = resolveProjectContext(root, config, {
3571
3751
  runtimeDir: options.runtimeDir
3572
3752
  });
3573
- const sources = resolveSources(config, context, {
3574
- mode,
3575
- preview,
3576
- refresh: options.refresh
3577
- });
3578
- for (const source of sources) {
3579
- source.validate?.();
3580
- }
3753
+ const sources = validatedSources(config, context, { mode, preview, refresh: options.refresh }, options.beforeSources);
3581
3754
  const metaSources = sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [
3582
3755
  {
3583
3756
  exclude: source.exclude,
@@ -3681,7 +3854,7 @@ var scanProject = async (root, options = {}) => {
3681
3854
  };
3682
3855
  };
3683
3856
 
3684
- export { directiveSource, resolveLocalizable, validateSearchPopularIcons, validateNavTargets, contentIndexable, buildManifest, findFolderMetaFiles, isWithin, scanProject };
3857
+ export { directiveSource, validateSearchPopularIcons, validateNavTargets, contentIndexable, buildManifest, findFolderMetaFiles, isWithin, scanProject };
3685
3858
 
3686
- //# debugId=89E5C13A316C75D864756E2164756E21
3687
- //# sourceMappingURL=chunk-43zcf4gz.js.map
3859
+ //# debugId=D6EC9010B970FDF164756E2164756E21
3860
+ //# sourceMappingURL=chunk-0pmb4tr4.js.map