blume 2.0.1 → 2.0.3

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 (492) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
  3. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
  4. package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
  5. package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
  6. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
  7. package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
  8. package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
  9. package/dist/cli/chunk-35d4wj9f.js.map +15 -0
  10. package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
  11. package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
  12. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
  13. package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
  14. package/dist/cli/chunk-5f86nr5m.js.map +11 -0
  15. package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
  16. package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
  17. package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
  18. package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
  19. package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
  20. package/dist/cli/chunk-6dsbexzp.js.map +10 -0
  21. package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
  22. package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
  23. package/dist/cli/chunk-9t7a85s3.js.map +10 -0
  24. package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
  25. package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
  26. package/dist/cli/chunk-acanzt5p.js.map +10 -0
  27. package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
  28. package/dist/cli/chunk-akbpwfxc.js.map +10 -0
  29. package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
  30. package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
  31. package/dist/cli/chunk-c8chx29p.js.map +36 -0
  32. package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
  33. package/dist/cli/chunk-crgn1q09.js.map +10 -0
  34. package/dist/cli/chunk-e04dxsz1.js +39 -0
  35. package/dist/cli/chunk-e04dxsz1.js.map +10 -0
  36. package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
  37. package/dist/cli/chunk-ey84smr6.js.map +10 -0
  38. package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
  39. package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
  40. package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
  41. package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
  42. package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
  43. package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
  44. package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
  45. package/dist/cli/chunk-j85vccga.js.map +36 -0
  46. package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
  47. package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
  48. package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
  49. package/dist/cli/chunk-p73c0m7w.js.map +11 -0
  50. package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
  51. package/dist/cli/chunk-pehfxfta.js.map +10 -0
  52. package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
  53. package/dist/cli/chunk-pv29h0wf.js.map +190 -0
  54. package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
  55. package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
  56. package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
  57. package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
  58. package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
  59. package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
  60. package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
  61. package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
  62. package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
  63. package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
  64. package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
  65. package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
  66. package/dist/cli/chunk-yfyb25rh.js.map +14 -0
  67. package/dist/cli/index.js +162 -35
  68. package/dist/cli/index.js.map +4 -4
  69. package/dist/types/ai/agent-surface.d.ts +32 -0
  70. package/dist/types/ai/api-catalog.d.ts +7 -1
  71. package/dist/types/ai/ask-context.d.ts +7 -0
  72. package/dist/types/ai/component-markdown.d.ts +4 -4
  73. package/dist/types/ai/link-headers.d.ts +8 -1
  74. package/dist/types/ai/openapi-components.d.ts +5 -2
  75. package/dist/types/ai/relative-links.d.ts +11 -4
  76. package/dist/types/ai/skills.d.ts +4 -1
  77. package/dist/types/ai/static-expression.d.ts +28 -0
  78. package/dist/types/ai/tar.d.ts +1 -3
  79. package/dist/types/analytics/databuddy.d.ts +43 -0
  80. package/dist/types/analytics/index.d.ts +4 -0
  81. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  82. package/dist/types/analytics/schema.d.ts +28 -0
  83. package/dist/types/astro/integration.d.ts +3 -2
  84. package/dist/types/cli/env.d.ts +5 -0
  85. package/dist/types/cli/init/scaffold.d.ts +19 -3
  86. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  87. package/dist/types/core/base-path.d.ts +24 -1
  88. package/dist/types/core/config-input.d.ts +13 -11
  89. package/dist/types/core/config.d.ts +2 -2
  90. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  91. package/dist/types/core/graph.d.ts +2 -0
  92. package/dist/types/core/heading-markers.d.ts +5 -7
  93. package/dist/types/core/i18n-ui.d.ts +31 -0
  94. package/dist/types/core/i18n.d.ts +9 -1
  95. package/dist/types/core/last-modified.d.ts +10 -0
  96. package/dist/types/core/links.d.ts +3 -1
  97. package/dist/types/core/load-module.d.ts +10 -0
  98. package/dist/types/core/locale-links.d.ts +12 -2
  99. package/dist/types/core/meta.d.ts +13 -1
  100. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  101. package/dist/types/core/navigation.d.ts +38 -0
  102. package/dist/types/core/ordering-prefix.d.ts +4 -0
  103. package/dist/types/core/safe-links.d.ts +3 -1
  104. package/dist/types/core/schema.d.ts +59 -13
  105. package/dist/types/core/sources/github-releases.d.ts +5 -0
  106. package/dist/types/core/sources/lower.d.ts +38 -13
  107. package/dist/types/core/sources/normalize.d.ts +23 -2
  108. package/dist/types/core/sources/remote.d.ts +11 -1
  109. package/dist/types/core/sources/resolve.d.ts +12 -0
  110. package/dist/types/core/sources/types.d.ts +31 -0
  111. package/dist/types/core/sources/watch.d.ts +12 -5
  112. package/dist/types/core/standard-schema.d.ts +5 -0
  113. package/dist/types/core/types.d.ts +9 -0
  114. package/dist/types/deploy/adapters/node.d.ts +5 -2
  115. package/dist/types/deploy/adapters/types.d.ts +7 -0
  116. package/dist/types/deploy/artifacts.d.ts +6 -4
  117. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  118. package/dist/types/deploy/headers.d.ts +36 -7
  119. package/dist/types/deploy/node-headers.d.ts +43 -8
  120. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  121. package/dist/types/deploy/platforms/node.d.ts +6 -5
  122. package/dist/types/deploy/platforms/types.d.ts +15 -0
  123. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  124. package/dist/types/deploy/redirects.d.ts +31 -15
  125. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  126. package/dist/types/markdown/directives.d.ts +62 -0
  127. package/dist/types/markdown/features.d.ts +21 -0
  128. package/dist/types/markdown/mdast.d.ts +63 -0
  129. package/dist/types/openapi/asyncapi.d.ts +4 -2
  130. package/dist/types/openapi/model.d.ts +20 -6
  131. package/dist/types/search/sync/algolia.d.ts +3 -1
  132. package/docs/01-quickstart.mdx +3 -2
  133. package/docs/02-deployment.mdx +20 -9
  134. package/docs/08-faq.mdx +10 -3
  135. package/docs/advanced/changelog.mdx +1 -1
  136. package/docs/advanced/custom-pages.mdx +7 -5
  137. package/docs/cli/audit.mdx +19 -3
  138. package/docs/cli/doctor.mdx +2 -2
  139. package/docs/cli/evals.mdx +4 -4
  140. package/docs/cli/index.mdx +4 -1
  141. package/docs/cli/translate.mdx +4 -4
  142. package/docs/cli/version.mdx +1 -1
  143. package/docs/configuration/analytics.mdx +42 -2
  144. package/docs/configuration/assistant.mdx +1 -1
  145. package/docs/configuration/customization.mdx +6 -3
  146. package/docs/configuration/index.mdx +5 -3
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +2 -2
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -1
  151. package/docs/content/index.mdx +8 -4
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +7 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -16
  156. package/docs/content/syntax.mdx +14 -0
  157. package/docs/content/versioning.mdx +2 -1
  158. package/docs/discoverability/agent-discovery.mdx +22 -8
  159. package/docs/discoverability/index.mdx +4 -3
  160. package/docs/discoverability/llms-txt.mdx +2 -5
  161. package/docs/discoverability/markdown.mdx +6 -4
  162. package/docs/discoverability/mcp.mdx +4 -0
  163. package/docs/discoverability/metadata.mdx +3 -2
  164. package/docs/discoverability/open-graph.mdx +8 -4
  165. package/docs/discoverability/rss.mdx +4 -2
  166. package/docs/references/asyncapi.mdx +2 -2
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +6 -4
  169. package/package.json +1 -1
  170. package/skills/blume-migrate/SKILL.md +6 -6
  171. package/skills/blume-migrate/references/docusaurus.md +6 -6
  172. package/skills/blume-migrate/references/fumadocs.md +1 -1
  173. package/skills/blume-migrate/references/mintlify.md +3 -3
  174. package/skills/blume-migrate/references/monorepo.md +1 -1
  175. package/skills/blume-migrate/references/nextra.md +2 -2
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +2 -2
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/ai-catalog.ts +6 -2
  182. package/src/ai/api/handlers.ts +2 -2
  183. package/src/ai/api/spec.ts +17 -4
  184. package/src/ai/api-catalog.ts +13 -3
  185. package/src/ai/ask-context.ts +14 -2
  186. package/src/ai/ask-data.ts +26 -12
  187. package/src/ai/changelog-markdown.ts +2 -2
  188. package/src/ai/component-markdown.ts +43 -37
  189. package/src/ai/link-headers.ts +22 -8
  190. package/src/ai/llms.ts +22 -10
  191. package/src/ai/markdown.ts +23 -11
  192. package/src/ai/mcp/discovery.ts +2 -2
  193. package/src/ai/mcp/query.ts +2 -2
  194. package/src/ai/mcp/server.ts +80 -9
  195. package/src/ai/openapi-components.ts +22 -6
  196. package/src/ai/relative-links.ts +99 -15
  197. package/src/ai/serializers.ts +2 -1
  198. package/src/ai/skills.ts +14 -1
  199. package/src/ai/static-expression.ts +416 -0
  200. package/src/ai/tar.ts +139 -12
  201. package/src/ai/visibility.ts +45 -14
  202. package/src/analytics/databuddy.ts +67 -0
  203. package/src/analytics/head.ts +8 -0
  204. package/src/analytics/index.ts +7 -0
  205. package/src/analytics/one-dollar-stats.ts +86 -0
  206. package/src/analytics/posthog.ts +21 -3
  207. package/src/analytics/schema.ts +4 -0
  208. package/src/astro/generate.ts +72 -28
  209. package/src/astro/include-hmr.ts +31 -12
  210. package/src/astro/include-refresh.ts +19 -8
  211. package/src/astro/integration.ts +108 -14
  212. package/src/astro/runtime-modules.ts +28 -13
  213. package/src/astro/templates.ts +139 -57
  214. package/src/audit/catalog.ts +2 -2
  215. package/src/audit/checks/assets.ts +25 -5
  216. package/src/audit/checks/content.ts +20 -2
  217. package/src/audit/checks/i18n.ts +46 -8
  218. package/src/audit/checks/indexability.ts +39 -19
  219. package/src/audit/checks/links.ts +22 -1
  220. package/src/audit/checks/llms.ts +6 -3
  221. package/src/audit/checks/network.ts +3 -1
  222. package/src/audit/checks/og-image.ts +10 -0
  223. package/src/audit/checks/robots.ts +6 -1
  224. package/src/audit/checks/sitemap.ts +56 -31
  225. package/src/audit/checks/social.ts +21 -2
  226. package/src/audit/crawl.ts +88 -15
  227. package/src/audit/graph.ts +3 -1
  228. package/src/audit/report.ts +54 -17
  229. package/src/audit/run.ts +13 -9
  230. package/src/audit/snapshot.ts +5 -0
  231. package/src/audit/types.ts +18 -0
  232. package/src/audit/url.ts +37 -6
  233. package/src/cli/args.ts +32 -0
  234. package/src/cli/build-failure.ts +50 -0
  235. package/src/cli/commands/audit.ts +11 -1
  236. package/src/cli/commands/build.ts +19 -5
  237. package/src/cli/commands/check.ts +2 -0
  238. package/src/cli/commands/doctor.ts +3 -1
  239. package/src/cli/commands/eval.ts +20 -18
  240. package/src/cli/commands/init.ts +15 -5
  241. package/src/cli/commands/preview.ts +15 -0
  242. package/src/cli/commands/sync.ts +2 -0
  243. package/src/cli/commands/translate.ts +18 -18
  244. package/src/cli/commands/upgrade.ts +11 -0
  245. package/src/cli/commands/validate.ts +3 -1
  246. package/src/cli/dev-lock.ts +157 -37
  247. package/src/cli/eject-scripts.ts +32 -7
  248. package/src/cli/env.ts +12 -1
  249. package/src/cli/init/scaffold.ts +117 -11
  250. package/src/cli/init/starter-spec.ts +235 -0
  251. package/src/cli/report-format.ts +11 -6
  252. package/src/components/colors.ts +19 -0
  253. package/src/components/content/AccordionItem.astro +26 -22
  254. package/src/components/content/Badge.astro +7 -12
  255. package/src/components/content/Card.astro +2 -2
  256. package/src/components/content/Component.astro +11 -5
  257. package/src/components/content/Expandable.astro +5 -1
  258. package/src/components/content/Frame.astro +2 -7
  259. package/src/components/content/GithubInfo.astro +12 -2
  260. package/src/components/content/Prompt.astro +2 -7
  261. package/src/components/content/Tab.astro +0 -1
  262. package/src/components/content/Tabs.astro +58 -5
  263. package/src/components/content/Tile.astro +1 -1
  264. package/src/components/content/Tooltip.astro +69 -7
  265. package/src/components/content/Tree.astro +7 -2
  266. package/src/components/content/TypeTable.astro +10 -5
  267. package/src/components/content/Update.astro +8 -2
  268. package/src/components/content/auto-type-table.ts +4 -1
  269. package/src/components/content/badge-color.ts +20 -0
  270. package/src/components/content/base-href.ts +14 -22
  271. package/src/components/content/inline-markdown.ts +27 -7
  272. package/src/components/copy-feedback.ts +35 -8
  273. package/src/components/islands/assistant.tsx +37 -6
  274. package/src/components/islands/base-path.ts +47 -10
  275. package/src/components/islands/hooks.ts +42 -21
  276. package/src/components/islands/webmcp.ts +16 -10
  277. package/src/components/layout/Banner.astro +23 -4
  278. package/src/components/layout/Breadcrumbs.astro +5 -2
  279. package/src/components/layout/DiscoveryLinks.astro +9 -5
  280. package/src/components/layout/Header.astro +23 -10
  281. package/src/components/layout/LanguageSwitcher.astro +2 -2
  282. package/src/components/layout/Logo.astro +5 -0
  283. package/src/components/layout/NavSelector.astro +8 -3
  284. package/src/components/layout/NavTabMenu.astro +133 -0
  285. package/src/components/layout/NavTree.astro +31 -12
  286. package/src/components/layout/NavTreeCache.astro +5 -2
  287. package/src/components/layout/NavTreeScript.astro +45 -5
  288. package/src/components/layout/PageActions.astro +4 -3
  289. package/src/components/layout/PageLayout.astro +28 -13
  290. package/src/components/layout/Pagination.astro +3 -3
  291. package/src/components/layout/ReferenceLayout.astro +4 -1
  292. package/src/components/layout/RootLayout.astro +25 -12
  293. package/src/components/layout/Search.astro +38 -14
  294. package/src/components/layout/VersionBanner.astro +2 -2
  295. package/src/components/layout/analytics-client.ts +14 -0
  296. package/src/components/layout/toc-active.ts +41 -0
  297. package/src/components/layout/toc-element.ts +8 -14
  298. package/src/components/openapi/ApiTagOperations.astro +2 -2
  299. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  300. package/src/components/openapi/GraphqlChip.astro +2 -2
  301. package/src/components/openapi/GraphqlType.astro +11 -3
  302. package/src/components/openapi/MessageComposer.astro +1 -1
  303. package/src/components/openapi/Operation.astro +23 -5
  304. package/src/components/openapi/PanelTabs.astro +4 -1
  305. package/src/components/openapi/Playground.astro +8 -2
  306. package/src/components/openapi/RequestPanel.astro +13 -6
  307. package/src/components/openapi/SchemaProperty.astro +11 -33
  308. package/src/components/openapi/SchemaTable.astro +34 -52
  309. package/src/components/openapi/async.ts +38 -6
  310. package/src/components/openapi/helpers.ts +100 -12
  311. package/src/components/openapi/message-composer.ts +14 -1
  312. package/src/components/openapi/message-model.ts +12 -2
  313. package/src/components/openapi/message.ts +21 -3
  314. package/src/components/openapi/operation-model.ts +119 -17
  315. package/src/components/openapi/panel.ts +31 -5
  316. package/src/components/openapi/param-style.ts +181 -0
  317. package/src/components/openapi/playground-client.ts +110 -23
  318. package/src/components/openapi/playground-schema.ts +25 -6
  319. package/src/components/openapi/request.ts +192 -26
  320. package/src/components/openapi/schema-tree.ts +209 -0
  321. package/src/components/openapi/snippets.ts +113 -19
  322. package/src/components/openapi/validate-json.ts +1 -1
  323. package/src/components/openapi/ws-client.ts +18 -2
  324. package/src/core/base-path.ts +69 -8
  325. package/src/core/config-input.ts +13 -11
  326. package/src/core/config.ts +18 -4
  327. package/src/core/diagnostics.ts +219 -30
  328. package/src/core/directive-diagnostics.ts +99 -0
  329. package/src/core/frontmatter.ts +21 -18
  330. package/src/core/graph.ts +120 -11
  331. package/src/core/heading-markers.ts +5 -18
  332. package/src/core/i18n-ui.ts +39 -2
  333. package/src/core/i18n.ts +44 -5
  334. package/src/core/last-modified.ts +25 -3
  335. package/src/core/links.ts +7 -4
  336. package/src/core/load-module.ts +20 -0
  337. package/src/core/locale-links.ts +22 -18
  338. package/src/core/manifest.ts +3 -2
  339. package/src/core/meta.ts +89 -12
  340. package/src/core/nav-diagnostics.ts +56 -1
  341. package/src/core/navigation.ts +272 -83
  342. package/src/core/ordering-prefix.ts +27 -0
  343. package/src/core/project-graph.ts +50 -6
  344. package/src/core/safe-href.ts +53 -1
  345. package/src/core/safe-links.ts +11 -2
  346. package/src/core/schema.ts +94 -18
  347. package/src/core/sources/assets.ts +83 -41
  348. package/src/core/sources/contentful-rich-text.ts +28 -20
  349. package/src/core/sources/contentful.ts +25 -12
  350. package/src/core/sources/filesystem.ts +25 -3
  351. package/src/core/sources/github-releases.ts +131 -11
  352. package/src/core/sources/lexical.ts +30 -20
  353. package/src/core/sources/lower.ts +276 -56
  354. package/src/core/sources/mdx-remote.ts +52 -16
  355. package/src/core/sources/normalize.ts +456 -119
  356. package/src/core/sources/notion.ts +77 -34
  357. package/src/core/sources/obsidian.ts +40 -9
  358. package/src/core/sources/payload.ts +1 -0
  359. package/src/core/sources/portable-text.ts +85 -49
  360. package/src/core/sources/remote.ts +18 -2
  361. package/src/core/sources/resolve.ts +52 -33
  362. package/src/core/sources/sanity.ts +1 -0
  363. package/src/core/sources/strapi-blocks.ts +28 -19
  364. package/src/core/sources/strapi.ts +1 -0
  365. package/src/core/sources/types.ts +31 -0
  366. package/src/core/sources/watch.ts +20 -7
  367. package/src/core/standard-schema.ts +10 -6
  368. package/src/core/types.ts +9 -0
  369. package/src/core/ui-packs/ar.ts +13 -0
  370. package/src/core/ui-packs/bg.ts +13 -0
  371. package/src/core/ui-packs/bn.ts +13 -0
  372. package/src/core/ui-packs/ca.ts +13 -0
  373. package/src/core/ui-packs/cs.ts +13 -0
  374. package/src/core/ui-packs/da.ts +13 -0
  375. package/src/core/ui-packs/de.ts +13 -0
  376. package/src/core/ui-packs/el.ts +13 -0
  377. package/src/core/ui-packs/es.ts +13 -0
  378. package/src/core/ui-packs/fa.ts +13 -0
  379. package/src/core/ui-packs/fi.ts +13 -0
  380. package/src/core/ui-packs/fr.ts +13 -0
  381. package/src/core/ui-packs/he.ts +13 -0
  382. package/src/core/ui-packs/hi.ts +13 -0
  383. package/src/core/ui-packs/hr.ts +13 -0
  384. package/src/core/ui-packs/hu.ts +13 -0
  385. package/src/core/ui-packs/id.ts +13 -0
  386. package/src/core/ui-packs/it.ts +13 -0
  387. package/src/core/ui-packs/ja.ts +13 -0
  388. package/src/core/ui-packs/ko.ts +13 -0
  389. package/src/core/ui-packs/nl.ts +13 -0
  390. package/src/core/ui-packs/no.ts +13 -0
  391. package/src/core/ui-packs/pl.ts +13 -0
  392. package/src/core/ui-packs/pt-br.ts +13 -0
  393. package/src/core/ui-packs/pt.ts +13 -0
  394. package/src/core/ui-packs/ro.ts +13 -0
  395. package/src/core/ui-packs/ru.ts +13 -0
  396. package/src/core/ui-packs/sk.ts +13 -0
  397. package/src/core/ui-packs/sr.ts +13 -0
  398. package/src/core/ui-packs/sv.ts +13 -0
  399. package/src/core/ui-packs/th.ts +13 -0
  400. package/src/core/ui-packs/tr.ts +13 -0
  401. package/src/core/ui-packs/uk.ts +13 -0
  402. package/src/core/ui-packs/vi.ts +13 -0
  403. package/src/core/ui-packs/zh-tw.ts +13 -0
  404. package/src/core/ui-packs/zh.ts +13 -0
  405. package/src/core/version-cut.ts +69 -17
  406. package/src/core/versions.ts +4 -1
  407. package/src/deploy/adapters/node.ts +5 -2
  408. package/src/deploy/adapters/registry.ts +2 -1
  409. package/src/deploy/adapters/types.ts +13 -1
  410. package/src/deploy/artifacts.ts +52 -11
  411. package/src/deploy/cloudflare-negotiation.ts +27 -16
  412. package/src/deploy/headers.ts +75 -55
  413. package/src/deploy/node-headers.ts +148 -27
  414. package/src/deploy/platforms/cloudflare.ts +13 -3
  415. package/src/deploy/platforms/netlify.ts +83 -5
  416. package/src/deploy/platforms/node.ts +8 -5
  417. package/src/deploy/platforms/static.ts +2 -0
  418. package/src/deploy/platforms/types.ts +15 -0
  419. package/src/deploy/platforms/vercel.ts +10 -3
  420. package/src/deploy/redirects.ts +68 -21
  421. package/src/deploy/robots.ts +2 -2
  422. package/src/deploy/rss.ts +4 -3
  423. package/src/deploy/sitemap.ts +7 -5
  424. package/src/deploy/vercel-negotiation.ts +35 -3
  425. package/src/eval/agents.ts +10 -2
  426. package/src/eval/run.ts +25 -0
  427. package/src/markdown/base-links.ts +74 -29
  428. package/src/markdown/directives.ts +242 -36
  429. package/src/markdown/features.ts +17 -0
  430. package/src/markdown/index.ts +13 -9
  431. package/src/markdown/mdast.ts +5 -2
  432. package/src/markdown/relative-links.ts +15 -27
  433. package/src/markdown/route-snapshot.ts +37 -0
  434. package/src/og/card.ts +149 -9
  435. package/src/og/derive.ts +156 -5
  436. package/src/og/index.ts +1 -0
  437. package/src/openapi/asyncapi.ts +4 -2
  438. package/src/openapi/graphql-build.ts +28 -2
  439. package/src/openapi/model.ts +99 -27
  440. package/src/openapi/proxy.ts +63 -10
  441. package/src/openapi/render-mdx.ts +51 -3
  442. package/src/registry/eject.ts +297 -43
  443. package/src/search/adapters/version-scope.ts +30 -0
  444. package/src/search/documents.ts +89 -27
  445. package/src/search/popular.ts +2 -1
  446. package/src/search/sync/algolia.ts +36 -2
  447. package/src/seo/jsonld.ts +7 -3
  448. package/src/sources/registry.ts +5 -0
  449. package/src/theme/entry.ts +11 -4
  450. package/src/translate/agents.ts +6 -1
  451. package/src/translate/ledger.ts +26 -3
  452. package/src/translate/meta.ts +76 -24
  453. package/src/translate/run.ts +14 -8
  454. package/src/translate/validate.ts +14 -2
  455. package/src/translate/work-list.ts +91 -19
  456. package/src/upgrade/upgrade.ts +36 -4
  457. package/dist/cli/chunk-27g6wdth.js.map +0 -10
  458. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  459. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  460. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  461. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  462. package/dist/cli/chunk-82bbrxdn.js +0 -51
  463. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  464. package/dist/cli/chunk-abh8yjkn.js +0 -31
  465. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  466. package/dist/cli/chunk-ce574jw2.js +0 -23
  467. package/dist/cli/chunk-ce574jw2.js.map +0 -10
  468. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  469. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  470. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  471. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  472. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  473. package/dist/cli/chunk-jwyddg7y.js.map +0 -15
  474. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  475. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  476. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  477. package/dist/cli/chunk-q5163e60.js.map +0 -11
  478. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  479. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  480. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  481. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  482. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  483. package/dist/cli/chunk-yw7dm696.js.map +0 -14
  484. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
  485. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
  486. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
  487. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
  488. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
  489. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
  490. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
  491. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
  492. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
@@ -19,28 +19,27 @@ import {
19
19
  writeAgentReport,
20
20
  fixPrompt,
21
21
  launchInstalledAgent
22
- } from "./chunk-qwsrynx5.js";
22
+ } from "./chunk-1d7ve1dm.js";
23
23
  import {
24
+ routeSetFor,
24
25
  frontmatter_default,
25
26
  gradeExternal,
26
27
  probeAll,
28
+ applyBaseToAstroRedirects,
27
29
  deployPlatform
28
- } from "./chunk-qkqwkpte.js";
30
+ } from "./chunk-pv29h0wf.js";
29
31
  import {
30
32
  normalizeBasePath,
31
33
  normalizePath,
32
- withBasePath,
33
34
  stripBasePath
34
- } from "./chunk-jts8mvcz.js";
35
- import {
36
- deployStaticDir
37
- } from "./chunk-82bbrxdn.js";
35
+ } from "./chunk-3em5wd2y.js";
38
36
  import {
37
+ deployStaticDir,
39
38
  examplesRouteBase
40
- } from "./chunk-2hn4b8z7.js";
39
+ } from "./chunk-5m5nmvyq.js";
41
40
  import {
42
41
  scanProject
43
- } from "./chunk-fs23ddbb.js";
42
+ } from "./chunk-j85vccga.js";
44
43
 
45
44
  // src/cli/commands/audit.ts
46
45
  import { defineCommand } from "citty";
@@ -71,6 +70,7 @@ var serverRoutes = (context) => {
71
70
  return mcp?.enabled ? [normalizePath(mcp.route)] : [];
72
71
  };
73
72
  var isServed = (context, path) => context.byUrl.has(path) || context.files.has(path) || context.files.has(`${path}/index.html`) || serverRoutes(context).includes(path);
73
+ var underBase = (deployBase, path) => !deployBase || path === deployBase || path.startsWith(`${deployBase}/`);
74
74
  var NON_HTTP_SCHEME = /^(?!https?:)[a-z][a-z0-9+.-]*:/iu;
75
75
  var decodePath = (path) => {
76
76
  try {
@@ -79,6 +79,14 @@ var decodePath = (path) => {
79
79
  return path;
80
80
  }
81
81
  };
82
+ var isHttpUrl = (input) => {
83
+ try {
84
+ const { protocol } = new URL(input);
85
+ return protocol === "http:" || protocol === "https:";
86
+ } catch {
87
+ return false;
88
+ }
89
+ };
82
90
  var siteOrigin = (site) => {
83
91
  if (!site) {
84
92
  return null;
@@ -106,10 +114,14 @@ var resolveHref = (pageUrl, href, origin, deployBase = "") => {
106
114
  return { kind: "ignored" };
107
115
  }
108
116
  if (origin && parsed.origin === origin) {
117
+ const served = decodePath(parsed.pathname);
118
+ if (!underBase(deployBase, served)) {
119
+ return { kind: "outside-base", path: normalizePath(served) };
120
+ }
109
121
  return {
110
122
  hash: parsed.hash.slice(1),
111
123
  kind: "self-origin",
112
- path: normalizePath(stripBasePath(deployBase, decodePath(parsed.pathname)))
124
+ path: normalizePath(stripBasePath(deployBase, served))
113
125
  };
114
126
  }
115
127
  return { kind: "external", url: parsed.toString() };
@@ -121,14 +133,19 @@ var resolveHref = (pageUrl, href, origin, deployBase = "") => {
121
133
  } catch {
122
134
  return { kind: "ignored" };
123
135
  }
136
+ const served = decodePath(resolved.pathname);
137
+ if (target.startsWith("/") && !underBase(deployBase, served)) {
138
+ return { kind: "outside-base", path: normalizePath(served) };
139
+ }
124
140
  return {
125
141
  hash: resolved.hash.slice(1),
126
142
  kind: "internal",
127
- path: normalizePath(stripBasePath(deployBase, decodePath(resolved.pathname)))
143
+ path: normalizePath(stripBasePath(deployBase, served))
128
144
  };
129
145
  };
130
146
 
131
147
  // src/audit/checks/assets.ts
148
+ var VERCEL_IMAGE_ENDPOINT = "/_vercel/image";
132
149
  var formatBytes = (bytes) => bytes < 1024 * 1024 ? `${Math.round(bytes / 1024)} kB` : `${(bytes / 1024 / 1024).toFixed(1)} MB`;
133
150
  var resolveAsset = (context, page, asset, missingId, kind, found) => {
134
151
  if (asset.src.startsWith("http://")) {
@@ -139,10 +156,18 @@ var resolveAsset = (context, page, asset, missingId, kind, found) => {
139
156
  return null;
140
157
  }
141
158
  const origin = siteOrigin(context.project.config.deployment.options.site);
142
- const resolved = resolveHref(page.url, asset.src, origin, normalizeBasePath(context.project.config.deployment.options.base));
159
+ const deployBase = normalizeBasePath(context.project.config.deployment.options.base);
160
+ const resolved = resolveHref(page.url, asset.src, origin, deployBase);
143
161
  if (resolved.kind === "external" || resolved.kind === "ignored") {
144
162
  return null;
145
163
  }
164
+ if (resolved.path === VERCEL_IMAGE_ENDPOINT) {
165
+ return null;
166
+ }
167
+ if (resolved.kind === "outside-base") {
168
+ found.push(finding(missingId, pageSite(context, page), `Page references ${asset.src}, which is missing deployment.base (${deployBase}), so the deployed site does not serve it.`));
169
+ return null;
170
+ }
146
171
  const bytes = context.files.get(resolved.path);
147
172
  if (bytes === undefined) {
148
173
  found.push(finding(missingId, pageSite(context, page), `Page references ${asset.src}, which is not in the build.`));
@@ -214,6 +239,9 @@ var frontmatterDate = (source) => {
214
239
  return null;
215
240
  }
216
241
  };
242
+ var isGeneratedReference = (page) => page.route?.source.name === "openapi";
243
+ var GENERATED_TITLE_FIX = "This page is generated from an API spec, which titles it with the operation's `summary` (the spec's own title on the overview page) — rewrite it there.";
244
+ var GENERATED_DESCRIPTION_FIX = "This page is generated from an API spec, which describes it with the operation's `description` or `summary` (the spec's own description on the overview page) — rewrite it there.";
217
245
  var titleChecks = (context, page) => {
218
246
  const { titleMin, titleMax } = context.thresholds;
219
247
  const [title] = page.titles;
@@ -229,7 +257,7 @@ var titleChecks = (context, page) => {
229
257
  const width = stringWidth(title.replaceAll(WHITESPACE, " "));
230
258
  if (width > titleMax || width < titleMin) {
231
259
  const direction = width > titleMax ? "long" : "short";
232
- found.push(finding("BLUME_AUDIT_TITLE_LENGTH", pageSite(context, page, ["title"]), `Title renders ${width} columns wide — too ${direction} (aim for ${titleMin}–${titleMax}).`));
260
+ found.push(finding("BLUME_AUDIT_TITLE_LENGTH", pageSite(context, page, ["title"]), `Title renders ${width} columns wide — too ${direction} (aim for ${titleMin}–${titleMax}).`, isGeneratedReference(page) ? GENERATED_TITLE_FIX : undefined));
233
261
  }
234
262
  return found;
235
263
  };
@@ -251,7 +279,7 @@ var descriptionChecks = (context, page) => {
251
279
  const width = stringWidth(description.replaceAll(WHITESPACE, " "));
252
280
  if (width > descriptionMax || width < descriptionMin) {
253
281
  const direction = width > descriptionMax ? "long" : "short";
254
- found.push(finding("BLUME_AUDIT_DESCRIPTION_LENGTH", pageSite(context, page, ["description"]), `Meta description renders ${width} columns wide — too ${direction} (aim for ${descriptionMin}–${descriptionMax}).`));
282
+ found.push(finding("BLUME_AUDIT_DESCRIPTION_LENGTH", pageSite(context, page, ["description"]), `Meta description renders ${width} columns wide — too ${direction} (aim for ${descriptionMin}–${descriptionMax}).`, isGeneratedReference(page) ? GENERATED_DESCRIPTION_FIX : undefined));
255
283
  }
256
284
  return found;
257
285
  };
@@ -467,6 +495,94 @@ var duplicateChecks = {
467
495
  tier: "static"
468
496
  };
469
497
 
498
+ // src/audit/checks/indexability.ts
499
+ var parseCanonical = (canonical) => {
500
+ try {
501
+ return new URL(canonical);
502
+ } catch {
503
+ return null;
504
+ }
505
+ };
506
+ var isArchivedLatestCanonical = (context, page, target) => {
507
+ const version = page.route?.version;
508
+ const archived = version ? context.project.config.versions?.archived.find((entry) => entry.id === version) : undefined;
509
+ const latestAlternate = page.route?.versionAlternates.find((alternate) => alternate.version === "");
510
+ return archived?.canonical === "latest" && latestAlternate !== undefined && normalizePath(latestAlternate.path) === target;
511
+ };
512
+ var canonicalChecks = (context, page) => {
513
+ const { site } = context.project.config.deployment.options;
514
+ const origin = siteOrigin(site);
515
+ if (ERROR_ROUTES.has(page.url)) {
516
+ return [];
517
+ }
518
+ if (!page.canonical) {
519
+ return site && page.indexable ? [
520
+ finding("BLUME_AUDIT_CANONICAL_MISSING", pageSite(context, page), "Page has no canonical URL.")
521
+ ] : [];
522
+ }
523
+ const canonical = parseCanonical(page.canonical);
524
+ if (!canonical) {
525
+ return [
526
+ finding("BLUME_AUDIT_CANONICAL_BAD_TARGET", pageSite(context, page, ["seo", "canonical"]), `Canonical "${page.canonical}" is not a valid absolute URL.`)
527
+ ];
528
+ }
529
+ const found = [];
530
+ if (origin && canonical.origin !== origin) {
531
+ const sameHost = canonical.host === new URL(origin).host;
532
+ if (sameHost) {
533
+ found.push(finding("BLUME_AUDIT_CANONICAL_PROTOCOL_MISMATCH", pageSite(context, page, ["seo", "canonical"]), `Canonical uses ${canonical.protocol}// but the site is ${new URL(origin).protocol}//.`));
534
+ }
535
+ return found;
536
+ }
537
+ const target = normalizePath(stripBasePath(normalizeBasePath(context.project.config.deployment.options.base), decodePath(canonical.pathname)));
538
+ if (target === normalizePath(page.url)) {
539
+ return found;
540
+ }
541
+ const redirect = context.redirects.find((entry) => normalizePath(entry.from) === target);
542
+ if (redirect) {
543
+ found.push(finding("BLUME_AUDIT_CANONICAL_BAD_TARGET", pageSite(context, page, ["seo", "canonical"]), `Canonical points at ${target}, which is a redirect to ${redirect.to}.`));
544
+ } else if (context.byUrl.has(target)) {
545
+ if (!(page.route?.fallback === true || isArchivedLatestCanonical(context, page, target))) {
546
+ found.push(finding("BLUME_AUDIT_CANONICAL_NOT_SELF", pageSite(context, page, ["seo", "canonical"]), `Page declares ${target} as its canonical, so it will not be indexed itself.`));
547
+ }
548
+ } else {
549
+ found.push(finding("BLUME_AUDIT_CANONICAL_BAD_TARGET", pageSite(context, page, ["seo", "canonical"]), `Canonical points at ${target}, which is not a page on this site.`));
550
+ }
551
+ return found;
552
+ };
553
+ var indexabilityChecks = {
554
+ category: "indexability",
555
+ run(context) {
556
+ const found = [];
557
+ if (!context.project.config.deployment.options.site) {
558
+ const { deployment } = context.project.config;
559
+ const site = {
560
+ file: context.project.context.configFile ?? undefined,
561
+ url: "/"
562
+ };
563
+ found.push(deployPlatform(deployment).env ? finding("BLUME_AUDIT_SITE_INFERRED_AT_DEPLOY", site, `deployment.site is not set in this build — on ${deployment.kind} it is inferred from the platform env at deploy time, so canonical URLs, Open Graph images, and the sitemap are only missing from this local artifact.`) : finding("BLUME_AUDIT_SITE_NOT_SET", site, "deployment.site is not set, so no canonical URLs, Open Graph images, or sitemap can be generated."));
564
+ }
565
+ for (const page of context.pages) {
566
+ found.push(...canonicalChecks(context, page));
567
+ if (page.robots && !page.indexable && !ERROR_ROUTES.has(page.url)) {
568
+ found.push(finding("BLUME_AUDIT_ROBOTS_META_UNEXPECTED", pageSite(context, page, ["noindex"]), `Page declares robots "${page.robots}" and will not be indexed.`));
569
+ if (!page.indexable && page.canonical) {
570
+ found.push(finding("BLUME_AUDIT_CANONICAL_ON_NOINDEX", pageSite(context, page, ["noindex"]), `Page is noindex but declares ${page.canonical} as its canonical.`));
571
+ }
572
+ }
573
+ if (page.route?.draft) {
574
+ found.push(finding("BLUME_AUDIT_DRAFT_PAGE_PUBLISHED", pageSite(context, page, ["draft"]), `${page.url} is marked draft in its front matter but is in the build.`));
575
+ }
576
+ if (page.bytes > context.thresholds.maxHtmlBytes) {
577
+ const mb = (page.bytes / 1024 / 1024).toFixed(1);
578
+ found.push(finding("BLUME_AUDIT_HTML_TOO_LARGE", pageSite(context, page), `Page HTML is ${mb} MB — past Googlebot's 2 MB limit, the rest is not crawled.`));
579
+ }
580
+ }
581
+ return found;
582
+ },
583
+ tier: "static"
584
+ };
585
+
470
586
  // src/audit/checks/i18n.ts
471
587
  var deployBaseOf = (context) => normalizeBasePath(context.project.config.deployment.options.base);
472
588
  var X_DEFAULT = "x-default";
@@ -480,11 +596,22 @@ var isValidBcp47 = (tag) => {
480
596
  };
481
597
  var alternatePath = (href, deployBase) => {
482
598
  try {
483
- return normalizePath(stripBasePath(deployBase, new URL(href).pathname));
599
+ return normalizePath(stripBasePath(deployBase, decodePath(new URL(href).pathname)));
484
600
  } catch {
485
601
  return null;
486
602
  }
487
603
  };
604
+ var isFallbackCopy = (page) => page.route?.fallback === true;
605
+ var canonicalizesElsewhere = (context, target, deployBase) => {
606
+ if (!target.canonical) {
607
+ return false;
608
+ }
609
+ const canonical = alternatePath(target.canonical, deployBase);
610
+ if (canonical === normalizePath(target.url)) {
611
+ return false;
612
+ }
613
+ return !(canonical !== null && isArchivedLatestCanonical(context, target, canonical));
614
+ };
488
615
  var langChecks = (context, page) => {
489
616
  if (!page.lang) {
490
617
  return [
@@ -521,7 +648,7 @@ var hreflangChecks = (context, page) => {
521
648
  found.push(finding("BLUME_AUDIT_HREFLANG_BAD_TARGET", pageSite(context, page), `hreflang="${alternate.lang}" points at ${path}, which redirects to ${redirect.to}.`));
522
649
  } else if (!target) {
523
650
  found.push(finding("BLUME_AUDIT_HREFLANG_BAD_TARGET", pageSite(context, page), `hreflang="${alternate.lang}" points at ${path}, which the build does not serve.`));
524
- } else if (target.canonical && alternatePath(target.canonical, deployBase) !== normalizePath(target.url)) {
651
+ } else if (canonicalizesElsewhere(context, target, deployBase)) {
525
652
  found.push(finding("BLUME_AUDIT_HREFLANG_BAD_TARGET", pageSite(context, page), `hreflang="${alternate.lang}" points at ${path}, which canonicalizes elsewhere.`));
526
653
  }
527
654
  }
@@ -547,6 +674,9 @@ var returnTagChecks = (context) => {
547
674
  const found = [];
548
675
  const deployBase = deployBaseOf(context);
549
676
  for (const page of context.pages) {
677
+ if (isFallbackCopy(page)) {
678
+ continue;
679
+ }
550
680
  for (const alternate of page.hreflang) {
551
681
  if (alternate.lang === X_DEFAULT) {
552
682
  continue;
@@ -573,7 +703,7 @@ var i18nChecks = {
573
703
  const found = [];
574
704
  for (const page of context.pages) {
575
705
  found.push(...langChecks(context, page));
576
- if (page.hreflang.length > 0) {
706
+ if (page.hreflang.length > 0 && !isFallbackCopy(page)) {
577
707
  found.push(...hreflangChecks(context, page));
578
708
  }
579
709
  }
@@ -583,92 +713,6 @@ var i18nChecks = {
583
713
  tier: "static"
584
714
  };
585
715
 
586
- // src/audit/checks/indexability.ts
587
- var parseCanonical = (canonical) => {
588
- try {
589
- return new URL(canonical);
590
- } catch {
591
- return null;
592
- }
593
- };
594
- var canonicalChecks = (context, page) => {
595
- const { site } = context.project.config.deployment.options;
596
- const origin = siteOrigin(site);
597
- if (ERROR_ROUTES.has(page.url)) {
598
- return [];
599
- }
600
- if (!page.canonical) {
601
- return site ? [
602
- finding("BLUME_AUDIT_CANONICAL_MISSING", pageSite(context, page), "Page has no canonical URL.")
603
- ] : [];
604
- }
605
- const canonical = parseCanonical(page.canonical);
606
- if (!canonical) {
607
- return [
608
- finding("BLUME_AUDIT_CANONICAL_BAD_TARGET", pageSite(context, page, ["seo", "canonical"]), `Canonical "${page.canonical}" is not a valid absolute URL.`)
609
- ];
610
- }
611
- const found = [];
612
- if (origin && canonical.origin !== origin) {
613
- const sameHost = canonical.host === new URL(origin).host;
614
- if (sameHost) {
615
- found.push(finding("BLUME_AUDIT_CANONICAL_PROTOCOL_MISMATCH", pageSite(context, page, ["seo", "canonical"]), `Canonical uses ${canonical.protocol}// but the site is ${new URL(origin).protocol}//.`));
616
- }
617
- return found;
618
- }
619
- const target = normalizePath(stripBasePath(normalizeBasePath(context.project.config.deployment.options.base), decodePath(canonical.pathname)));
620
- if (target === normalizePath(page.url)) {
621
- return found;
622
- }
623
- const redirect = context.redirects.find((entry) => normalizePath(entry.from) === target);
624
- if (redirect) {
625
- found.push(finding("BLUME_AUDIT_CANONICAL_BAD_TARGET", pageSite(context, page, ["seo", "canonical"]), `Canonical points at ${target}, which is a redirect to ${redirect.to}.`));
626
- } else if (context.byUrl.has(target)) {
627
- const version = page.route?.version;
628
- const archived = version ? context.project.config.versions?.archived.find((entry) => entry.id === version) : undefined;
629
- const latestAlternate = page.route?.versionAlternates.find((alternate) => alternate.version === "");
630
- const intentional = archived?.canonical === "latest" && latestAlternate !== undefined && normalizePath(latestAlternate.path) === target;
631
- if (!intentional) {
632
- found.push(finding("BLUME_AUDIT_CANONICAL_NOT_SELF", pageSite(context, page, ["seo", "canonical"]), `Page declares ${target} as its canonical, so it will not be indexed itself.`));
633
- }
634
- } else {
635
- found.push(finding("BLUME_AUDIT_CANONICAL_BAD_TARGET", pageSite(context, page, ["seo", "canonical"]), `Canonical points at ${target}, which is not a page on this site.`));
636
- }
637
- return found;
638
- };
639
- var indexabilityChecks = {
640
- category: "indexability",
641
- run(context) {
642
- const found = [];
643
- if (!context.project.config.deployment.options.site) {
644
- const { deployment } = context.project.config;
645
- const site = {
646
- file: context.project.context.configFile ?? undefined,
647
- url: "/"
648
- };
649
- found.push(deployPlatform(deployment).env ? finding("BLUME_AUDIT_SITE_INFERRED_AT_DEPLOY", site, `deployment.site is not set in this build — on ${deployment.kind} it is inferred from the platform env at deploy time, so canonical URLs, Open Graph images, and the sitemap are only missing from this local artifact.`) : finding("BLUME_AUDIT_SITE_NOT_SET", site, "deployment.site is not set, so no canonical URLs, Open Graph images, or sitemap can be generated."));
650
- }
651
- for (const page of context.pages) {
652
- found.push(...canonicalChecks(context, page));
653
- if (page.robots && !page.indexable && !ERROR_ROUTES.has(page.url)) {
654
- found.push(finding("BLUME_AUDIT_ROBOTS_META_UNEXPECTED", pageSite(context, page, ["noindex"]), `Page declares robots "${page.robots}" and will not be indexed.`));
655
- if (!page.indexable && page.canonical) {
656
- found.push(finding("BLUME_AUDIT_CANONICAL_ON_NOINDEX", pageSite(context, page, ["noindex"]), `Page is noindex but declares ${page.canonical} as its canonical.`));
657
- }
658
- }
659
- if (page.route?.draft) {
660
- found.push(finding("BLUME_AUDIT_DRAFT_PAGE_PUBLISHED", pageSite(context, page, ["draft"]), `${page.url} is marked draft in its front matter but is in the build.`));
661
- }
662
- if (page.bytes > context.thresholds.maxHtmlBytes) {
663
- const mb = (page.bytes / 1024 / 1024).toFixed(1);
664
- found.push(finding("BLUME_AUDIT_HTML_TOO_LARGE", pageSite(context, page), `Page HTML is ${mb} MB — past Googlebot's 2 MB limit, the rest is not crawled.`));
665
- }
666
- }
667
- return found;
668
- },
669
- tier: "static"
670
- };
671
-
672
716
  // src/audit/graph.ts
673
717
  var add = (map, key, value) => {
674
718
  const existing = map.get(key);
@@ -699,10 +743,11 @@ var buildGraph = (pages, origin, deployBase = "") => {
699
743
  }
700
744
  return graph;
701
745
  };
702
- var orphanPages = (pages, graph, homeUrl = "/") => pages.filter((page) => page.indexable && page.url !== "/" && page.url !== homeUrl && (graph.contentIn.get(page.url)?.size ?? 0) === 0);
746
+ var orphanPages = (pages, graph, homeUrl = "/") => pages.filter((page) => page.indexable && page.route?.fallback !== true && page.url !== "/" && page.url !== homeUrl && (graph.contentIn.get(page.url)?.size ?? 0) === 0);
703
747
 
704
748
  // src/audit/checks/links.ts
705
749
  var MAGIC_FRAGMENTS = new Set(["", "top"]);
750
+ var FRAGMENT_DIRECTIVE = ":~:";
706
751
  var anchorResolves = (target, fragment) => {
707
752
  if (MAGIC_FRAGMENTS.has(fragment)) {
708
753
  return true;
@@ -723,7 +768,8 @@ var linkChecks = {
723
768
  const brokenChrome = new Map;
724
769
  const redirectedChrome = new Map;
725
770
  const brokenChromeAnchors = new Map;
726
- const checkAnchor = (page, link, target, fragment) => {
771
+ const checkAnchor = (page, link, target, hash) => {
772
+ const [fragment = ""] = hash.split(FRAGMENT_DIRECTIVE);
727
773
  if (anchorResolves(target, fragment)) {
728
774
  return;
729
775
  }
@@ -746,6 +792,14 @@ var linkChecks = {
746
792
  if (resolved.kind === "external" || resolved.kind === "ignored") {
747
793
  return;
748
794
  }
795
+ if (resolved.kind === "outside-base") {
796
+ if (link.content) {
797
+ found.push(finding("BLUME_AUDIT_LINK_TO_BROKEN", pageSite(context, page), `Link to ${link.href} is missing deployment.base (${deployBase}), so the deployed site does not serve it.`));
798
+ } else if (!brokenChrome.has(resolved.path)) {
799
+ brokenChrome.set(resolved.path, page);
800
+ }
801
+ return;
802
+ }
749
803
  const anchorTarget = resolved.hash ? context.byUrl.get(resolved.path) : undefined;
750
804
  if (anchorTarget) {
751
805
  checkAnchor(page, link, anchorTarget, resolved.hash);
@@ -858,7 +912,7 @@ var llmsChecks = {
858
912
  }
859
913
  for (const page of context.pages) {
860
914
  const { route } = page;
861
- if (!route || route.hidden || route.draft || !page.indexable || ERROR_ROUTES.has(page.url) || !openapi && route.source.name === "openapi" || listed.has(normalizePath(page.url))) {
915
+ if (!route || route.hidden || route.draft || route.aiExclude || route.fallback || !page.indexable || ERROR_ROUTES.has(page.url) || !openapi && route.source.name === "openapi" || listed.has(normalizePath(page.url))) {
862
916
  continue;
863
917
  }
864
918
  found.push(finding("BLUME_AUDIT_LLMS_TXT_PAGE_MISSING", pageSite(context, page), `${page.url} is built and indexable but is not listed in llms.txt.`));
@@ -934,7 +988,7 @@ var networkChecks = {
934
988
  found.push(...servedPageChecks(context, page, result, origin));
935
989
  }
936
990
  const robots = results.get(robotsUrl);
937
- if (robots && !robots.ok) {
991
+ if (context.project.config.seo.robots && robots && !robots.ok) {
938
992
  found.push(finding("BLUME_AUDIT_ROBOTS_NOT_ACCESSIBLE", { url: "/robots.txt" }, `robots.txt is not reachable at ${robotsUrl}.`));
939
993
  }
940
994
  const sitemap = results.get(sitemapUrl);
@@ -1046,6 +1100,10 @@ var ogImageChecks = {
1046
1100
  if (resolved.kind === "external" || resolved.kind === "ignored") {
1047
1101
  continue;
1048
1102
  }
1103
+ if (resolved.kind === "outside-base") {
1104
+ found.push(finding("BLUME_AUDIT_OG_IMAGE_BROKEN", pageSite(context, page, ["seo", "image"]), `og:image points at ${resolved.path}, which is missing deployment.base (${deployBase}), so the deployed site does not serve it.`));
1105
+ continue;
1106
+ }
1049
1107
  const { path } = resolved;
1050
1108
  if (!(context.files.has(path) || context.files.has(`${path}/index.html`))) {
1051
1109
  found.push(finding("BLUME_AUDIT_OG_IMAGE_BROKEN", pageSite(context, page, ["seo", "image"]), `og:image points at ${path}, which is not in the build.`));
@@ -1117,7 +1175,7 @@ var robotsChecks = {
1117
1175
  ];
1118
1176
  }
1119
1177
  const found = robots.invalid.map((line) => finding("BLUME_AUDIT_ROBOTS_INVALID", { file: robots.file, line: line.line, url: "/robots.txt" }, `robots.txt line ${line.line} is not a directive: "${line.text}"`));
1120
- if (site && robots.sitemaps.length === 0) {
1178
+ if (site && context.project.config.seo.sitemap && robots.sitemaps.length === 0) {
1121
1179
  found.push(finding("BLUME_AUDIT_ROBOTS_SITEMAP_MISSING", { file: robots.file, url: "/robots.txt" }, "robots.txt does not declare a Sitemap."));
1122
1180
  }
1123
1181
  const parser = robotsParser(`${MATCH_ORIGIN}/robots.txt`, robots.raw);
@@ -1195,6 +1253,15 @@ var checkListedUrl = (context, loc, origin, file, deployBase) => {
1195
1253
  }
1196
1254
  return found;
1197
1255
  };
1256
+ var unlistedPages = (context, listed, deployBase) => context.pages.flatMap((page) => {
1257
+ const canonical = page.canonical ? canonicalPath(page.canonical, deployBase) : null;
1258
+ if (!page.indexable || page.route?.fallback || canonical && canonical !== normalizePath(page.url) || ERROR_ROUTES2.has(page.url) || listed.has(normalizePath(page.url))) {
1259
+ return [];
1260
+ }
1261
+ return [
1262
+ finding("BLUME_AUDIT_INDEXABLE_PAGE_NOT_IN_SITEMAP", pageSite(context, page), `${page.url} is built and indexable but is not listed in sitemap.xml.`)
1263
+ ];
1264
+ });
1198
1265
  var sitemapChecks = {
1199
1266
  category: "sitemap",
1200
1267
  run(context) {
@@ -1210,11 +1277,21 @@ var sitemapChecks = {
1210
1277
  }
1211
1278
  const found = [];
1212
1279
  if (sitemap.error) {
1213
- found.push(finding("BLUME_AUDIT_SITEMAP_INVALID", { file: sitemap.file, url: "/sitemap.xml" }, `sitemap.xml is not a valid urlset: ${sitemap.error}`));
1280
+ found.push(finding("BLUME_AUDIT_SITEMAP_INVALID", { file: sitemap.file, url: "/sitemap.xml" }, `sitemap.xml is not a valid urlset or sitemap index: ${sitemap.error}`));
1214
1281
  return found;
1215
1282
  }
1216
- if (sitemap.bytes > MAX_SITEMAP_BYTES || sitemap.urls.length > MAX_SITEMAP_URLS) {
1217
- found.push(finding("BLUME_AUDIT_SITEMAP_TOO_LARGE", { file: sitemap.file, url: "/sitemap.xml" }, `sitemap.xml holds ${sitemap.urls.length} URLs in ${Math.round(sitemap.bytes / 1024 / 1024)} MB.`));
1283
+ const parts = sitemap.parts ?? [
1284
+ {
1285
+ bytes: sitemap.bytes,
1286
+ file: sitemap.file,
1287
+ url: "/sitemap.xml",
1288
+ urls: sitemap.urls.length
1289
+ }
1290
+ ];
1291
+ for (const part of parts) {
1292
+ if (part.bytes > MAX_SITEMAP_BYTES || part.urls > MAX_SITEMAP_URLS) {
1293
+ found.push(finding("BLUME_AUDIT_SITEMAP_TOO_LARGE", { file: part.file, url: part.url }, `${part.url.slice(1)} holds ${part.urls} URLs in ${Math.round(part.bytes / 1024 / 1024)} MB.`));
1294
+ }
1218
1295
  }
1219
1296
  for (const [loc, lastmod] of sitemap.lastmod ?? []) {
1220
1297
  const time = Date.parse(lastmod);
@@ -1230,12 +1307,7 @@ var sitemapChecks = {
1230
1307
  for (const loc of sitemap.urls) {
1231
1308
  found.push(...checkListedUrl(context, loc, origin, sitemap.file, deployBase));
1232
1309
  }
1233
- for (const page of context.pages) {
1234
- if (!page.indexable || ERROR_ROUTES2.has(page.url) || listed.has(normalizePath(page.url))) {
1235
- continue;
1236
- }
1237
- found.push(finding("BLUME_AUDIT_INDEXABLE_PAGE_NOT_IN_SITEMAP", pageSite(context, page), `${page.url} is built and indexable but is not listed in sitemap.xml.`));
1238
- }
1310
+ found.push(...unlistedPages(context, listed, deployBase));
1239
1311
  return found;
1240
1312
  },
1241
1313
  tier: "static"
@@ -1317,6 +1389,16 @@ var URL_STYLE = [
1317
1389
  { name: "underscores", test: /_/u },
1318
1390
  { name: "spaces", test: /%20| /u }
1319
1391
  ];
1392
+ var authoredPath = (context, url) => {
1393
+ const { basePath, i18n } = context.project.config;
1394
+ if (!i18n) {
1395
+ return url;
1396
+ }
1397
+ const base = normalizeBasePath(basePath);
1398
+ const [, first = "", ...rest] = stripBasePath(base, url).split("/");
1399
+ const isLocale = i18n.locales.some((locale) => locale.code === first);
1400
+ return isLocale ? `${base}/${rest.join("/")}` : url;
1401
+ };
1320
1402
  var DOTTED_HOST = /^\/\/[^/]*\./u;
1321
1403
  var doubledSlash = (href) => {
1322
1404
  if (href.startsWith("//")) {
@@ -1343,7 +1425,8 @@ var urlChecks = {
1343
1425
  doubled.set(href, page);
1344
1426
  }
1345
1427
  }
1346
- const untidy = URL_STYLE.filter((style) => style.test.test(page.url));
1428
+ const authored = authoredPath(context, page.url);
1429
+ const untidy = URL_STYLE.filter((style) => style.test.test(authored));
1347
1430
  if (untidy.length > 0) {
1348
1431
  found.push(finding("BLUME_AUDIT_URL_STYLE", pageSite(context, page), `URL ${page.url} contains ${untidy.map((style) => style.name).join(" and ")}.`));
1349
1432
  }
@@ -1495,7 +1578,7 @@ var buildSnapshot = (options) => {
1495
1578
  scripts: collectAssets(document, "script[src]", "src"),
1496
1579
  source: route?.sourcePath,
1497
1580
  styles: collectAssets(document, 'link[rel="stylesheet"][href]', "href"),
1498
- titles: document.querySelectorAll("title").map((title) => title.text.trim()).filter((text) => text.length > 0),
1581
+ titles: document.querySelectorAll("title").filter((title) => !title.closest("svg")).map((title) => title.text.trim()).filter((text) => text.length > 0),
1499
1582
  twitter: prefixedMeta(document, "name", "twitter:"),
1500
1583
  url,
1501
1584
  viewport: document.querySelector('meta[name="viewport"]')?.getAttribute("content")?.trim() ?? null,
@@ -1538,6 +1621,7 @@ var sitemapParser = new XMLParser({
1538
1621
  removeNSPrefix: true
1539
1622
  });
1540
1623
  var isUrlsetElement = (value) => typeof value === "object" && value !== null;
1624
+ var isSitemapIndexElement = (value) => typeof value === "object" && value !== null;
1541
1625
  var isUrlEntry = (value) => typeof value === "object" && value !== null;
1542
1626
  var isText = (value) => typeof value === "string";
1543
1627
  var parseSitemap = (file, xml, bytes) => {
@@ -1549,8 +1633,14 @@ var parseSitemap = (file, xml, bytes) => {
1549
1633
  doc.error = "no <urlset> element";
1550
1634
  return doc;
1551
1635
  }
1636
+ if (Object.hasOwn(parsed, "sitemapindex")) {
1637
+ const { sitemapindex } = parsed;
1638
+ const children = isSitemapIndexElement(sitemapindex) ? [sitemapindex.sitemap].flat() : [];
1639
+ doc.sitemaps = children.flatMap((child) => isUrlEntry(child) && isText(child.loc) && child.loc.trim() ? [child.loc.trim()] : []);
1640
+ return doc;
1641
+ }
1552
1642
  if (!Object.hasOwn(parsed, "urlset")) {
1553
- doc.error = Object.hasOwn(parsed, "sitemapindex") ? "sitemap is an index, not a urlset" : "no <urlset> element";
1643
+ doc.error = "no <urlset> element";
1554
1644
  return doc;
1555
1645
  }
1556
1646
  const { urlset } = parsed;
@@ -1615,8 +1705,47 @@ var readIfPresent = async (file) => {
1615
1705
  return null;
1616
1706
  }
1617
1707
  };
1708
+ var readSitemap = async (staticDir, files, deployBase) => {
1709
+ const file = join2(staticDir, "sitemap.xml");
1710
+ const xml = await readIfPresent(file);
1711
+ if (xml === null) {
1712
+ return null;
1713
+ }
1714
+ const doc = parseSitemap(file, xml, files.get("/sitemap.xml") ?? Buffer.byteLength(xml, "utf-8"));
1715
+ if (!doc.sitemaps) {
1716
+ return doc;
1717
+ }
1718
+ doc.parts = [];
1719
+ for (const loc of doc.sitemaps) {
1720
+ let url;
1721
+ try {
1722
+ url = stripBasePath(deployBase, decodeURI(new URL(loc).pathname));
1723
+ } catch {
1724
+ doc.error = `the sitemap index lists "${loc}", which is not a valid absolute URL`;
1725
+ return doc;
1726
+ }
1727
+ const childFile = join2(staticDir, url);
1728
+ const childXml = await readIfPresent(childFile);
1729
+ const child = childXml === null ? null : parseSitemap(childFile, childXml, files.get(url) ?? Buffer.byteLength(childXml, "utf-8"));
1730
+ if (!child || child.error || child.sitemaps) {
1731
+ doc.error = `the sitemap index lists ${loc}, which is not a urlset in the build`;
1732
+ return doc;
1733
+ }
1734
+ doc.urls.push(...child.urls);
1735
+ for (const [key, value] of child.lastmod ?? []) {
1736
+ doc.lastmod?.set(key, value);
1737
+ }
1738
+ doc.parts.push({
1739
+ bytes: child.bytes,
1740
+ file: childFile,
1741
+ url,
1742
+ urls: child.urls.length
1743
+ });
1744
+ }
1745
+ return doc;
1746
+ };
1618
1747
  var crawlStaticDir = async (options) => {
1619
- const { staticDir, manifest, basePath } = options;
1748
+ const { staticDir, manifest, basePath, deployBase = "" } = options;
1620
1749
  const routes = routeIndex(manifest, basePath);
1621
1750
  const htmlFiles = await glob("**/*.html", { absolute: true, cwd: staticDir });
1622
1751
  const snapshots = await pMap(htmlFiles.toSorted(), async (file) => {
@@ -1636,8 +1765,6 @@ var crawlStaticDir = async (options) => {
1636
1765
  });
1637
1766
  }, { concurrency: CRAWL_CONCURRENCY });
1638
1767
  const pages = snapshots.filter((page) => page !== null);
1639
- const sitemapFile = join2(staticDir, "sitemap.xml");
1640
- const sitemapXml = await readIfPresent(sitemapFile);
1641
1768
  const robotsFile = join2(staticDir, "robots.txt");
1642
1769
  const robotsTxt = await readIfPresent(robotsFile);
1643
1770
  const llmsFile = join2(staticDir, "llms.txt");
@@ -1648,7 +1775,7 @@ var crawlStaticDir = async (options) => {
1648
1775
  llms: llmsText === null ? null : parseLlms(llmsFile, llmsText),
1649
1776
  pages,
1650
1777
  robots: robotsTxt === null ? null : parseRobots(robotsFile, robotsTxt),
1651
- sitemap: sitemapXml === null ? null : parseSitemap(sitemapFile, sitemapXml, files.get("/sitemap.xml") ?? Buffer.byteLength(sitemapXml, "utf-8"))
1778
+ sitemap: await readSitemap(staticDir, files, deployBase)
1652
1779
  };
1653
1780
  };
1654
1781
 
@@ -1775,6 +1902,7 @@ var runAudit = async (options) => {
1775
1902
  const basePath = normalizeBasePath(project.config.basePath);
1776
1903
  const crawl = await crawlStaticDir({
1777
1904
  basePath,
1905
+ deployBase: normalizeBasePath(project.config.deployment.options.base),
1778
1906
  manifest: project.manifest,
1779
1907
  staticDir
1780
1908
  });
@@ -1791,11 +1919,7 @@ var runAudit = async (options) => {
1791
1919
  origin,
1792
1920
  pages: crawl.pages,
1793
1921
  project,
1794
- redirects: resolveRedirects(project.config.redirects.map((redirect) => ({
1795
- ...redirect,
1796
- from: withBasePath(basePath, redirect.from),
1797
- to: withBasePath(basePath, redirect.to)
1798
- })), (path) => isServed({ byUrl, files: crawl.files, project }, path)),
1922
+ redirects: resolveRedirects(applyBaseToAstroRedirects(project.config.redirects, basePath, "", routeSetFor(project.manifest.routes)), (path) => isServed({ byUrl, files: crawl.files, project }, path)),
1799
1923
  robots: crawl.robots,
1800
1924
  sitemap: crawl.sitemap,
1801
1925
  sources: await readSources(crawl.pages),
@@ -1924,6 +2048,10 @@ var auditCommand = defineCommand({
1924
2048
  logger.error(`--json and --${agent} are mutually exclusive.`);
1925
2049
  process.exit(1);
1926
2050
  }
2051
+ if (args.url !== undefined && !isHttpUrl(args.url)) {
2052
+ logger.error(`Invalid --url "${args.url}": pass the deployment's full URL, scheme included (e.g. https://${args.url}).`);
2053
+ process.exit(1);
2054
+ }
1927
2055
  const only = splitTerms(args.only);
1928
2056
  const skip = splitTerms(args.skip);
1929
2057
  rejectUnknownTerms([...only, ...skip]);
@@ -1976,7 +2104,7 @@ var auditCommand = defineCommand({
1976
2104
  }
1977
2105
  process.stderr.write(formatReport(result, root, { verbose: args.verbose }));
1978
2106
  if (shouldFail(result, gate)) {
1979
- process.exit(1);
2107
+ process.exitCode = 1;
1980
2108
  }
1981
2109
  }
1982
2110
  });
@@ -1985,5 +2113,5 @@ export {
1985
2113
  shouldFail
1986
2114
  };
1987
2115
 
1988
- //# debugId=5DFBDEAD3AD54A7664756E2164756E21
1989
- //# sourceMappingURL=chunk-79jhk4py.js.map
2116
+ //# debugId=E1B16241D184BD6E64756E2164756E21
2117
+ //# sourceMappingURL=chunk-wrr3j9w9.js.map