@kirchdev/duxt 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -0
  3. package/app/app.config.ts +13 -0
  4. package/app/app.vue +122 -0
  5. package/app/assets/css/duxt.css +555 -0
  6. package/app/assets/css/typeset.css +498 -0
  7. package/app/components/DuxtBrand.vue +53 -0
  8. package/app/components/DuxtBreadcrumb.vue +37 -0
  9. package/app/components/DuxtCodeBlock.vue +143 -0
  10. package/app/components/DuxtCopyPage.vue +117 -0
  11. package/app/components/DuxtFooter.vue +86 -0
  12. package/app/components/DuxtHeader.vue +496 -0
  13. package/app/components/DuxtJsonEditor.vue +359 -0
  14. package/app/components/DuxtLandingProse.vue +90 -0
  15. package/app/components/DuxtLandingShowcase.vue +295 -0
  16. package/app/components/DuxtLiveWindow.vue +343 -0
  17. package/app/components/DuxtLocale.vue +86 -0
  18. package/app/components/DuxtMetaList.vue +76 -0
  19. package/app/components/DuxtNavigation.vue +94 -0
  20. package/app/components/DuxtNavigationLink.vue +41 -0
  21. package/app/components/DuxtOpenApiCallbacks.vue +92 -0
  22. package/app/components/DuxtOpenApiClient.vue +1339 -0
  23. package/app/components/DuxtOpenApiMedia.vue +108 -0
  24. package/app/components/DuxtOpenApiMethod.vue +59 -0
  25. package/app/components/DuxtOpenApiParameters.vue +114 -0
  26. package/app/components/DuxtOpenApiResponses.vue +109 -0
  27. package/app/components/DuxtOpenApiSchema.vue +365 -0
  28. package/app/components/DuxtOpenApiSecurity.vue +115 -0
  29. package/app/components/DuxtPageFeedback.vue +56 -0
  30. package/app/components/DuxtPageInfo.vue +194 -0
  31. package/app/components/DuxtPageNav.vue +93 -0
  32. package/app/components/DuxtPreviewSkeleton.vue +262 -0
  33. package/app/components/DuxtProgress.vue +45 -0
  34. package/app/components/DuxtSearch.vue +271 -0
  35. package/app/components/DuxtSections.vue +65 -0
  36. package/app/components/DuxtShortcuts.vue +46 -0
  37. package/app/components/DuxtSkipLink.vue +34 -0
  38. package/app/components/DuxtToc.vue +86 -0
  39. package/app/components/DuxtTranslationBanner.vue +64 -0
  40. package/app/components/DuxtVersion.vue +168 -0
  41. package/app/components/DuxtVersionBanner.vue +109 -0
  42. package/app/components/OgImage/Duxt.satori.vue +82 -0
  43. package/app/components/content/Accordion.vue +37 -0
  44. package/app/components/content/AccordionItem.vue +46 -0
  45. package/app/components/content/Callout.vue +58 -0
  46. package/app/components/content/ChangelogGroup.vue +91 -0
  47. package/app/components/content/ChangelogReleases.vue +520 -0
  48. package/app/components/content/CodeGroup.vue +183 -0
  49. package/app/components/content/DevtoolsPanel.vue +107 -0
  50. package/app/components/content/Field.vue +50 -0
  51. package/app/components/content/FieldGroup.vue +8 -0
  52. package/app/components/content/FileTree.vue +92 -0
  53. package/app/components/content/Mermaid.vue +70 -0
  54. package/app/components/content/OpenApiOperation.vue +185 -0
  55. package/app/components/content/OpenApiOperations.vue +72 -0
  56. package/app/components/content/OpenApiOverview.vue +302 -0
  57. package/app/components/content/PackageManagers.vue +187 -0
  58. package/app/components/content/PageCards.vue +48 -0
  59. package/app/components/content/Partial.vue +65 -0
  60. package/app/components/content/Preview.vue +173 -0
  61. package/app/components/content/ProseA.vue +55 -0
  62. package/app/components/content/ProseH2.vue +53 -0
  63. package/app/components/content/ProseH3.vue +53 -0
  64. package/app/components/content/ProseH4.vue +53 -0
  65. package/app/components/content/ProseImg.vue +87 -0
  66. package/app/components/content/ProsePre.vue +58 -0
  67. package/app/components/content/ProseTable.vue +11 -0
  68. package/app/components/content/Since.vue +21 -0
  69. package/app/components/content/Steps.vue +20 -0
  70. package/app/components/content/Tab.vue +23 -0
  71. package/app/components/content/TabGroup.vue +58 -0
  72. package/app/components/ui/alert/Alert.vue +21 -0
  73. package/app/components/ui/alert/AlertDescription.vue +22 -0
  74. package/app/components/ui/alert/AlertTitle.vue +22 -0
  75. package/app/components/ui/alert/index.ts +24 -0
  76. package/app/components/ui/badge/Badge.vue +28 -0
  77. package/app/components/ui/badge/index.ts +52 -0
  78. package/app/components/ui/breadcrumb/Breadcrumb.vue +13 -0
  79. package/app/components/ui/breadcrumb/BreadcrumbEllipsis.vue +22 -0
  80. package/app/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
  81. package/app/components/ui/breadcrumb/BreadcrumbLink.vue +24 -0
  82. package/app/components/ui/breadcrumb/BreadcrumbList.vue +22 -0
  83. package/app/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
  84. package/app/components/ui/breadcrumb/BreadcrumbSeparator.vue +21 -0
  85. package/app/components/ui/breadcrumb/index.ts +7 -0
  86. package/app/components/ui/button/Button.vue +31 -0
  87. package/app/components/ui/button/index.ts +39 -0
  88. package/app/components/ui/card/Card.vue +22 -0
  89. package/app/components/ui/card/CardAction.vue +22 -0
  90. package/app/components/ui/card/CardContent.vue +14 -0
  91. package/app/components/ui/card/CardDescription.vue +17 -0
  92. package/app/components/ui/card/CardFooter.vue +17 -0
  93. package/app/components/ui/card/CardHeader.vue +22 -0
  94. package/app/components/ui/card/CardTitle.vue +17 -0
  95. package/app/components/ui/card/index.ts +7 -0
  96. package/app/components/ui/collapsible/Collapsible.vue +19 -0
  97. package/app/components/ui/collapsible/CollapsibleContent.vue +12 -0
  98. package/app/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
  99. package/app/components/ui/collapsible/index.ts +3 -0
  100. package/app/components/ui/command/Command.vue +98 -0
  101. package/app/components/ui/command/CommandDialog.vue +42 -0
  102. package/app/components/ui/command/CommandEmpty.vue +31 -0
  103. package/app/components/ui/command/CommandGroup.vue +52 -0
  104. package/app/components/ui/command/CommandInput.vue +45 -0
  105. package/app/components/ui/command/CommandItem.vue +85 -0
  106. package/app/components/ui/command/CommandList.vue +32 -0
  107. package/app/components/ui/command/CommandSeparator.vue +23 -0
  108. package/app/components/ui/command/CommandShortcut.vue +19 -0
  109. package/app/components/ui/command/index.ts +29 -0
  110. package/app/components/ui/dialog/Dialog.vue +15 -0
  111. package/app/components/ui/dialog/DialogClose.vue +12 -0
  112. package/app/components/ui/dialog/DialogContent.vue +61 -0
  113. package/app/components/ui/dialog/DialogDescription.vue +25 -0
  114. package/app/components/ui/dialog/DialogFooter.vue +30 -0
  115. package/app/components/ui/dialog/DialogHeader.vue +17 -0
  116. package/app/components/ui/dialog/DialogOverlay.vue +28 -0
  117. package/app/components/ui/dialog/DialogScrollContent.vue +65 -0
  118. package/app/components/ui/dialog/DialogTitle.vue +25 -0
  119. package/app/components/ui/dialog/DialogTrigger.vue +12 -0
  120. package/app/components/ui/dialog/index.ts +10 -0
  121. package/app/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  122. package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +47 -0
  123. package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +47 -0
  124. package/app/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
  125. package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
  126. package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +27 -0
  127. package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  128. package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +48 -0
  129. package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  130. package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +19 -0
  131. package/app/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  132. package/app/components/ui/dropdown-menu/DropdownMenuSubContent.vue +34 -0
  133. package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +34 -0
  134. package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  135. package/app/components/ui/dropdown-menu/index.ts +16 -0
  136. package/app/components/ui/input/Input.vue +35 -0
  137. package/app/components/ui/input/index.ts +1 -0
  138. package/app/components/ui/navigation-menu/NavigationMenu.vue +42 -0
  139. package/app/components/ui/navigation-menu/NavigationMenuContent.vue +35 -0
  140. package/app/components/ui/navigation-menu/NavigationMenuIndicator.vue +32 -0
  141. package/app/components/ui/navigation-menu/NavigationMenuItem.vue +23 -0
  142. package/app/components/ui/navigation-menu/NavigationMenuLink.vue +30 -0
  143. package/app/components/ui/navigation-menu/NavigationMenuList.vue +30 -0
  144. package/app/components/ui/navigation-menu/NavigationMenuTrigger.vue +31 -0
  145. package/app/components/ui/navigation-menu/NavigationMenuViewport.vue +30 -0
  146. package/app/components/ui/navigation-menu/index.ts +14 -0
  147. package/app/components/ui/scroll-area/ScrollArea.vue +31 -0
  148. package/app/components/ui/scroll-area/ScrollBar.vue +38 -0
  149. package/app/components/ui/scroll-area/index.ts +2 -0
  150. package/app/components/ui/select/Select.vue +15 -0
  151. package/app/components/ui/select/SelectContent.vue +59 -0
  152. package/app/components/ui/select/SelectGroup.vue +19 -0
  153. package/app/components/ui/select/SelectItem.vue +42 -0
  154. package/app/components/ui/select/SelectItemText.vue +12 -0
  155. package/app/components/ui/select/SelectLabel.vue +16 -0
  156. package/app/components/ui/select/SelectScrollDownButton.vue +28 -0
  157. package/app/components/ui/select/SelectScrollUpButton.vue +28 -0
  158. package/app/components/ui/select/SelectSeparator.vue +20 -0
  159. package/app/components/ui/select/SelectTrigger.vue +32 -0
  160. package/app/components/ui/select/SelectValue.vue +12 -0
  161. package/app/components/ui/select/index.ts +11 -0
  162. package/app/components/ui/separator/Separator.vue +30 -0
  163. package/app/components/ui/separator/index.ts +1 -0
  164. package/app/components/ui/sheet/Sheet.vue +15 -0
  165. package/app/components/ui/sheet/SheetClose.vue +12 -0
  166. package/app/components/ui/sheet/SheetContent.vue +68 -0
  167. package/app/components/ui/sheet/SheetDescription.vue +23 -0
  168. package/app/components/ui/sheet/SheetFooter.vue +15 -0
  169. package/app/components/ui/sheet/SheetHeader.vue +15 -0
  170. package/app/components/ui/sheet/SheetOverlay.vue +28 -0
  171. package/app/components/ui/sheet/SheetTitle.vue +23 -0
  172. package/app/components/ui/sheet/SheetTrigger.vue +12 -0
  173. package/app/components/ui/sheet/index.ts +8 -0
  174. package/app/components/ui/sidebar/Sidebar.vue +110 -0
  175. package/app/components/ui/sidebar/SidebarContent.vue +23 -0
  176. package/app/components/ui/sidebar/SidebarFooter.vue +18 -0
  177. package/app/components/ui/sidebar/SidebarGroup.vue +18 -0
  178. package/app/components/ui/sidebar/SidebarGroupAction.vue +31 -0
  179. package/app/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  180. package/app/components/ui/sidebar/SidebarGroupLabel.vue +30 -0
  181. package/app/components/ui/sidebar/SidebarHeader.vue +18 -0
  182. package/app/components/ui/sidebar/SidebarInput.vue +19 -0
  183. package/app/components/ui/sidebar/SidebarInset.vue +23 -0
  184. package/app/components/ui/sidebar/SidebarMenu.vue +18 -0
  185. package/app/components/ui/sidebar/SidebarMenuAction.vue +42 -0
  186. package/app/components/ui/sidebar/SidebarMenuBadge.vue +28 -0
  187. package/app/components/ui/sidebar/SidebarMenuButton.vue +60 -0
  188. package/app/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
  189. package/app/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  190. package/app/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
  191. package/app/components/ui/sidebar/SidebarMenuSub.vue +24 -0
  192. package/app/components/ui/sidebar/SidebarMenuSubButton.vue +43 -0
  193. package/app/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  194. package/app/components/ui/sidebar/SidebarProvider.vue +109 -0
  195. package/app/components/ui/sidebar/SidebarRail.vue +35 -0
  196. package/app/components/ui/sidebar/SidebarSeparator.vue +19 -0
  197. package/app/components/ui/sidebar/SidebarTrigger.vue +26 -0
  198. package/app/components/ui/sidebar/index.ts +62 -0
  199. package/app/components/ui/sidebar/utils.ts +19 -0
  200. package/app/components/ui/skeleton/Skeleton.vue +17 -0
  201. package/app/components/ui/skeleton/index.ts +1 -0
  202. package/app/components/ui/sonner/Sonner.vue +67 -0
  203. package/app/components/ui/sonner/index.ts +1 -0
  204. package/app/components/ui/tabs/Tabs.vue +26 -0
  205. package/app/components/ui/tabs/TabsContent.vue +23 -0
  206. package/app/components/ui/tabs/TabsList.vue +28 -0
  207. package/app/components/ui/tabs/TabsTrigger.vue +30 -0
  208. package/app/components/ui/tabs/index.ts +4 -0
  209. package/app/components/ui/textarea/Textarea.vue +32 -0
  210. package/app/components/ui/textarea/index.ts +1 -0
  211. package/app/components/ui/tooltip/Tooltip.vue +15 -0
  212. package/app/components/ui/tooltip/TooltipContent.vue +49 -0
  213. package/app/components/ui/tooltip/TooltipProvider.vue +14 -0
  214. package/app/components/ui/tooltip/TooltipTrigger.vue +12 -0
  215. package/app/components/ui/tooltip/index.ts +4 -0
  216. package/app/composables/useActiveHeading.ts +90 -0
  217. package/app/composables/useDuxtAnimatedHeight.ts +155 -0
  218. package/app/composables/useDuxtBreadcrumb.ts +60 -0
  219. package/app/composables/useDuxtChoice.ts +28 -0
  220. package/app/composables/useDuxtCollection.ts +89 -0
  221. package/app/composables/useDuxtConfig.ts +47 -0
  222. package/app/composables/useDuxtNavigation.ts +87 -0
  223. package/app/composables/useDuxtPageFocus.ts +30 -0
  224. package/app/composables/useDuxtPath.ts +42 -0
  225. package/app/composables/useDuxtSearch.ts +208 -0
  226. package/app/composables/useDuxtSection.ts +44 -0
  227. package/app/composables/useDuxtSectionRow.ts +49 -0
  228. package/app/composables/useDuxtShortcuts.ts +58 -0
  229. package/app/composables/useDuxtSiteUrl.ts +27 -0
  230. package/app/composables/useDuxtToast.ts +25 -0
  231. package/app/composables/useDuxtVersion.ts +138 -0
  232. package/app/composables/useFuzzySearch.ts +83 -0
  233. package/app/composables/usePackageManager.ts +4 -0
  234. package/app/composables/useRecentPages.ts +52 -0
  235. package/app/error.vue +133 -0
  236. package/app/layouts/changelog.vue +85 -0
  237. package/app/layouts/default.vue +23 -0
  238. package/app/layouts/docs.vue +62 -0
  239. package/app/layouts/reference.vue +85 -0
  240. package/app/lib/utils.ts +7 -0
  241. package/app/middleware/duxt-section-layout.global.ts +57 -0
  242. package/app/pages/[...slug].vue +441 -0
  243. package/app/pages/index.vue +479 -0
  244. package/app/types/duxt.d.ts +955 -0
  245. package/app/utils/aside.ts +16 -0
  246. package/app/utils/changelog.ts +182 -0
  247. package/app/utils/code-highlight.ts +116 -0
  248. package/app/utils/duxt-config.ts +185 -0
  249. package/app/utils/duxt-text.ts +135 -0
  250. package/app/utils/file-icons.ts +62 -0
  251. package/app/utils/generated-sections.ts +190 -0
  252. package/app/utils/generated-toc.ts +216 -0
  253. package/app/utils/heading-link.ts +32 -0
  254. package/app/utils/locale-path.ts +45 -0
  255. package/app/utils/navigation-tree.ts +206 -0
  256. package/app/utils/nearest-page.ts +72 -0
  257. package/app/utils/openapi.ts +1028 -0
  258. package/app/utils/package-command.ts +162 -0
  259. package/app/utils/request-samples.ts +635 -0
  260. package/app/utils/section-scope.ts +97 -0
  261. package/app/utils/sheet-navigation.ts +100 -0
  262. package/app/utils/source-file.ts +84 -0
  263. package/app/utils/version-choices.ts +68 -0
  264. package/app/utils/version-paths.ts +65 -0
  265. package/bin/duxt-report.mjs +45 -0
  266. package/components.json +19 -0
  267. package/content-cache.ts +193 -0
  268. package/content.config.ts +38 -0
  269. package/duxt-app-config.ts +124 -0
  270. package/highlight-langs.ts +68 -0
  271. package/i18n/i18n.config.ts +16 -0
  272. package/i18n/locales/de/duxt/changelog.json +17 -0
  273. package/i18n/locales/de/duxt/code.json +13 -0
  274. package/i18n/locales/de/duxt/defaults.json +22 -0
  275. package/i18n/locales/de/duxt/devtools.json +7 -0
  276. package/i18n/locales/de/duxt/error.json +10 -0
  277. package/i18n/locales/de/duxt/footer.json +7 -0
  278. package/i18n/locales/de/duxt/locale.json +7 -0
  279. package/i18n/locales/de/duxt/nav.json +17 -0
  280. package/i18n/locales/de/duxt/openapi.json +89 -0
  281. package/i18n/locales/de/duxt/page.json +34 -0
  282. package/i18n/locales/de/duxt/search.json +12 -0
  283. package/i18n/locales/de/duxt/shortcuts.json +11 -0
  284. package/i18n/locales/de/duxt/theme.json +7 -0
  285. package/i18n/locales/de/duxt/toc.json +7 -0
  286. package/i18n/locales/de/duxt/version.json +25 -0
  287. package/i18n/locales/en/duxt/changelog.json +17 -0
  288. package/i18n/locales/en/duxt/code.json +13 -0
  289. package/i18n/locales/en/duxt/defaults.json +22 -0
  290. package/i18n/locales/en/duxt/devtools.json +7 -0
  291. package/i18n/locales/en/duxt/error.json +10 -0
  292. package/i18n/locales/en/duxt/footer.json +7 -0
  293. package/i18n/locales/en/duxt/locale.json +7 -0
  294. package/i18n/locales/en/duxt/nav.json +17 -0
  295. package/i18n/locales/en/duxt/openapi.json +89 -0
  296. package/i18n/locales/en/duxt/page.json +34 -0
  297. package/i18n/locales/en/duxt/search.json +12 -0
  298. package/i18n/locales/en/duxt/shortcuts.json +11 -0
  299. package/i18n/locales/en/duxt/theme.json +7 -0
  300. package/i18n/locales/en/duxt/toc.json +7 -0
  301. package/i18n/locales/en/duxt/version.json +25 -0
  302. package/i18n/locales/es/duxt/changelog.json +17 -0
  303. package/i18n/locales/es/duxt/code.json +13 -0
  304. package/i18n/locales/es/duxt/defaults.json +22 -0
  305. package/i18n/locales/es/duxt/devtools.json +7 -0
  306. package/i18n/locales/es/duxt/error.json +10 -0
  307. package/i18n/locales/es/duxt/footer.json +7 -0
  308. package/i18n/locales/es/duxt/locale.json +7 -0
  309. package/i18n/locales/es/duxt/nav.json +17 -0
  310. package/i18n/locales/es/duxt/openapi.json +89 -0
  311. package/i18n/locales/es/duxt/page.json +34 -0
  312. package/i18n/locales/es/duxt/search.json +12 -0
  313. package/i18n/locales/es/duxt/shortcuts.json +11 -0
  314. package/i18n/locales/es/duxt/theme.json +7 -0
  315. package/i18n/locales/es/duxt/toc.json +7 -0
  316. package/i18n/locales/es/duxt/version.json +25 -0
  317. package/i18n/locales/fr/duxt/changelog.json +17 -0
  318. package/i18n/locales/fr/duxt/code.json +13 -0
  319. package/i18n/locales/fr/duxt/defaults.json +22 -0
  320. package/i18n/locales/fr/duxt/devtools.json +7 -0
  321. package/i18n/locales/fr/duxt/error.json +10 -0
  322. package/i18n/locales/fr/duxt/footer.json +7 -0
  323. package/i18n/locales/fr/duxt/locale.json +7 -0
  324. package/i18n/locales/fr/duxt/nav.json +17 -0
  325. package/i18n/locales/fr/duxt/openapi.json +89 -0
  326. package/i18n/locales/fr/duxt/page.json +34 -0
  327. package/i18n/locales/fr/duxt/search.json +12 -0
  328. package/i18n/locales/fr/duxt/shortcuts.json +11 -0
  329. package/i18n/locales/fr/duxt/theme.json +7 -0
  330. package/i18n/locales/fr/duxt/toc.json +7 -0
  331. package/i18n/locales/fr/duxt/version.json +25 -0
  332. package/i18n/locales/pt/duxt/changelog.json +17 -0
  333. package/i18n/locales/pt/duxt/code.json +13 -0
  334. package/i18n/locales/pt/duxt/defaults.json +22 -0
  335. package/i18n/locales/pt/duxt/devtools.json +7 -0
  336. package/i18n/locales/pt/duxt/error.json +10 -0
  337. package/i18n/locales/pt/duxt/footer.json +7 -0
  338. package/i18n/locales/pt/duxt/locale.json +7 -0
  339. package/i18n/locales/pt/duxt/nav.json +17 -0
  340. package/i18n/locales/pt/duxt/openapi.json +89 -0
  341. package/i18n/locales/pt/duxt/page.json +34 -0
  342. package/i18n/locales/pt/duxt/search.json +12 -0
  343. package/i18n/locales/pt/duxt/shortcuts.json +11 -0
  344. package/i18n/locales/pt/duxt/theme.json +7 -0
  345. package/i18n/locales/pt/duxt/toc.json +7 -0
  346. package/i18n/locales/pt/duxt/version.json +25 -0
  347. package/i18n/locales/pt-BR/duxt/error.json +8 -0
  348. package/i18n/locales/pt-BR/duxt/nav.json +10 -0
  349. package/i18n/locales/pt-BR/duxt/page.json +10 -0
  350. package/i18n/locales/pt-BR/duxt/search.json +11 -0
  351. package/i18n/locales/pt-BR/duxt/version.json +13 -0
  352. package/mdc.config.ts +28 -0
  353. package/modules/config.ts +440 -0
  354. package/modules/devtools.ts +126 -0
  355. package/modules/git-meta.ts +349 -0
  356. package/modules/redirects.ts +152 -0
  357. package/modules/validate.ts +137 -0
  358. package/nuxt.config.ts +530 -0
  359. package/openapi-model.ts +358 -0
  360. package/openapi-parse.ts +1052 -0
  361. package/package.json +161 -0
  362. package/public/devtools/cache.html +318 -0
  363. package/public/devtools/checks.html +309 -0
  364. package/public/devtools/config.html +461 -0
  365. package/public/devtools/i18n.html +309 -0
  366. package/public/devtools/pages.html +348 -0
  367. package/public/devtools/paths.html +315 -0
  368. package/public/devtools/redirects.html +309 -0
  369. package/public/devtools/search.html +324 -0
  370. package/public/devtools/sources.html +309 -0
  371. package/public/devtools/versions.html +318 -0
  372. package/report.ts +303 -0
  373. package/repository-root.ts +26 -0
  374. package/section-reports.ts +55 -0
  375. package/sections-changelog.ts +644 -0
  376. package/sections-openapi.ts +450 -0
  377. package/sections-resolve.ts +711 -0
  378. package/sections.ts +157 -0
  379. package/server/devtools/content.ts +120 -0
  380. package/server/devtools/context.ts +38 -0
  381. package/server/devtools/entry-path.ts +30 -0
  382. package/server/devtools/handler.ts +104 -0
  383. package/server/devtools/preview.ts +461 -0
  384. package/server/devtools/render/content.ts +233 -0
  385. package/server/devtools/render/sources.ts +414 -0
  386. package/server/devtools/render/system.ts +352 -0
  387. package/server/devtools/shell.ts +611 -0
  388. package/server/devtools/sources.ts +122 -0
  389. package/server/devtools/system.ts +178 -0
  390. package/server/mcp/tools/list-pages.ts +52 -0
  391. package/server/mcp/tools/read-page.ts +57 -0
  392. package/server/mcp/tools/search-docs.ts +74 -0
  393. package/server/middleware/raw-markdown.ts +86 -0
  394. package/server/routes/llms-full.txt.get.ts +75 -0
  395. package/server/routes/llms.txt.get.ts +63 -0
  396. package/server/routes/rss.xml.get.ts +125 -0
  397. package/server/utils/duxt-server-text.ts +46 -0
  398. package/sources-git.ts +113 -0
  399. package/sources-resolve.ts +908 -0
  400. package/sources.ts +287 -0
  401. package/validate-report.ts +494 -0
@@ -0,0 +1,711 @@
1
+ /**
2
+ * Generated sections: an artefact that is not Markdown, published as pages.
3
+ *
4
+ * A source declares `generated: [{ type, path, label }]` beside its docs tree,
5
+ * and each entry becomes an ordinary Content collection at a URL prefix of its
6
+ * own. That is the whole design decision: everything the layer can already do
7
+ * hangs off a collection — search, `llms.txt`, the feed, the sitemap, the
8
+ * redirects, the 404 fallback and the MCP tools all iterate the resolved
9
+ * manifest — so a section that IS a collection inherits them instead of
10
+ * rebuilding them one type at a time.
11
+ *
12
+ * Pure, and separate from `sections.ts` for the reason `sources-resolve.ts` is
13
+ * separate from `sources.ts`: that half imports `@nuxt/content` and reads
14
+ * files, and the browser refuses to follow either out of app code. This half is
15
+ * a list in, a manifest out, which is what `app.config.ts` and the tests read.
16
+ */
17
+ import type {
18
+ DuxtRef,
19
+ DuxtResolvedSource,
20
+ DuxtSource,
21
+ DuxtSourceStatus,
22
+ DuxtSourcesOptions
23
+ } from './sources-resolve';
24
+ import {
25
+ expandSources,
26
+ identifier,
27
+ resolveSources,
28
+ slugify
29
+ } from './sources-resolve';
30
+ import { changelogSectionType } from './sections-changelog';
31
+ import { openapiSectionType } from './sections-openapi';
32
+
33
+ /**
34
+ * One artefact beside a source's Markdown, published as pages of the site.
35
+ *
36
+ * A LIST, because a monorepo is the normal case: `release-please-config.json`
37
+ * already declares a `changelog-path` per package, so several artefacts beside
38
+ * one docs tree is ordinary rather than exotic.
39
+ *
40
+ * Not called `sections`: `duxt.sections` is taken for the second navbar row,
41
+ * and two meanings on one public name is a collision the layer pays for later.
42
+ */
43
+ /**
44
+ * One version of a generated section — the artefact, and what it is called.
45
+ *
46
+ * `path` and `locales` are the declaration's own, overridden per version: a
47
+ * translated artefact is translated per version, because that is the file that
48
+ * exists.
49
+ */
50
+ export interface DuxtGeneratedSectionVersion {
51
+ /** Shown in the switcher and used in the URL. */
52
+ version: string;
53
+ /** The artefact this version reads; defaults to the declaration's. */
54
+ path?: string;
55
+ /** Per-locale artefacts for this version; defaults to the declaration's. */
56
+ locales?: Record<string, string>;
57
+ /** Lifecycle of this version. See `DuxtSourceStatus`. */
58
+ status?: DuxtSourceStatus;
59
+ /** Served without a version segment. Defaults to the first in the list. */
60
+ default?: boolean;
61
+ }
62
+
63
+ export interface DuxtGeneratedSection {
64
+ /** The registry key of the type that parses it, e.g. `changelog`. */
65
+ type: string;
66
+ /**
67
+ * The artefact, relative to the SOURCE'S OWN ROOT — the repository root for
68
+ * a source read off disk, the root of the downloaded checkout for one Content
69
+ * clones. One rule in both directions.
70
+ *
71
+ * A path only, never a URL: an arbitrary location would let an artefact be
72
+ * fetched from anywhere and reopen the build-time-network question the
73
+ * `sources` shorthand has already closed.
74
+ */
75
+ path: string;
76
+ /**
77
+ * The navbar entry's label, and — slugified — its URL segment.
78
+ *
79
+ * A plain string rather than a `DuxtText`, for the reason a version's label
80
+ * is one: a translated text is not a stable URL. Same pair as `refs`.
81
+ */
82
+ label: string;
83
+ /** URL segment for this section; defaults to the slugified label. */
84
+ slug?: string;
85
+ /**
86
+ * The versions of THIS ARTEFACT, where the source's own refs do not name
87
+ * them.
88
+ *
89
+ * A version is normally a checkout: list `refs` on the source and every
90
+ * section it carries is published once per ref. An API is usually not kept
91
+ * that way — `openapi/v1.yaml` sits beside `openapi/v2.yaml` in one
92
+ * repository, versioned by FILE — and without this key the two are two
93
+ * unrelated sections with two navbar entries.
94
+ *
95
+ * Declared HERE rather than as two sources, and that is the whole design: two
96
+ * sections are offered as versions of one another only when they came from
97
+ * one declaration (see `DuxtGeneratedMeta.declaration`), and a source always
98
+ * publishes a documentation tree, so a source per API version would publish
99
+ * the prose twice. One declaration, several versions, one artefact.
100
+ *
101
+ * Never beside a versioned SOURCE: the two would each want the same segment
102
+ * of the URL, and the resolver says so rather than nesting them.
103
+ */
104
+ versions?: DuxtGeneratedSectionVersion[];
105
+ /**
106
+ * The knobs THIS TYPE offers, as the site turned them.
107
+ *
108
+ * Opaque to the scaffold on purpose: a granularity means something to a
109
+ * changelog and nothing to an API reference, and a scaffold that knew the
110
+ * difference would have to grow a field per type. So it is carried, not
111
+ * read — the type validates its own and says so in its own words.
112
+ */
113
+ options?: DuxtSectionOptions;
114
+ /**
115
+ * The artefact a LOCALE reads, where that locale ships one of its own.
116
+ *
117
+ * Only a `per-locale` type reads this — a `changelog` is written once by the
118
+ * release tool and says so through the translation banner instead. Keyed by
119
+ * the locale code the source declares, with the language alone accepted as
120
+ * well, so `pt` answers for `pt-BR`.
121
+ *
122
+ * A locale absent from this map builds NO collection of its own, which is
123
+ * the point: the existing `fallbackLocale` chain then resolves it to the
124
+ * default language's section and `DuxtTranslationBanner` says the reader is
125
+ * looking at the original. Declaring an artefact for every locale and
126
+ * quietly serving the same file under each would leave that banner silent
127
+ * and the reader unaware.
128
+ */
129
+ locales?: Record<string, string>;
130
+ /**
131
+ * Where its navbar entry goes.
132
+ *
133
+ * `sections` is the second navbar row — the existing "top-level parts of the
134
+ * documentation" — and the default, so a declared section is findable
135
+ * without further config. `navigation` is the narrow first row, which stays
136
+ * a deliberate choice. `false` puts it nowhere and leaves the site to link
137
+ * it.
138
+ */
139
+ navigation?: DuxtSectionPlacement;
140
+ /** Icon for that entry; falls back to the type's own. */
141
+ icon?: string;
142
+ }
143
+
144
+ export type DuxtSectionPlacement = 'navigation' | 'sections' | false;
145
+
146
+ /**
147
+ * What a declaration hands its own type, and nothing else reads.
148
+ *
149
+ * A bag rather than a union of the known shapes: the registry is open, so the
150
+ * set of types is not the layer's to enumerate — a consumer's own type gets the
151
+ * same seam the built-in ones use.
152
+ */
153
+ export type DuxtSectionOptions = Record<string, unknown>;
154
+
155
+ /** One page a type's parser produced out of an artefact. */
156
+ export interface DuxtSectionPage {
157
+ /**
158
+ * File name inside the section, extension included.
159
+ *
160
+ * Content reads the name the way it reads any other: a `NN.` prefix orders
161
+ * the page and is stripped from the URL, so a changelog can be newest-first
162
+ * without the order leaking into the address.
163
+ */
164
+ file: string;
165
+ /** The whole file — frontmatter block and body. */
166
+ body: string;
167
+ }
168
+
169
+ /** What the parser is told about the section it is filling. */
170
+ export interface DuxtSectionContext {
171
+ /** The declared label, which is also the index page's title. */
172
+ label: string;
173
+ /** The URL prefix its pages are served at, e.g. `/releases`. */
174
+ prefix: string;
175
+ /** The declaration's own `options`, empty where it named none. */
176
+ options: DuxtSectionOptions;
177
+ /**
178
+ * What the type could not make sense of, but carried on past.
179
+ *
180
+ * NOT `console.warn`, which is what both built-in types did: an artefact is
181
+ * parsed while the config is being loaded, so a warning printed there has
182
+ * scrolled away long before anyone looks — the same failure the Checks panel
183
+ * exists to fix. Collected here instead, the finding reaches
184
+ * `DuxtSectionReport` and from there the ONE report this layer has: the build
185
+ * validator prints it and the Checks panel keeps it.
186
+ *
187
+ * For findings the parser survives. What it cannot read at all it throws on,
188
+ * and a section that produced no pages is reported by the scaffold rather
189
+ * than by the type — neither needs this.
190
+ *
191
+ * Optional, because a type is called with a bare context in a test.
192
+ */
193
+ warn?: (message: string) => void;
194
+ }
195
+
196
+ /**
197
+ * What reading one artefact said about it, for the report.
198
+ *
199
+ * On the manifest entry rather than in a registry of its own, and that is the
200
+ * same decision `duxtSectionTypes` documents above: `content.config.ts` and the
201
+ * duxt module are loaded through two different loaders, so a module-level
202
+ * collector written by one of them is invisible to the other. A field on the
203
+ * entry travels with the manifest that is already handed around — into
204
+ * `app.config`, which is how the Checks panel sees it at all.
205
+ */
206
+ export interface DuxtSectionReport {
207
+ /** How many pages the type produced. */
208
+ pages: number;
209
+ /** What the type carried on past — see `DuxtSectionContext.warn`. */
210
+ warnings: string[];
211
+ /** Set when the declared artefact was not there to read. */
212
+ missing?: boolean;
213
+ }
214
+
215
+ /**
216
+ * A type in the registry: what turns one artefact into pages.
217
+ *
218
+ * The whole extension surface, and public from day one. While the package is
219
+ * `0.x`, `bump-minor-pre-major` makes a `feat!:` a minor bump, so correcting
220
+ * this shape costs no major release — the promise is reviewed once before
221
+ * `1.0.0`, which is when it stops being cheap.
222
+ */
223
+ export interface DuxtSectionType {
224
+ /** The artefact, split into pages carrying their own frontmatter. */
225
+ parse: (artefact: string, context: DuxtSectionContext) => DuxtSectionPage[];
226
+ /**
227
+ * How this type behaves against the one-collection-per-version mechanic.
228
+ *
229
+ * `global` is one history read from the default version and served at a
230
+ * version-neutral URL, with the switcher suppressed — what a changelog is.
231
+ * `per-version` is a section per version, like any other page.
232
+ */
233
+ versioning: 'global' | 'per-version';
234
+ /**
235
+ * What a localised site shows when the artefact has one language.
236
+ *
237
+ * `original` builds ONE collection from the default locale and lets the
238
+ * existing translation banner say so — a release log is written once by the
239
+ * release tool, and there is nothing per-locale about it.
240
+ *
241
+ * `per-locale` follows the source's own languages, as far as the declaration
242
+ * reaches: a collection for the default language, and one for every locale
243
+ * `DuxtGeneratedSection.locales` names an artefact for. A locale it does not
244
+ * name builds NOTHING rather than a copy of the original — which is what
245
+ * leaves `sourcesForRoute` free to fall through to the default entry and
246
+ * `DuxtTranslationBanner` free to say the reader is looking at the original.
247
+ */
248
+ localisation: 'original' | 'per-locale';
249
+ /**
250
+ * The layout its pages render in, through the shared slot.
251
+ *
252
+ * A layout name a type binds is PUBLIC SURFACE — renaming one later is a
253
+ * `feat!:`. Unset renders the section in the ordinary docs chrome.
254
+ *
255
+ * A FUNCTION where the declaration's own options decide: the same type can
256
+ * produce two different things, and a changelog is the case that proves it —
257
+ * split into a page per release it is a timeline with chrome of its own,
258
+ * rendered as the one file it was written as it is an ordinary docs page and
259
+ * wants the sidebar, the breadcrumb and the table of contents back.
260
+ */
261
+ layout?: string | ((options: DuxtSectionOptions) => string | undefined);
262
+ /** Icon for the navbar entry, when the declaration names none. */
263
+ icon?: string;
264
+ }
265
+
266
+ /** The registry: type name to type. */
267
+ export type DuxtSectionTypes = Record<string, DuxtSectionType>;
268
+
269
+ /** The types the layer ships. */
270
+ export const duxtBuiltinSectionTypes: DuxtSectionTypes = {
271
+ changelog: changelogSectionType,
272
+ openapi: openapiSectionType
273
+ };
274
+
275
+ /**
276
+ * The registry, open for a consumer to add to.
277
+ *
278
+ * A merge rather than a mutable global: `content.config.ts` and the duxt module
279
+ * are loaded through two different loaders, and a registry each of them wrote
280
+ * into would be two registries. Handing the map in keeps both halves reading
281
+ * the one the site declared — the same reason `sources` is data rather than a
282
+ * hook. A consumer's entry wins, so a type can also be replaced.
283
+ */
284
+ export function duxtSectionTypes(custom?: DuxtSectionTypes): DuxtSectionTypes {
285
+ return { ...duxtBuiltinSectionTypes, ...custom };
286
+ }
287
+
288
+ /**
289
+ * What a generated collection carries beyond an ordinary source.
290
+ *
291
+ * Lives under one key rather than as loose fields, so `source.generated` is the
292
+ * single question anything downstream has to ask — the version switcher, the
293
+ * layout slot and the "edit this page" link each ask it once.
294
+ */
295
+ export interface DuxtGeneratedMeta {
296
+ /** The registry key the artefact was parsed with. */
297
+ type: string;
298
+ /** The navbar label, as declared. */
299
+ label: string;
300
+ /** The URL segment, as resolved. */
301
+ slug: string;
302
+ /**
303
+ * WHICH DECLARATION produced this entry — the identity, not an address.
304
+ *
305
+ * A section is one collection per version and per locale, so a single
306
+ * `generated: [...]` entry reaches the manifest as several sources, and the
307
+ * navbar has to put ONE link in the row for all of them. Which of them belong
308
+ * together is known exactly here, while `source.generated` is being iterated,
309
+ * and nowhere else afterwards — so it is recorded rather than reconstructed.
310
+ *
311
+ * It was reconstructed, from `(slug, repository)`, and that guess is wrong in
312
+ * both directions: two sources declaring a `Releases` section — per-package
313
+ * docs trees in a monorepo, the shape this feature calls the normal case —
314
+ * read as one declaration and lost an entry from the row, while nothing in
315
+ * the resolver ever promised the pair was unique. Prefixes are claimed per
316
+ * `(locale, prefix)`, so those two sections are not a collision and no error
317
+ * is raised.
318
+ *
319
+ * Its VALUE means nothing: a counter over the declarations in the site's own
320
+ * source list, only ever compared for equality inside one manifest.
321
+ */
322
+ declaration: number;
323
+ /** Where its navbar entry goes. */
324
+ navigation: DuxtSectionPlacement;
325
+ /** Icon for that entry. */
326
+ icon?: string;
327
+ /** The layout its pages render in, when the type names one for these options. */
328
+ layout?: string;
329
+ /** The declaration's own options, where it named any. */
330
+ options?: DuxtSectionOptions;
331
+ /** The type's versioning policy — `global` suppresses the switcher. */
332
+ versioning: 'global' | 'per-version';
333
+ /** The type's localisation policy. */
334
+ localisation: 'original' | 'per-locale';
335
+ /** Whether Content downloads the repository this artefact sits in. */
336
+ remote: boolean;
337
+ /**
338
+ * What reading the artefact said about it — see `DuxtSectionReport`.
339
+ *
340
+ * Absent until something has actually read it, which is not the resolver's
341
+ * job: `resolveGeneratedSections` is pure and never touches disk. It is
342
+ * filled by whoever reads the file — `sections.ts` while the collections are
343
+ * being declared, and `readSectionReports` for the manifest the modules hold.
344
+ * Absent therefore means "not read here", never "nothing to report", and the
345
+ * checks are written to say nothing rather than guess.
346
+ */
347
+ report?: DuxtSectionReport;
348
+ }
349
+
350
+ /**
351
+ * The generated sections a source list declares, as manifest entries.
352
+ *
353
+ * They are ORDINARY resolved sources with one extra key, because that is what
354
+ * makes the rest of the layer work on them unchanged: `sourcesForRoute` picks
355
+ * the longest matching prefix, so `/releases` beats the docs tree at `/` with
356
+ * no special case anywhere.
357
+ */
358
+ export function resolveGeneratedSections(
359
+ sources: DuxtSource[],
360
+ options: DuxtSourcesOptions = {},
361
+ types: DuxtSectionTypes = duxtSectionTypes()
362
+ ): DuxtResolvedSource[] {
363
+ const resolved = resolveSources(sources, options);
364
+ const expanded = expandSources(sources, options);
365
+ const generated: DuxtResolvedSource[] = [];
366
+
367
+ // Claimed per locale, exactly as `resolveSources` claims its own prefixes:
368
+ // two languages serving one prefix is not a collision, it is the point.
369
+ const taken = new Map<string, string>(
370
+ resolved.map((entry) => [
371
+ `${entry.locale ?? ''}|${entry.prefix}`,
372
+ `the documentation at "${entry.prefix || '/'}"`
373
+ ])
374
+ );
375
+
376
+ // Counted over the whole list rather than per source, so the identity is
377
+ // unique across the manifest without anything having to pair it with a source
378
+ // again. Incremented for every declaration, including one that resolves to no
379
+ // entry at all: it names a declaration, not a row of the output.
380
+ let declarations = 0;
381
+
382
+ sources.forEach((source) => {
383
+ for (const declared of source.generated ?? []) {
384
+ const declaration = declarations++;
385
+ const type = types[declared.type];
386
+
387
+ if (!type) {
388
+ // Named rather than ignored: an unknown type is a typo in the site's
389
+ // own config, and a section silently absent is the failure this layer
390
+ // exists to stop.
391
+ throw new Error(
392
+ `duxt: the generated section "${declared.label}" asks for the type ` +
393
+ `"${declared.type}", which no type in the registry answers to ` +
394
+ `(${Object.keys(types).sort().join(', ') || 'none'}). ` +
395
+ 'Register it in `duxt.sectionTypes`.'
396
+ );
397
+ }
398
+
399
+ const options = declared.options ?? {};
400
+
401
+ // LOWERCASED, and that is not a style choice. Content slugifies a page's
402
+ // own path with `lower: true`, so a section called `Releases` would be
403
+ // served at `/releases` while the manifest said `/Releases` — and every
404
+ // lookup that matches a route against a prefix would miss.
405
+ const slug = slugify(declared.slug ?? declared.label).toLowerCase();
406
+
407
+ if (!slug) {
408
+ throw new Error(
409
+ `duxt: the generated section "${declared.label}" resolves to an ` +
410
+ 'empty URL segment. Give it a `slug`.'
411
+ );
412
+ }
413
+
414
+ // One pass where the declaration names no versions, so a section that
415
+ // has none resolves exactly as it did.
416
+ const editions: (DuxtGeneratedSectionVersion | undefined)[] = declared
417
+ .versions?.length
418
+ ? declared.versions
419
+ : [undefined];
420
+
421
+ for (const base of basesFor(source, resolved, expanded, type, declared)) {
422
+ for (const [index, edition] of editions.entries()) {
423
+ if (edition && base.entry.version) {
424
+ throw new Error(
425
+ `duxt: the generated section "${declared.label}" names its own ` +
426
+ 'versions and sits on a source that is versioned by refs. ' +
427
+ 'Both want the same segment of the URL — put the versions on ' +
428
+ 'one of the two.'
429
+ );
430
+ }
431
+
432
+ // A version-NEUTRAL type has one history at one URL, so a list of
433
+ // versions is a contradiction rather than a shape to resolve.
434
+ if (edition && type.versioning === 'global') {
435
+ throw new Error(
436
+ `duxt: the generated section "${declared.label}" names versions, ` +
437
+ `but the type "${declared.type}" publishes one history at a ` +
438
+ 'version-neutral URL.'
439
+ );
440
+ }
441
+
442
+ const editionDefault =
443
+ editions.length === 1 ||
444
+ (edition?.default ??
445
+ (!editions.some((entry) => entry?.default) && index === 0));
446
+
447
+ const version = edition ? slugify(edition.version) : undefined;
448
+
449
+ const segments = version && !editionDefault ? `/${version}` : '';
450
+ const artefact = edition
451
+ ? (artefactFor({ ...declared, ...edition }, base.entry) ??
452
+ base.path)
453
+ : base.path;
454
+
455
+ const prefix = `${base.entry.prefix}${segments}/${slug}`;
456
+ const claim = `${base.entry.locale ?? ''}|${prefix}`;
457
+ const previous = taken.get(claim);
458
+
459
+ if (previous) {
460
+ throw new Error(
461
+ `duxt: the generated section "${declared.label}" resolves to the ` +
462
+ `URL prefix "${prefix}", which ${previous} already claims. ` +
463
+ 'Give the section a `slug`.'
464
+ );
465
+ }
466
+
467
+ taken.set(claim, `the generated section "${declared.label}"`);
468
+
469
+ generated.push({
470
+ // THE VERSION IS PART OF THE NAME where the declaration owns it.
471
+ // A ref-versioned source hands each version its own base entry, so
472
+ // the base's name already differs; a declaration's versions share
473
+ // one base, and without this both editions claimed one collection —
474
+ // the second overwrote the first in `content.config.ts`, and the
475
+ // default version 404'd while the older one rendered.
476
+ collection: `${base.entry.collection}_${identifier(slug)}${
477
+ version ? `_${identifier(version)}` : ''
478
+ }`,
479
+ prefix,
480
+ repo: base.entry.repo,
481
+ // `global` is version-NEUTRAL, so it carries no version at all: the
482
+ // switcher lists what has a version, and a changelog must not be
483
+ // offered as one of the versions of the documentation beside it.
484
+ version:
485
+ type.versioning === 'per-version'
486
+ ? (version ?? base.entry.version)
487
+ : undefined,
488
+ // FOLLOWS THE VERSION IT WAS READ AT, where there is one. This was
489
+ // hard-wired to `true`, which is right for a `global` section — one
490
+ // entry, served at a URL with no version in it — and wrong for every
491
+ // other: it made the v1.9 reference claim to be the default as
492
+ // loudly as the v2 one. Two things read that claim and both got it
493
+ // wrong. `excludeOldVersionsFromSitemap` hides what is not the
494
+ // default, so a deprecated reference stayed in the sitemap while the
495
+ // deprecated documentation beside it was excluded; and the version
496
+ // switcher captions the default, so it offered two of them.
497
+ isDefault:
498
+ type.versioning === 'per-version'
499
+ ? base.entry.isDefault && editionDefault
500
+ : true,
501
+ repository: base.entry.repository,
502
+ repositoryUrl: base.entry.repositoryUrl,
503
+ ref: base.entry.ref,
504
+ refKind: base.entry.refKind,
505
+ // The ARTEFACT, not a folder: `DuxtPageInfo` links back to the file a
506
+ // page was written in, and for a generated section every page in it
507
+ // was written in this one. Per LOCALE where the declaration names one
508
+ // — see `artefactFor`.
509
+ path: artefact,
510
+ locale: base.entry.locale,
511
+ isDefaultLocale: base.entry.isDefaultLocale,
512
+ status: edition?.status ?? base.entry.status,
513
+ // Never read for history. Its pages have no file of their own on
514
+ // disk, so `git log` would answer about nothing; a type that has
515
+ // dates puts them in the frontmatter it writes.
516
+ history: false,
517
+ generated: {
518
+ type: declared.type,
519
+ label: declared.label,
520
+ slug,
521
+ declaration,
522
+ navigation: declared.navigation ?? 'sections',
523
+ icon: declared.icon ?? type.icon,
524
+ layout:
525
+ typeof type.layout === 'function'
526
+ ? type.layout(options)
527
+ : type.layout,
528
+ versioning: type.versioning,
529
+ localisation: type.localisation,
530
+ remote: base.remote,
531
+ // Only when the site named some: an empty object where there was
532
+ // `undefined` is a different value in the manifest every page
533
+ // ships, and this one changes on no site that declares nothing.
534
+ ...(Object.keys(options).length ? { options } : {})
535
+ }
536
+ });
537
+ }
538
+ }
539
+ }
540
+ });
541
+
542
+ return generated;
543
+ }
544
+
545
+ /**
546
+ * Which resolved entries a section hangs off, per the type's own policies.
547
+ *
548
+ * Making versioning and localisation POLICIES is what turns the sharpest
549
+ * difference between two types into a parameter instead of a fork: a changelog
550
+ * is one global history, an API reference genuinely is per version, and both
551
+ * are then ordinary answers rather than special cases.
552
+ */
553
+ function basesFor(
554
+ source: DuxtSource,
555
+ resolved: DuxtResolvedSource[],
556
+ expanded: ReturnType<typeof expandSources>,
557
+ type: DuxtSectionType,
558
+ declared: DuxtGeneratedSection
559
+ ): { entry: DuxtResolvedSource; path: string; remote: boolean }[] {
560
+ const bases: { entry: DuxtResolvedSource; path: string; remote: boolean }[] =
561
+ [];
562
+
563
+ resolved.forEach((entry, index) => {
564
+ const combination = expanded[index]!;
565
+ if (combination.source !== source) return;
566
+
567
+ if (type.localisation === 'original' && !entry.isDefaultLocale) return;
568
+ if (type.versioning === 'global' && !entry.isDefault) return;
569
+
570
+ const path = artefactFor(declared, entry);
571
+ if (!path) return;
572
+
573
+ bases.push({ entry, path, remote: Boolean(combination.effective.repo) });
574
+ });
575
+
576
+ return bases;
577
+ }
578
+
579
+ /**
580
+ * The artefact one entry reads, or nothing where it reads none.
581
+ *
582
+ * The default language always reads the declared `path`; every other language
583
+ * reads what `locales` names for it, and builds no collection at all when that
584
+ * map is silent — see `DuxtGeneratedSection.locales` for why the silence is the
585
+ * useful answer rather than a gap to paper over.
586
+ */
587
+ function artefactFor(
588
+ declared: DuxtGeneratedSection,
589
+ entry: DuxtResolvedSource
590
+ ): string | undefined {
591
+ if (entry.isDefaultLocale || !entry.locale) return declared.path;
592
+
593
+ const locales = declared.locales ?? {};
594
+
595
+ // The language alone answers for a region, exactly as the locale FILES do:
596
+ // one `pt` artefact serves `pt-PT` and `pt-BR`.
597
+ return locales[entry.locale] ?? locales[entry.locale.split('-')[0]!];
598
+ }
599
+
600
+ /**
601
+ * The ref an entry was read at, for the half of the build that downloads it.
602
+ *
603
+ * `DuxtResolvedSource` keeps a ref by NAME and a kind beside it, because that
604
+ * is what a link back to the file needs; Content wants the pair as a `branch`
605
+ * or a `tag` again. Rebuilding it here keeps that translation in one place.
606
+ */
607
+ export function generatedSectionRef(
608
+ entry: DuxtResolvedSource
609
+ ): DuxtRef | undefined {
610
+ if (!entry.ref) return undefined;
611
+
612
+ return entry.refKind === 'tag' ? { tag: entry.ref } : { branch: entry.ref };
613
+ }
614
+
615
+ /**
616
+ * The artefact, as its type reads it.
617
+ *
618
+ * Here rather than beside the file reading, so the SEVERITY POLICY is testable:
619
+ * whether a finding fails the build or warns is one of this feature's stated
620
+ * decisions, and it was decided per source kind rather than per finding. The
621
+ * reading itself stays in `sections.ts`, which is the half that touches disk.
622
+ *
623
+ * A type that produces nothing out of a file that exists is the same finding as
624
+ * a file that is not there, and carries the same severity — the section would
625
+ * otherwise be an empty collection, which is a 404 on every URL it claims and
626
+ * nothing said about why.
627
+ */
628
+ export function sectionPages(
629
+ entry: DuxtResolvedSource,
630
+ type: DuxtSectionType,
631
+ artefact: string
632
+ ): DuxtSectionPage[] {
633
+ const warnings: string[] = [];
634
+
635
+ const pages = type.parse(artefact, {
636
+ label: entry.generated!.label,
637
+ prefix: entry.prefix,
638
+ options: entry.generated!.options ?? {},
639
+ warn: (message) => {
640
+ // Deduplicated here rather than in each type: one unresolvable `$ref` is
641
+ // reached from every operation that uses it, and a report that says the
642
+ // same sentence forty times is a report nobody reads to the end.
643
+ if (!warnings.includes(message)) warnings.push(message);
644
+ }
645
+ });
646
+
647
+ entry.generated!.report = { pages: pages.length, warnings };
648
+
649
+ // An EMPTY section is not reported here any more, in either direction beyond
650
+ // the local throw. The report above says `pages: 0`, and `validate-report.ts`
651
+ // turns that into a finding at the severity the source's kind asks for —
652
+ // which is where every other finding in this layer already lives. A
653
+ // `console.warn` beside it was a second reporting channel that scrolled away.
654
+ if (!pages.length && !entry.generated!.remote) {
655
+ throw new Error(
656
+ `duxt: ${entry.path} holds nothing the "${entry.generated!.type}" type ` +
657
+ `can read, so the section "${entry.generated!.label}" has no pages.`
658
+ );
659
+ }
660
+
661
+ return pages;
662
+ }
663
+
664
+ /**
665
+ * A declared artefact that is not there.
666
+ *
667
+ * The severity is not uniform, and follows the rule `modules/validate.ts`
668
+ * already states. A LOCAL source is the site's own configuration, so a path
669
+ * that does not exist is a mistake in it and fails the build. A REMOTE one may
670
+ * legitimately not have had the file at an older tag — a remote source can go
671
+ * stale between releases without that being this build's fault — so it is
672
+ * recorded as a finding, and the section is simply not built.
673
+ *
674
+ * Returns the pages a caller should carry on with, which for the recorded case
675
+ * is none — so the two severities read as one expression at both call sites.
676
+ */
677
+ export function missingSectionArtefact(
678
+ entry: DuxtResolvedSource,
679
+ file: string
680
+ ): DuxtSectionPage[] {
681
+ entry.generated!.report = { pages: 0, warnings: [], missing: true };
682
+
683
+ // Remote: recorded, not printed. `validate-report.ts` reads the report and
684
+ // says it once, in the same list as every other finding — see `sectionPages`.
685
+ if (entry.generated!.remote) return [];
686
+
687
+ throw new Error(
688
+ `duxt: the generated section "${entry.generated!.label}" declares ` +
689
+ `${entry.path}, which this repository does not have (looked in ${file}). ` +
690
+ "A generated section resolves its path against the source's own root."
691
+ );
692
+ }
693
+
694
+ /**
695
+ * The whole manifest: the documentation, then whatever it generates beside it.
696
+ *
697
+ * `duxtSourceManifest` deliberately stays the documentation half alone —
698
+ * `duxtSources` walks it index-for-index against `expandSources`, and an entry
699
+ * appended there would put the two out of step. Everything that reads the
700
+ * manifest to serve a site reads this one.
701
+ */
702
+ export function duxtManifest(
703
+ sources: DuxtSource[],
704
+ options: DuxtSourcesOptions = {},
705
+ types: DuxtSectionTypes = duxtSectionTypes()
706
+ ): DuxtResolvedSource[] {
707
+ return [
708
+ ...resolveSources(sources, options),
709
+ ...resolveGeneratedSections(sources, options, types)
710
+ ];
711
+ }