@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,494 @@
1
+ /**
2
+ * The checks themselves, over data rather than over a build.
3
+ *
4
+ * Split from `modules/validate.ts` for the reason `sources-resolve.ts` is split
5
+ * from `sources.ts`: the module half imports `node:url`, `@nuxt/schema` and the
6
+ * jiti-backed config reader, none of which belong in a bundle. This half is
7
+ * plain data in, findings out — so a test can run it, and so the devtools panel
8
+ * can run the SAME checks against the pages the site is actually serving,
9
+ * rather than reprinting a build log that has already scrolled away.
10
+ */
11
+ import { reservedSegments } from './sources-resolve';
12
+ import { packageCommandIssues } from './app/utils/package-command';
13
+
14
+ export interface PageRecord {
15
+ collection: string;
16
+ path: string;
17
+ file: string;
18
+ title?: string;
19
+ description?: string;
20
+ anchors: Set<string>;
21
+ links: { href: string }[];
22
+ /**
23
+ * When the file last changed, as `git-meta` read it off the history.
24
+ *
25
+ * Absent for a source that did not ask for its history, and for one whose
26
+ * clone could not be deepened — which is why the staleness half of the
27
+ * translation report is skipped rather than guessed when it is missing.
28
+ */
29
+ lastUpdated?: string;
30
+ /**
31
+ * The `command` of every `::package-managers` block on the page.
32
+ *
33
+ * Read off the parsed body rather than the source, because that is where the
34
+ * prop has already been resolved — see `walk`.
35
+ */
36
+ commands?: string[];
37
+ }
38
+
39
+ /** The manifest, as much of it as the checks read. */
40
+ export interface SourceRecord {
41
+ collection: string;
42
+ prefix: string;
43
+ locale?: string;
44
+ isDefaultLocale?: boolean;
45
+ /** The artefact a generated section was read from, for its findings. */
46
+ path?: string;
47
+ /** Where that artefact lives, for a finding that has to name it. */
48
+ repository?: string;
49
+ repositoryUrl?: string;
50
+ ref?: string;
51
+ /**
52
+ * Set when this collection is a GENERATED SECTION rather than a docs tree.
53
+ *
54
+ * Checked DIFFERENTLY rather than skipped. An empty section is a finding
55
+ * about the artefact and not about a docs folder, so it is worded and graded
56
+ * from `report` instead of from the collection rule; a page split out of a
57
+ * changelog has no field a `description` could come from, so it is not asked
58
+ * for one; and what the type could not read is a finding no other check could
59
+ * ever have produced.
60
+ */
61
+ generated?: GeneratedRecord;
62
+ }
63
+
64
+ /** What a generated section carries into the checks. */
65
+ export interface GeneratedRecord {
66
+ type: string;
67
+ label: string;
68
+ remote?: boolean;
69
+ report?: {
70
+ pages: number;
71
+ warnings: string[];
72
+ missing?: boolean;
73
+ };
74
+ }
75
+
76
+ /** What one pass over a parsed MDC body picks up. */
77
+ export interface Collected {
78
+ anchors: Set<string>;
79
+ links: { href: string }[];
80
+ commands: string[];
81
+ }
82
+
83
+ /**
84
+ * Collect anchor ids, internal links and command blocks out of a parsed MDC
85
+ * body.
86
+ *
87
+ * ONE ACCUMULATOR rather than a parameter per kind: the third thing to collect
88
+ * is the point at which a positional list stops reading as a signature and
89
+ * starts reading as an argument order to get wrong.
90
+ */
91
+ export function walk(node: unknown, into: Collected): void {
92
+ if (Array.isArray(node)) {
93
+ const [tag, props] = node as [unknown, Record<string, unknown> | undefined];
94
+
95
+ if (typeof tag === 'string' && props && typeof props === 'object') {
96
+ if (typeof props.id === 'string') into.anchors.add(props.id);
97
+
98
+ if (tag === 'a' && typeof props.href === 'string') {
99
+ into.links.push({ href: props.href });
100
+ }
101
+
102
+ if (tag === 'package-managers' && typeof props.command === 'string') {
103
+ into.commands.push(props.command);
104
+ }
105
+ }
106
+
107
+ for (const child of node) walk(child, into);
108
+ return;
109
+ }
110
+
111
+ if (node && typeof node === 'object') {
112
+ for (const value of Object.values(node as Record<string, unknown>)) {
113
+ walk(value, into);
114
+ }
115
+ }
116
+ }
117
+
118
+ export function report(
119
+ sources: SourceRecord[],
120
+ pages: PageRecord[]
121
+ ): { errors: string[]; warnings: string[]; notes: string[] } {
122
+ const errors: string[] = [];
123
+ const warnings: string[] = [];
124
+
125
+ const byCollection = new Map<string, PageRecord[]>();
126
+ for (const page of pages) {
127
+ const list = byCollection.get(page.collection) ?? [];
128
+ list.push(page);
129
+ byCollection.set(page.collection, list);
130
+ }
131
+
132
+ // 1. A collection with nothing in it. The symptom is an empty sidebar and a
133
+ // 404 on every page of one version — never a message.
134
+ //
135
+ // A GENERATED SECTION answers the same question from its own artefact, at
136
+ // its own severity and in its own words — see `sectionFindings`. It used
137
+ // to be skipped here and reported by a `console.warn` where the file is
138
+ // read, which put half of this layer's findings in a channel that has
139
+ // scrolled away by the time anyone looks at the other half.
140
+ for (const source of sources) {
141
+ if (source.generated) {
142
+ warnings.push(
143
+ ...sectionFindings(source, byCollection.get(source.collection)?.length)
144
+ );
145
+ continue;
146
+ }
147
+
148
+ if (!byCollection.get(source.collection)?.length) {
149
+ errors.push(
150
+ `collection "${source.collection}" (serving "${source.prefix || '/'}") ` +
151
+ "contains no pages. Check the source's `path` and `refs`."
152
+ );
153
+ }
154
+ }
155
+
156
+ // 2. A docs folder named like a repository or a version segment. The prefix
157
+ // wins, so the folder is simply unreachable.
158
+ const reserved = reservedSegments(sources as never);
159
+
160
+ for (const source of sources) {
161
+ const claimed = reserved.get(source.collection);
162
+ if (!claimed?.size) continue;
163
+
164
+ for (const page of byCollection.get(source.collection) ?? []) {
165
+ const rest = source.prefix
166
+ ? page.path.slice(source.prefix.length)
167
+ : page.path;
168
+ const segment = rest.split('/')[1];
169
+
170
+ if (segment && claimed.has(segment)) {
171
+ errors.push(
172
+ `"${page.file}" sits in a folder called "${segment}", which is ` +
173
+ `already a repository or version segment under "${source.prefix || '/'}". ` +
174
+ 'Rename the folder, or give the source a `slug`.'
175
+ );
176
+ }
177
+ }
178
+ }
179
+
180
+ // 3. Frontmatter. Neither field breaks a page; both quietly degrade the
181
+ // table of contents, the OG image and llms.txt.
182
+ //
183
+ // A generated page is asked for a title and not for a description: the
184
+ // title is the type's to produce and a missing one is a bug in the parser,
185
+ // where a description would have to be invented — a changelog entry has no
186
+ // field that could carry one.
187
+ const isGenerated = new Set(
188
+ sources.filter((source) => source.generated).map((s) => s.collection)
189
+ );
190
+
191
+ for (const page of pages) {
192
+ const missing = [
193
+ !page.title && 'title',
194
+ !page.description && !isGenerated.has(page.collection) && 'description'
195
+ ].filter(Boolean);
196
+
197
+ if (missing.length) {
198
+ warnings.push(`"${page.file}" has no ${missing.join(' and no ')}.`);
199
+ }
200
+ }
201
+
202
+ // 4. Links. Internal ones only — an external URL is not this build's to
203
+ // verify, and checking it would put the network in the build.
204
+ //
205
+ // Resolved exactly as `ProseA` resolves them at render time: an absolute
206
+ // path written in a page is relative to that page's OWN source, so it is
207
+ // tried under the source's prefix first and bare second. Checking only the
208
+ // bare form reports every correct link on a prefixed site.
209
+ //
210
+ // PER LANGUAGE, and that is not a refinement. Every language of one source
211
+ // serves IDENTICAL content paths — the locale lives in front of the URL,
212
+ // not in the content tree — so a map keyed by path alone holds one page per
213
+ // path and whichever language was parsed last wins. Anchors are derived
214
+ // from heading TEXT, so that map would check a German link against English
215
+ // headings and report every correct anchor on a translated site. A link is
216
+ // therefore resolved in the linking page's OWN collection first, and in the
217
+ // untranslated original second — which is where the site itself falls back
218
+ // when a language does not carry the page.
219
+ const known = new Map<string, PageRecord[]>();
220
+ for (const page of pages) {
221
+ const list = known.get(page.path) ?? [];
222
+ list.push(page);
223
+ known.set(page.path, list);
224
+ }
225
+
226
+ const prefixOf = new Map(
227
+ sources.map((source) => [source.collection, source.prefix])
228
+ );
229
+ const originals = new Set(
230
+ sources
231
+ .filter((source) => !source.locale || source.isDefaultLocale)
232
+ .map((source) => source.collection)
233
+ );
234
+
235
+ const resolve = (path: string, from: PageRecord) => {
236
+ const candidates = known.get(stripTrailingSlash(path));
237
+ if (!candidates) return undefined;
238
+
239
+ return (
240
+ candidates.find((page) => page.collection === from.collection) ??
241
+ candidates.find((page) => originals.has(page.collection)) ??
242
+ candidates[0]
243
+ );
244
+ };
245
+
246
+ /** A percent-encoded fragment, or the fragment itself where it is not one. */
247
+ const decodeAnchor = (fragment: string) => {
248
+ try {
249
+ return decodeURIComponent(fragment);
250
+ } catch {
251
+ // A stray `%` is a link nobody can follow either way, and the anchor
252
+ // check is not the place to say so.
253
+ return fragment;
254
+ }
255
+ };
256
+
257
+ for (const page of pages) {
258
+ const prefix = prefixOf.get(page.collection) ?? '';
259
+
260
+ for (const link of page.links) {
261
+ const { href } = link;
262
+ if (!href.startsWith('/') && !href.startsWith('#')) continue;
263
+
264
+ const [target, fragment] = href.split('#');
265
+
266
+ // DECODED, because the two halves are written in different alphabets. A
267
+ // heading's id is the text as it stands — `icônes` — while the link
268
+ // arrives percent-encoded, so comparing them as written reported every
269
+ // accented anchor on the site as missing and told the author to point at
270
+ // a heading that was already there.
271
+ const anchor = fragment && decodeAnchor(fragment);
272
+ const destination = target
273
+ ? (resolve(`${prefix}${target}`, page) ?? resolve(target, page))
274
+ : page;
275
+
276
+ if (target && !destination) {
277
+ warnings.push(
278
+ `"${page.file}" links to "${href}", which no page serves.`
279
+ );
280
+ continue;
281
+ }
282
+
283
+ if (anchor && destination && !destination.anchors.has(anchor)) {
284
+ warnings.push(
285
+ `"${page.file}" links to "${href}", but that page has no "${anchor}" heading.`
286
+ );
287
+ }
288
+ }
289
+ }
290
+
291
+ commandWarnings(pages, warnings);
292
+
293
+ return { errors, warnings, notes: translationNotes(sources, byCollection) };
294
+ }
295
+
296
+ /**
297
+ * What a `::package-managers` block asks for that a manager cannot say.
298
+ *
299
+ * NEITHER FAULT FAILS ANYTHING, and both are otherwise invisible. A manager with
300
+ * no equivalent silently loses its tab, so a page written as
301
+ * `command="outdated"` shows three tabs where the site offers four and nothing
302
+ * anywhere says why. An unknown verb is printed as written for all four, which is
303
+ * usually right and occasionally a typo — `outdatd` renders four plausible
304
+ * commands that none of them accept.
305
+ *
306
+ * Warnings rather than errors, because both are legitimate: a command only three
307
+ * managers have is a fine thing to document, and this table will always be behind
308
+ * some manager's newest subcommand.
309
+ */
310
+ function commandWarnings(pages: PageRecord[], warnings: string[]): void {
311
+ for (const page of pages) {
312
+ for (const command of page.commands ?? []) {
313
+ const { unknownVerb, unavailable } = packageCommandIssues(command);
314
+
315
+ if (unknownVerb) {
316
+ warnings.push(
317
+ `"${page.file}": "${command}" starts with "${unknownVerb}", which duxt ` +
318
+ 'does not translate — every manager shows it as written.'
319
+ );
320
+ }
321
+
322
+ if (unavailable.length) {
323
+ warnings.push(
324
+ `"${page.file}": "${command}" has no equivalent in ` +
325
+ `${unavailable.join(' and ')}, so that tab is not drawn.`
326
+ );
327
+ }
328
+ }
329
+ }
330
+ }
331
+
332
+ const stripTrailingSlash = (path: string) =>
333
+ path.length > 1 ? path.replace(/\/+$/, '') : path;
334
+
335
+ /** The repository and ref an artefact was looked for in. */
336
+ const sectionOrigin = (source: SourceRecord) =>
337
+ `${source.repository ?? source.repositoryUrl ?? 'the source'}${
338
+ source.ref ? `@${source.ref}` : ''
339
+ }`;
340
+
341
+ /**
342
+ * What reading one generated section's artefact had to say.
343
+ *
344
+ * WARNINGS, all of them, and the severity is not a compromise. Everything a
345
+ * local artefact can get wrong — a path that is not there, a file the type
346
+ * cannot read at all, an option it does not know — has already thrown while the
347
+ * config was loading, long before this report exists: the site's own
348
+ * configuration is a mistake the build must not carry. What is left to report
349
+ * here is therefore either a REMOTE artefact, which may legitimately have gone
350
+ * stale between releases and is not this build's to reject, or a page that
351
+ * rendered with something missing from it. Neither is an error.
352
+ *
353
+ * `report` absent is not "nothing to report" — it is "nobody read the artefact
354
+ * on this side". A remote one is read where Content put the checkout, which is
355
+ * a directory only the config loader knows, so the manifest a module holds
356
+ * carries no report for it at all. The page count answers for that case, and
357
+ * says less because less is known: a section serving nothing is still visible
358
+ * from the collection, only the reason for it is not.
359
+ */
360
+ function sectionFindings(
361
+ source: SourceRecord,
362
+ pages: number | undefined
363
+ ): string[] {
364
+ const meta = source.generated!;
365
+ const report = meta.report;
366
+ const where = `the generated section "${meta.label}"`;
367
+ const findings: string[] = [];
368
+
369
+ // Quoted only where the file is actually in this checkout. The Checks panel
370
+ // turns the first quoted `.md` in a finding into an editor link, and a remote
371
+ // `CHANGELOG.md` resolved against the local root is a link to nothing.
372
+ const artefact = meta.remote ? source.path : `"${source.path}"`;
373
+
374
+ if (report?.missing) {
375
+ findings.push(
376
+ `${where} declares ${artefact}, which ${sectionOrigin(source)} does not ` +
377
+ 'have. The section is not built.'
378
+ );
379
+ } else if (report && !report.pages) {
380
+ findings.push(
381
+ `${where} holds nothing the "${meta.type}" type can read in ` +
382
+ `${artefact}, so it has no pages.`
383
+ );
384
+ } else if (!pages) {
385
+ findings.push(
386
+ report
387
+ ? `${where} produced ${report.pages} pages out of ${artefact}, but the ` +
388
+ `collection "${source.collection}" serves none — Content dropped it.`
389
+ : `${where} produced no pages out of ${artefact} in ` +
390
+ `${sectionOrigin(source)}. Either the artefact is not there at that ` +
391
+ `ref, or it holds nothing the "${meta.type}" type can read.`
392
+ );
393
+ }
394
+
395
+ // Prefixed with the section, never with a page: a generated page's name is
396
+ // this layer's own invention, so pointing at one would send a reader to a
397
+ // file that does not exist. The artefact is the thing to open.
398
+ for (const warning of report?.warnings ?? []) {
399
+ findings.push(`${where} (${artefact}): ${warning}`);
400
+ }
401
+
402
+ return findings;
403
+ }
404
+
405
+ /**
406
+ * What each language carries, and what has stood still.
407
+ *
408
+ * Notes rather than warnings: an untranslated page is not a defect, it is a
409
+ * state — and one nothing else in this layer ever says out loud. The layer
410
+ * knows, per page, which languages carry it and when each file last changed;
411
+ * without a report that knowledge sits in the manifest and reaches nobody.
412
+ *
413
+ * OpenCode is the cautionary case, and the reason this is a build report rather
414
+ * than a dashboard: seventeen languages, an agent that kept them in sync, the
415
+ * workflow switched off, and nothing anywhere saying the translations had
416
+ * stopped moving. A line per language in every build is what makes that
417
+ * visible on the day it happens instead of a year later.
418
+ *
419
+ * Two findings, and the second is skipped rather than guessed when the data is
420
+ * missing: a page the original has and a translation does not, and a
421
+ * translation whose file has not moved since the original's did. `lastUpdated`
422
+ * comes from `git-meta`, which a source without `history: true` never gets.
423
+ */
424
+ function translationNotes(
425
+ sources: SourceRecord[],
426
+ byCollection: Map<string, PageRecord[]>
427
+ ): string[] {
428
+ const notes: string[] = [];
429
+
430
+ // Grouped by PREFIX, because that is what a translation shares with its
431
+ // original — the locale is never part of a content path.
432
+ const byPrefix = new Map<string, SourceRecord[]>();
433
+ for (const source of sources) {
434
+ const list = byPrefix.get(source.prefix) ?? [];
435
+ list.push(source);
436
+ byPrefix.set(source.prefix, list);
437
+ }
438
+
439
+ for (const [prefix, group] of byPrefix) {
440
+ const original = group.find(
441
+ (source) => !source.locale || source.isDefaultLocale
442
+ );
443
+ const translations = group.filter((source) => source !== original);
444
+
445
+ if (!original || !translations.length) continue;
446
+
447
+ const base = byCollection.get(original.collection) ?? [];
448
+ const dates = new Map(
449
+ base.map((page) => [page.path, page.lastUpdated] as const)
450
+ );
451
+
452
+ for (const translation of translations) {
453
+ const pages = byCollection.get(translation.collection) ?? [];
454
+ const have = new Map(pages.map((page) => [page.path, page] as const));
455
+
456
+ const missing = base.filter((page) => !have.has(page.path));
457
+
458
+ const stale = pages.filter((page) => {
459
+ const source = dates.get(page.path);
460
+ return (
461
+ source &&
462
+ page.lastUpdated &&
463
+ Date.parse(page.lastUpdated) < Date.parse(source)
464
+ );
465
+ });
466
+
467
+ const where = prefix ? ` under "${prefix}"` : '';
468
+
469
+ notes.push(
470
+ `${translation.locale}${where}: ${have.size}/${base.length} pages` +
471
+ (stale.length ? `, ${stale.length} behind the original` : '')
472
+ );
473
+
474
+ // Named, not just counted — a figure says a translation has drifted and
475
+ // a file name says where to start. Capped, because a language nobody has
476
+ // begun would otherwise print the whole tree.
477
+ for (const page of [...missing, ...stale].slice(0, NAMED)) {
478
+ notes.push(
479
+ missing.includes(page)
480
+ ? ` "${page.file}" has no ${translation.locale} translation.`
481
+ : ` "${page.file}" has not moved since the original changed.`
482
+ );
483
+ }
484
+
485
+ const rest = missing.length + stale.length - NAMED;
486
+ if (rest > 0) notes.push(` … and ${rest} more.`);
487
+ }
488
+ }
489
+
490
+ return notes;
491
+ }
492
+
493
+ /** How many files a language names before the report starts counting instead. */
494
+ const NAMED = 10;