@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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/app/app.config.ts +13 -0
- package/app/app.vue +122 -0
- package/app/assets/css/duxt.css +555 -0
- package/app/assets/css/typeset.css +498 -0
- package/app/components/DuxtBrand.vue +53 -0
- package/app/components/DuxtBreadcrumb.vue +37 -0
- package/app/components/DuxtCodeBlock.vue +143 -0
- package/app/components/DuxtCopyPage.vue +117 -0
- package/app/components/DuxtFooter.vue +86 -0
- package/app/components/DuxtHeader.vue +496 -0
- package/app/components/DuxtJsonEditor.vue +359 -0
- package/app/components/DuxtLandingProse.vue +90 -0
- package/app/components/DuxtLandingShowcase.vue +295 -0
- package/app/components/DuxtLiveWindow.vue +343 -0
- package/app/components/DuxtLocale.vue +86 -0
- package/app/components/DuxtMetaList.vue +76 -0
- package/app/components/DuxtNavigation.vue +94 -0
- package/app/components/DuxtNavigationLink.vue +41 -0
- package/app/components/DuxtOpenApiCallbacks.vue +92 -0
- package/app/components/DuxtOpenApiClient.vue +1339 -0
- package/app/components/DuxtOpenApiMedia.vue +108 -0
- package/app/components/DuxtOpenApiMethod.vue +59 -0
- package/app/components/DuxtOpenApiParameters.vue +114 -0
- package/app/components/DuxtOpenApiResponses.vue +109 -0
- package/app/components/DuxtOpenApiSchema.vue +365 -0
- package/app/components/DuxtOpenApiSecurity.vue +115 -0
- package/app/components/DuxtPageFeedback.vue +56 -0
- package/app/components/DuxtPageInfo.vue +194 -0
- package/app/components/DuxtPageNav.vue +93 -0
- package/app/components/DuxtPreviewSkeleton.vue +262 -0
- package/app/components/DuxtProgress.vue +45 -0
- package/app/components/DuxtSearch.vue +271 -0
- package/app/components/DuxtSections.vue +65 -0
- package/app/components/DuxtShortcuts.vue +46 -0
- package/app/components/DuxtSkipLink.vue +34 -0
- package/app/components/DuxtToc.vue +86 -0
- package/app/components/DuxtTranslationBanner.vue +64 -0
- package/app/components/DuxtVersion.vue +168 -0
- package/app/components/DuxtVersionBanner.vue +109 -0
- package/app/components/OgImage/Duxt.satori.vue +82 -0
- package/app/components/content/Accordion.vue +37 -0
- package/app/components/content/AccordionItem.vue +46 -0
- package/app/components/content/Callout.vue +58 -0
- package/app/components/content/ChangelogGroup.vue +91 -0
- package/app/components/content/ChangelogReleases.vue +520 -0
- package/app/components/content/CodeGroup.vue +183 -0
- package/app/components/content/DevtoolsPanel.vue +107 -0
- package/app/components/content/Field.vue +50 -0
- package/app/components/content/FieldGroup.vue +8 -0
- package/app/components/content/FileTree.vue +92 -0
- package/app/components/content/Mermaid.vue +70 -0
- package/app/components/content/OpenApiOperation.vue +185 -0
- package/app/components/content/OpenApiOperations.vue +72 -0
- package/app/components/content/OpenApiOverview.vue +302 -0
- package/app/components/content/PackageManagers.vue +187 -0
- package/app/components/content/PageCards.vue +48 -0
- package/app/components/content/Partial.vue +65 -0
- package/app/components/content/Preview.vue +173 -0
- package/app/components/content/ProseA.vue +55 -0
- package/app/components/content/ProseH2.vue +53 -0
- package/app/components/content/ProseH3.vue +53 -0
- package/app/components/content/ProseH4.vue +53 -0
- package/app/components/content/ProseImg.vue +87 -0
- package/app/components/content/ProsePre.vue +58 -0
- package/app/components/content/ProseTable.vue +11 -0
- package/app/components/content/Since.vue +21 -0
- package/app/components/content/Steps.vue +20 -0
- package/app/components/content/Tab.vue +23 -0
- package/app/components/content/TabGroup.vue +58 -0
- package/app/components/ui/alert/Alert.vue +21 -0
- package/app/components/ui/alert/AlertDescription.vue +22 -0
- package/app/components/ui/alert/AlertTitle.vue +22 -0
- package/app/components/ui/alert/index.ts +24 -0
- package/app/components/ui/badge/Badge.vue +28 -0
- package/app/components/ui/badge/index.ts +52 -0
- package/app/components/ui/breadcrumb/Breadcrumb.vue +13 -0
- package/app/components/ui/breadcrumb/BreadcrumbEllipsis.vue +22 -0
- package/app/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/app/components/ui/breadcrumb/BreadcrumbLink.vue +24 -0
- package/app/components/ui/breadcrumb/BreadcrumbList.vue +22 -0
- package/app/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/app/components/ui/breadcrumb/BreadcrumbSeparator.vue +21 -0
- package/app/components/ui/breadcrumb/index.ts +7 -0
- package/app/components/ui/button/Button.vue +31 -0
- package/app/components/ui/button/index.ts +39 -0
- package/app/components/ui/card/Card.vue +22 -0
- package/app/components/ui/card/CardAction.vue +22 -0
- package/app/components/ui/card/CardContent.vue +14 -0
- package/app/components/ui/card/CardDescription.vue +17 -0
- package/app/components/ui/card/CardFooter.vue +17 -0
- package/app/components/ui/card/CardHeader.vue +22 -0
- package/app/components/ui/card/CardTitle.vue +17 -0
- package/app/components/ui/card/index.ts +7 -0
- package/app/components/ui/collapsible/Collapsible.vue +19 -0
- package/app/components/ui/collapsible/CollapsibleContent.vue +12 -0
- package/app/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
- package/app/components/ui/collapsible/index.ts +3 -0
- package/app/components/ui/command/Command.vue +98 -0
- package/app/components/ui/command/CommandDialog.vue +42 -0
- package/app/components/ui/command/CommandEmpty.vue +31 -0
- package/app/components/ui/command/CommandGroup.vue +52 -0
- package/app/components/ui/command/CommandInput.vue +45 -0
- package/app/components/ui/command/CommandItem.vue +85 -0
- package/app/components/ui/command/CommandList.vue +32 -0
- package/app/components/ui/command/CommandSeparator.vue +23 -0
- package/app/components/ui/command/CommandShortcut.vue +19 -0
- package/app/components/ui/command/index.ts +29 -0
- package/app/components/ui/dialog/Dialog.vue +15 -0
- package/app/components/ui/dialog/DialogClose.vue +12 -0
- package/app/components/ui/dialog/DialogContent.vue +61 -0
- package/app/components/ui/dialog/DialogDescription.vue +25 -0
- package/app/components/ui/dialog/DialogFooter.vue +30 -0
- package/app/components/ui/dialog/DialogHeader.vue +17 -0
- package/app/components/ui/dialog/DialogOverlay.vue +28 -0
- package/app/components/ui/dialog/DialogScrollContent.vue +65 -0
- package/app/components/ui/dialog/DialogTitle.vue +25 -0
- package/app/components/ui/dialog/DialogTrigger.vue +12 -0
- package/app/components/ui/dialog/index.ts +10 -0
- package/app/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +47 -0
- package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +47 -0
- package/app/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
- package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
- package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +27 -0
- package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +48 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +19 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSubContent.vue +34 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +34 -0
- package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/app/components/ui/dropdown-menu/index.ts +16 -0
- package/app/components/ui/input/Input.vue +35 -0
- package/app/components/ui/input/index.ts +1 -0
- package/app/components/ui/navigation-menu/NavigationMenu.vue +42 -0
- package/app/components/ui/navigation-menu/NavigationMenuContent.vue +35 -0
- package/app/components/ui/navigation-menu/NavigationMenuIndicator.vue +32 -0
- package/app/components/ui/navigation-menu/NavigationMenuItem.vue +23 -0
- package/app/components/ui/navigation-menu/NavigationMenuLink.vue +30 -0
- package/app/components/ui/navigation-menu/NavigationMenuList.vue +30 -0
- package/app/components/ui/navigation-menu/NavigationMenuTrigger.vue +31 -0
- package/app/components/ui/navigation-menu/NavigationMenuViewport.vue +30 -0
- package/app/components/ui/navigation-menu/index.ts +14 -0
- package/app/components/ui/scroll-area/ScrollArea.vue +31 -0
- package/app/components/ui/scroll-area/ScrollBar.vue +38 -0
- package/app/components/ui/scroll-area/index.ts +2 -0
- package/app/components/ui/select/Select.vue +15 -0
- package/app/components/ui/select/SelectContent.vue +59 -0
- package/app/components/ui/select/SelectGroup.vue +19 -0
- package/app/components/ui/select/SelectItem.vue +42 -0
- package/app/components/ui/select/SelectItemText.vue +12 -0
- package/app/components/ui/select/SelectLabel.vue +16 -0
- package/app/components/ui/select/SelectScrollDownButton.vue +28 -0
- package/app/components/ui/select/SelectScrollUpButton.vue +28 -0
- package/app/components/ui/select/SelectSeparator.vue +20 -0
- package/app/components/ui/select/SelectTrigger.vue +32 -0
- package/app/components/ui/select/SelectValue.vue +12 -0
- package/app/components/ui/select/index.ts +11 -0
- package/app/components/ui/separator/Separator.vue +30 -0
- package/app/components/ui/separator/index.ts +1 -0
- package/app/components/ui/sheet/Sheet.vue +15 -0
- package/app/components/ui/sheet/SheetClose.vue +12 -0
- package/app/components/ui/sheet/SheetContent.vue +68 -0
- package/app/components/ui/sheet/SheetDescription.vue +23 -0
- package/app/components/ui/sheet/SheetFooter.vue +15 -0
- package/app/components/ui/sheet/SheetHeader.vue +15 -0
- package/app/components/ui/sheet/SheetOverlay.vue +28 -0
- package/app/components/ui/sheet/SheetTitle.vue +23 -0
- package/app/components/ui/sheet/SheetTrigger.vue +12 -0
- package/app/components/ui/sheet/index.ts +8 -0
- package/app/components/ui/sidebar/Sidebar.vue +110 -0
- package/app/components/ui/sidebar/SidebarContent.vue +23 -0
- package/app/components/ui/sidebar/SidebarFooter.vue +18 -0
- package/app/components/ui/sidebar/SidebarGroup.vue +18 -0
- package/app/components/ui/sidebar/SidebarGroupAction.vue +31 -0
- package/app/components/ui/sidebar/SidebarGroupContent.vue +18 -0
- package/app/components/ui/sidebar/SidebarGroupLabel.vue +30 -0
- package/app/components/ui/sidebar/SidebarHeader.vue +18 -0
- package/app/components/ui/sidebar/SidebarInput.vue +19 -0
- package/app/components/ui/sidebar/SidebarInset.vue +23 -0
- package/app/components/ui/sidebar/SidebarMenu.vue +18 -0
- package/app/components/ui/sidebar/SidebarMenuAction.vue +42 -0
- package/app/components/ui/sidebar/SidebarMenuBadge.vue +28 -0
- package/app/components/ui/sidebar/SidebarMenuButton.vue +60 -0
- package/app/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
- package/app/components/ui/sidebar/SidebarMenuItem.vue +18 -0
- package/app/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
- package/app/components/ui/sidebar/SidebarMenuSub.vue +24 -0
- package/app/components/ui/sidebar/SidebarMenuSubButton.vue +43 -0
- package/app/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- package/app/components/ui/sidebar/SidebarProvider.vue +109 -0
- package/app/components/ui/sidebar/SidebarRail.vue +35 -0
- package/app/components/ui/sidebar/SidebarSeparator.vue +19 -0
- package/app/components/ui/sidebar/SidebarTrigger.vue +26 -0
- package/app/components/ui/sidebar/index.ts +62 -0
- package/app/components/ui/sidebar/utils.ts +19 -0
- package/app/components/ui/skeleton/Skeleton.vue +17 -0
- package/app/components/ui/skeleton/index.ts +1 -0
- package/app/components/ui/sonner/Sonner.vue +67 -0
- package/app/components/ui/sonner/index.ts +1 -0
- package/app/components/ui/tabs/Tabs.vue +26 -0
- package/app/components/ui/tabs/TabsContent.vue +23 -0
- package/app/components/ui/tabs/TabsList.vue +28 -0
- package/app/components/ui/tabs/TabsTrigger.vue +30 -0
- package/app/components/ui/tabs/index.ts +4 -0
- package/app/components/ui/textarea/Textarea.vue +32 -0
- package/app/components/ui/textarea/index.ts +1 -0
- package/app/components/ui/tooltip/Tooltip.vue +15 -0
- package/app/components/ui/tooltip/TooltipContent.vue +49 -0
- package/app/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/app/components/ui/tooltip/TooltipTrigger.vue +12 -0
- package/app/components/ui/tooltip/index.ts +4 -0
- package/app/composables/useActiveHeading.ts +90 -0
- package/app/composables/useDuxtAnimatedHeight.ts +155 -0
- package/app/composables/useDuxtBreadcrumb.ts +60 -0
- package/app/composables/useDuxtChoice.ts +28 -0
- package/app/composables/useDuxtCollection.ts +89 -0
- package/app/composables/useDuxtConfig.ts +47 -0
- package/app/composables/useDuxtNavigation.ts +87 -0
- package/app/composables/useDuxtPageFocus.ts +30 -0
- package/app/composables/useDuxtPath.ts +42 -0
- package/app/composables/useDuxtSearch.ts +208 -0
- package/app/composables/useDuxtSection.ts +44 -0
- package/app/composables/useDuxtSectionRow.ts +49 -0
- package/app/composables/useDuxtShortcuts.ts +58 -0
- package/app/composables/useDuxtSiteUrl.ts +27 -0
- package/app/composables/useDuxtToast.ts +25 -0
- package/app/composables/useDuxtVersion.ts +138 -0
- package/app/composables/useFuzzySearch.ts +83 -0
- package/app/composables/usePackageManager.ts +4 -0
- package/app/composables/useRecentPages.ts +52 -0
- package/app/error.vue +133 -0
- package/app/layouts/changelog.vue +85 -0
- package/app/layouts/default.vue +23 -0
- package/app/layouts/docs.vue +62 -0
- package/app/layouts/reference.vue +85 -0
- package/app/lib/utils.ts +7 -0
- package/app/middleware/duxt-section-layout.global.ts +57 -0
- package/app/pages/[...slug].vue +441 -0
- package/app/pages/index.vue +479 -0
- package/app/types/duxt.d.ts +955 -0
- package/app/utils/aside.ts +16 -0
- package/app/utils/changelog.ts +182 -0
- package/app/utils/code-highlight.ts +116 -0
- package/app/utils/duxt-config.ts +185 -0
- package/app/utils/duxt-text.ts +135 -0
- package/app/utils/file-icons.ts +62 -0
- package/app/utils/generated-sections.ts +190 -0
- package/app/utils/generated-toc.ts +216 -0
- package/app/utils/heading-link.ts +32 -0
- package/app/utils/locale-path.ts +45 -0
- package/app/utils/navigation-tree.ts +206 -0
- package/app/utils/nearest-page.ts +72 -0
- package/app/utils/openapi.ts +1028 -0
- package/app/utils/package-command.ts +162 -0
- package/app/utils/request-samples.ts +635 -0
- package/app/utils/section-scope.ts +97 -0
- package/app/utils/sheet-navigation.ts +100 -0
- package/app/utils/source-file.ts +84 -0
- package/app/utils/version-choices.ts +68 -0
- package/app/utils/version-paths.ts +65 -0
- package/bin/duxt-report.mjs +45 -0
- package/components.json +19 -0
- package/content-cache.ts +193 -0
- package/content.config.ts +38 -0
- package/duxt-app-config.ts +124 -0
- package/highlight-langs.ts +68 -0
- package/i18n/i18n.config.ts +16 -0
- package/i18n/locales/de/duxt/changelog.json +17 -0
- package/i18n/locales/de/duxt/code.json +13 -0
- package/i18n/locales/de/duxt/defaults.json +22 -0
- package/i18n/locales/de/duxt/devtools.json +7 -0
- package/i18n/locales/de/duxt/error.json +10 -0
- package/i18n/locales/de/duxt/footer.json +7 -0
- package/i18n/locales/de/duxt/locale.json +7 -0
- package/i18n/locales/de/duxt/nav.json +17 -0
- package/i18n/locales/de/duxt/openapi.json +89 -0
- package/i18n/locales/de/duxt/page.json +34 -0
- package/i18n/locales/de/duxt/search.json +12 -0
- package/i18n/locales/de/duxt/shortcuts.json +11 -0
- package/i18n/locales/de/duxt/theme.json +7 -0
- package/i18n/locales/de/duxt/toc.json +7 -0
- package/i18n/locales/de/duxt/version.json +25 -0
- package/i18n/locales/en/duxt/changelog.json +17 -0
- package/i18n/locales/en/duxt/code.json +13 -0
- package/i18n/locales/en/duxt/defaults.json +22 -0
- package/i18n/locales/en/duxt/devtools.json +7 -0
- package/i18n/locales/en/duxt/error.json +10 -0
- package/i18n/locales/en/duxt/footer.json +7 -0
- package/i18n/locales/en/duxt/locale.json +7 -0
- package/i18n/locales/en/duxt/nav.json +17 -0
- package/i18n/locales/en/duxt/openapi.json +89 -0
- package/i18n/locales/en/duxt/page.json +34 -0
- package/i18n/locales/en/duxt/search.json +12 -0
- package/i18n/locales/en/duxt/shortcuts.json +11 -0
- package/i18n/locales/en/duxt/theme.json +7 -0
- package/i18n/locales/en/duxt/toc.json +7 -0
- package/i18n/locales/en/duxt/version.json +25 -0
- package/i18n/locales/es/duxt/changelog.json +17 -0
- package/i18n/locales/es/duxt/code.json +13 -0
- package/i18n/locales/es/duxt/defaults.json +22 -0
- package/i18n/locales/es/duxt/devtools.json +7 -0
- package/i18n/locales/es/duxt/error.json +10 -0
- package/i18n/locales/es/duxt/footer.json +7 -0
- package/i18n/locales/es/duxt/locale.json +7 -0
- package/i18n/locales/es/duxt/nav.json +17 -0
- package/i18n/locales/es/duxt/openapi.json +89 -0
- package/i18n/locales/es/duxt/page.json +34 -0
- package/i18n/locales/es/duxt/search.json +12 -0
- package/i18n/locales/es/duxt/shortcuts.json +11 -0
- package/i18n/locales/es/duxt/theme.json +7 -0
- package/i18n/locales/es/duxt/toc.json +7 -0
- package/i18n/locales/es/duxt/version.json +25 -0
- package/i18n/locales/fr/duxt/changelog.json +17 -0
- package/i18n/locales/fr/duxt/code.json +13 -0
- package/i18n/locales/fr/duxt/defaults.json +22 -0
- package/i18n/locales/fr/duxt/devtools.json +7 -0
- package/i18n/locales/fr/duxt/error.json +10 -0
- package/i18n/locales/fr/duxt/footer.json +7 -0
- package/i18n/locales/fr/duxt/locale.json +7 -0
- package/i18n/locales/fr/duxt/nav.json +17 -0
- package/i18n/locales/fr/duxt/openapi.json +89 -0
- package/i18n/locales/fr/duxt/page.json +34 -0
- package/i18n/locales/fr/duxt/search.json +12 -0
- package/i18n/locales/fr/duxt/shortcuts.json +11 -0
- package/i18n/locales/fr/duxt/theme.json +7 -0
- package/i18n/locales/fr/duxt/toc.json +7 -0
- package/i18n/locales/fr/duxt/version.json +25 -0
- package/i18n/locales/pt/duxt/changelog.json +17 -0
- package/i18n/locales/pt/duxt/code.json +13 -0
- package/i18n/locales/pt/duxt/defaults.json +22 -0
- package/i18n/locales/pt/duxt/devtools.json +7 -0
- package/i18n/locales/pt/duxt/error.json +10 -0
- package/i18n/locales/pt/duxt/footer.json +7 -0
- package/i18n/locales/pt/duxt/locale.json +7 -0
- package/i18n/locales/pt/duxt/nav.json +17 -0
- package/i18n/locales/pt/duxt/openapi.json +89 -0
- package/i18n/locales/pt/duxt/page.json +34 -0
- package/i18n/locales/pt/duxt/search.json +12 -0
- package/i18n/locales/pt/duxt/shortcuts.json +11 -0
- package/i18n/locales/pt/duxt/theme.json +7 -0
- package/i18n/locales/pt/duxt/toc.json +7 -0
- package/i18n/locales/pt/duxt/version.json +25 -0
- package/i18n/locales/pt-BR/duxt/error.json +8 -0
- package/i18n/locales/pt-BR/duxt/nav.json +10 -0
- package/i18n/locales/pt-BR/duxt/page.json +10 -0
- package/i18n/locales/pt-BR/duxt/search.json +11 -0
- package/i18n/locales/pt-BR/duxt/version.json +13 -0
- package/mdc.config.ts +28 -0
- package/modules/config.ts +440 -0
- package/modules/devtools.ts +126 -0
- package/modules/git-meta.ts +349 -0
- package/modules/redirects.ts +152 -0
- package/modules/validate.ts +137 -0
- package/nuxt.config.ts +530 -0
- package/openapi-model.ts +358 -0
- package/openapi-parse.ts +1052 -0
- package/package.json +161 -0
- package/public/devtools/cache.html +318 -0
- package/public/devtools/checks.html +309 -0
- package/public/devtools/config.html +461 -0
- package/public/devtools/i18n.html +309 -0
- package/public/devtools/pages.html +348 -0
- package/public/devtools/paths.html +315 -0
- package/public/devtools/redirects.html +309 -0
- package/public/devtools/search.html +324 -0
- package/public/devtools/sources.html +309 -0
- package/public/devtools/versions.html +318 -0
- package/report.ts +303 -0
- package/repository-root.ts +26 -0
- package/section-reports.ts +55 -0
- package/sections-changelog.ts +644 -0
- package/sections-openapi.ts +450 -0
- package/sections-resolve.ts +711 -0
- package/sections.ts +157 -0
- package/server/devtools/content.ts +120 -0
- package/server/devtools/context.ts +38 -0
- package/server/devtools/entry-path.ts +30 -0
- package/server/devtools/handler.ts +104 -0
- package/server/devtools/preview.ts +461 -0
- package/server/devtools/render/content.ts +233 -0
- package/server/devtools/render/sources.ts +414 -0
- package/server/devtools/render/system.ts +352 -0
- package/server/devtools/shell.ts +611 -0
- package/server/devtools/sources.ts +122 -0
- package/server/devtools/system.ts +178 -0
- package/server/mcp/tools/list-pages.ts +52 -0
- package/server/mcp/tools/read-page.ts +57 -0
- package/server/mcp/tools/search-docs.ts +74 -0
- package/server/middleware/raw-markdown.ts +86 -0
- package/server/routes/llms-full.txt.get.ts +75 -0
- package/server/routes/llms.txt.get.ts +63 -0
- package/server/routes/rss.xml.get.ts +125 -0
- package/server/utils/duxt-server-text.ts +46 -0
- package/sources-git.ts +113 -0
- package/sources-resolve.ts +908 -0
- package/sources.ts +287 -0
- package/validate-report.ts +494 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import { reservedSegments, sourcesForRoute } from '../../../sources-resolve';
|
|
2
|
+
import { splitLocalePath } from '../../../app/utils/locale-path';
|
|
3
|
+
import { code, dim, escape, row, stat, stats, table, tag } from '../shell';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The three panels that answer "what did my `sources` list turn into".
|
|
7
|
+
*
|
|
8
|
+
* They share one idea: the resolver is pure and already tested, so none of this
|
|
9
|
+
* reimplements it — the panels call the same functions the theme calls and
|
|
10
|
+
* print what came back. A panel that computed its own answer would be able to
|
|
11
|
+
* disagree with the site, which is the one thing a debugging view must not do.
|
|
12
|
+
*
|
|
13
|
+
* Data in, HTML out. The queries that produce that data live in
|
|
14
|
+
* `../sources.ts`, and keeping the two apart is what lets the documentation
|
|
15
|
+
* render these very panels from fixtures instead of shipping screenshots.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface FoundPage {
|
|
19
|
+
path: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The sources table — what a consumer wrote, as the site resolved it. */
|
|
25
|
+
export function renderSources(
|
|
26
|
+
sources: DuxtResolvedSource[],
|
|
27
|
+
appConfigFile?: string
|
|
28
|
+
): string {
|
|
29
|
+
const reserved = reservedSegments(sources);
|
|
30
|
+
|
|
31
|
+
const rows = sources.map((source) =>
|
|
32
|
+
row([
|
|
33
|
+
code(source.prefix || '/'),
|
|
34
|
+
// A generated section is a collection like any other, which is what makes
|
|
35
|
+
// the rest of the layer work on it unchanged — and what made it
|
|
36
|
+
// indistinguishable here from a docs tree whose folder happens to be
|
|
37
|
+
// called CHANGELOG.md. The type is the one thing that tells them apart,
|
|
38
|
+
// so it is said rather than left to be inferred from the Folder column.
|
|
39
|
+
source.generated
|
|
40
|
+
? `${code(source.collection)} ${tag(source.generated.type)}`
|
|
41
|
+
: code(source.collection),
|
|
42
|
+
source.repositoryUrl
|
|
43
|
+
? `<a class="file" href="${escape(source.repositoryUrl)}" target="_blank">${escape(source.repository)}</a>`
|
|
44
|
+
: (escape(source.repository) ?? dim('this repository')),
|
|
45
|
+
code(source.path),
|
|
46
|
+
source.ref
|
|
47
|
+
? `${escape(source.ref)} ${dim(`(${source.refKind})`)}`
|
|
48
|
+
: dim('checkout'),
|
|
49
|
+
`${escape(source.version ?? '—')}${source.isDefault ? ` ${tag('default')}` : ''}`,
|
|
50
|
+
source.status === 'current'
|
|
51
|
+
? tag('current', 'ok')
|
|
52
|
+
: tag(source.status, source.status === 'eol' ? 'error' : 'warn'),
|
|
53
|
+
(reserved.get(source.collection)
|
|
54
|
+
? [...reserved.get(source.collection)!]
|
|
55
|
+
: []
|
|
56
|
+
)
|
|
57
|
+
.map((segment) => code(segment))
|
|
58
|
+
.join(' ') || dim('—')
|
|
59
|
+
])
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const configured = appConfigFile
|
|
63
|
+
? `<p class="hint">Configured in ${escape(appConfigFile)}.</p>`
|
|
64
|
+
: '';
|
|
65
|
+
|
|
66
|
+
const repos = new Set(
|
|
67
|
+
sources.map((source) => source.repository ?? 'this repository')
|
|
68
|
+
);
|
|
69
|
+
const generated = sources.filter((source) => source.generated);
|
|
70
|
+
// DISTINCT versions, not entries carrying one. A version is a collection per
|
|
71
|
+
// language and per generated section, so counting rows told a site with two
|
|
72
|
+
// versions in two languages that it had four — and eight once it declared a
|
|
73
|
+
// reference beside them. Keyed with the repository, because two projects may
|
|
74
|
+
// both call a version `v2`.
|
|
75
|
+
const versioned = new Set(
|
|
76
|
+
sources
|
|
77
|
+
.filter((source) => source.version)
|
|
78
|
+
.map((source) => `${repoKey(source)}\u0000${source.version}`)
|
|
79
|
+
);
|
|
80
|
+
const notCurrent = sources.filter((source) => source.status !== 'current');
|
|
81
|
+
|
|
82
|
+
const summary = stats([
|
|
83
|
+
stat(sources.length, sources.length === 1 ? 'collection' : 'collections'),
|
|
84
|
+
stat(repos.size, repos.size === 1 ? 'repository' : 'repositories'),
|
|
85
|
+
stat(versioned.size, 'versions'),
|
|
86
|
+
stat(
|
|
87
|
+
generated.length,
|
|
88
|
+
generated.length === 1 ? 'generated section' : 'generated sections'
|
|
89
|
+
),
|
|
90
|
+
// Not "the default version": with two repositories there are two defaults,
|
|
91
|
+
// and the figure worth seeing is how many versions carry a warning the
|
|
92
|
+
// reader will meet — deprecated, eol, or a branch still moving.
|
|
93
|
+
stat(notCurrent.length, 'not current', notCurrent.length ? 'warn' : 'ok')
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
return `${summary}${table(
|
|
97
|
+
[
|
|
98
|
+
'Prefix',
|
|
99
|
+
'Collection',
|
|
100
|
+
'Repository',
|
|
101
|
+
'Folder / artefact',
|
|
102
|
+
'Ref',
|
|
103
|
+
'Version',
|
|
104
|
+
'Status',
|
|
105
|
+
'Reserved segments'
|
|
106
|
+
],
|
|
107
|
+
rows,
|
|
108
|
+
'No sources resolved at all — `duxt.sources` is empty or unreadable.'
|
|
109
|
+
)}${configured}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface PathsData {
|
|
113
|
+
/** The URL as the reader pasted it, locale segment and all. */
|
|
114
|
+
input: string;
|
|
115
|
+
locales: string[];
|
|
116
|
+
/** Where the locale chain ends — vue-i18n's own fallback. */
|
|
117
|
+
fallbackLocale?: string | string[];
|
|
118
|
+
sources: DuxtResolvedSource[];
|
|
119
|
+
/** The page the claiming collection holds at that path, if it holds one. */
|
|
120
|
+
found: FoundPage | null;
|
|
121
|
+
/** Every page path the site serves, by collection. */
|
|
122
|
+
byCollection: Map<string, string[]>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The path debugger.
|
|
127
|
+
*
|
|
128
|
+
* Every version bug this repo had was a disagreement between what a URL looks
|
|
129
|
+
* like and which source claims it — `/workflows/v0.7.0` 404ing, and
|
|
130
|
+
* `/workflows/v0.7.0/v0.7.0` being generated. Both were arithmetic on prefixes
|
|
131
|
+
* that nothing printed. This prints it.
|
|
132
|
+
*/
|
|
133
|
+
export function renderPaths({
|
|
134
|
+
input,
|
|
135
|
+
locales,
|
|
136
|
+
fallbackLocale,
|
|
137
|
+
sources,
|
|
138
|
+
found,
|
|
139
|
+
byCollection
|
|
140
|
+
}: PathsData): string {
|
|
141
|
+
const form = `<form method="get">
|
|
142
|
+
<input type="text" name="path" value="${escape(input)}" placeholder="/workflows/v0.7.0/guides/add-a-body" autofocus>
|
|
143
|
+
<button type="submit">Resolve</button>
|
|
144
|
+
</form>`;
|
|
145
|
+
|
|
146
|
+
if (!input) {
|
|
147
|
+
return `${form}<div class="note">Paste a URL as the browser shows it — locale segment included. The steps below are the ones the theme walks on every request.</div>`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const steps: string[] = [];
|
|
151
|
+
|
|
152
|
+
// 1. The locale segment, which is interface and not content — but which is
|
|
153
|
+
// kept, because it decides WHICH COLLECTION serves the path below.
|
|
154
|
+
const { locale, path: documentationPath } = splitLocalePath(input, locales);
|
|
155
|
+
steps.push(
|
|
156
|
+
documentationPath === input
|
|
157
|
+
? `No locale segment. Documentation path is ${code(input)}.`
|
|
158
|
+
: `Locale segment stripped: ${code(input)} → ${code(documentationPath)}, language ${code(locale!)}. ${dim('The locale translates the interface, never the content tree — but it does pick the collection.')}`
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// 2. Which source claims it: longest prefix, then the language. Both at once,
|
|
162
|
+
// because a translation carries the SAME prefix as its original — taking
|
|
163
|
+
// the prefix alone answers with whichever collection sorted first.
|
|
164
|
+
const chain = sourcesForRoute(
|
|
165
|
+
documentationPath,
|
|
166
|
+
locale,
|
|
167
|
+
sources,
|
|
168
|
+
fallbackLocale
|
|
169
|
+
);
|
|
170
|
+
const source = chain[0];
|
|
171
|
+
|
|
172
|
+
if (!source) {
|
|
173
|
+
steps.push(
|
|
174
|
+
`${tag('stops here', 'error')} No source claims this path, and none serves the root. Add a source without a prefix, or check the Sources panel.`
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
return `${form}<ol class="steps">${steps.map((step) => `<li>${step}</li>`).join('')}</ol>`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
steps.push(
|
|
181
|
+
`Longest matching prefix is ${code(source.prefix || '/')} → collection ${code(source.collection)}${source.version ? `, version ${code(source.version)}` : ''}${source.locale ? `, language ${code(source.locale)}` : ''}.`
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// 2b. Where a page missing from that collection goes next. Nothing else
|
|
185
|
+
// shows the chain, and the fallback is the half of translation support
|
|
186
|
+
// that has no visible symptom until a page is silently English.
|
|
187
|
+
if (chain.length > 1) {
|
|
188
|
+
steps.push(
|
|
189
|
+
`Falls back to ${chain
|
|
190
|
+
.slice(1)
|
|
191
|
+
.map((other) => code(other.collection))
|
|
192
|
+
.join(
|
|
193
|
+
' → '
|
|
194
|
+
)}. ${dim('A page is translated whole or not at all, so this is a chain of collections rather than a merge.')}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 3. Does that collection hold the page.
|
|
199
|
+
if (found) {
|
|
200
|
+
steps.push(
|
|
201
|
+
`${tag('found', 'ok')} ${escape(found.title ?? documentationPath)} — ${escape(found.id ?? '')}`
|
|
202
|
+
);
|
|
203
|
+
} else {
|
|
204
|
+
steps.push(
|
|
205
|
+
`${tag('stops here', 'error')} Collection ${code(source.collection)} has no page at ${code(documentationPath)}.`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const body = `<ol class="steps">${steps.map((step) => `<li>${step}</li>`).join('')}</ol>`;
|
|
210
|
+
|
|
211
|
+
// 4. Whether it exists elsewhere. A page missing from one version and present
|
|
212
|
+
// in another is the fallback's case; a page missing everywhere is a typo.
|
|
213
|
+
const rest = source.prefix
|
|
214
|
+
? documentationPath.slice(source.prefix.length)
|
|
215
|
+
: documentationPath;
|
|
216
|
+
|
|
217
|
+
// Only versions of the SAME repository: `/duxt/guide` and `/workflows/guide`
|
|
218
|
+
// are two projects that happen to share a folder name, and putting them in
|
|
219
|
+
// one table invents a comparison nobody asked for.
|
|
220
|
+
const siblings = sources.filter(
|
|
221
|
+
(other) =>
|
|
222
|
+
(other.repo ?? other.repository) === (source.repo ?? source.repository)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const elsewhere = siblings.map((other) => {
|
|
226
|
+
const candidate = `${other.prefix}${rest}` || '/';
|
|
227
|
+
const has = byCollection.get(other.collection)?.includes(candidate);
|
|
228
|
+
|
|
229
|
+
return row([
|
|
230
|
+
code(other.prefix || '/'),
|
|
231
|
+
escape(other.version ?? '—'),
|
|
232
|
+
// A translation shares its original's prefix and version, so without the
|
|
233
|
+
// language the two are one row printed twice.
|
|
234
|
+
other.locale
|
|
235
|
+
? `${escape(other.locale)}${other.isDefaultLocale ? ` ${tag('original')}` : ''}`
|
|
236
|
+
: dim('—'),
|
|
237
|
+
code(candidate),
|
|
238
|
+
has ? tag('present', 'ok') : tag('missing', 'muted')
|
|
239
|
+
]);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const near = found
|
|
243
|
+
? ''
|
|
244
|
+
: `<h2>Nearest paths in ${escape(source.collection)}</h2>${table(
|
|
245
|
+
['Path'],
|
|
246
|
+
nearest(
|
|
247
|
+
byCollection.get(source.collection) ?? [],
|
|
248
|
+
documentationPath
|
|
249
|
+
).map((path) => row([code(path)]))
|
|
250
|
+
)}`;
|
|
251
|
+
|
|
252
|
+
return `${form}${body}<h2>The same page in other sources</h2>${table(['Prefix', 'Version', 'Language', 'Would be', 'State'], elsewhere)}${near}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** The handful of paths that share the longest leading run with the miss. */
|
|
256
|
+
function nearest(paths: string[], target: string): string[] {
|
|
257
|
+
const score = (path: string) => {
|
|
258
|
+
let index = 0;
|
|
259
|
+
while (index < path.length && path[index] === target[index]) index += 1;
|
|
260
|
+
return index;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
return [...paths].sort((a, b) => score(b) - score(a)).slice(0, 8);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Which version — and which language — carries which page.
|
|
268
|
+
*
|
|
269
|
+
* Nothing else shows this. The fallback's whole job is the gap between two
|
|
270
|
+
* columns of this table, and until now the only way to find one was to open a
|
|
271
|
+
* URL and see a 404. A translation falls back exactly the same way, so the
|
|
272
|
+
* missing `de` page is the same question one axis over.
|
|
273
|
+
*/
|
|
274
|
+
export function renderVersions(
|
|
275
|
+
sources: DuxtResolvedSource[],
|
|
276
|
+
byCollection: Map<string, string[]>
|
|
277
|
+
): string {
|
|
278
|
+
// Two groupings, one grid. Versions of different repositories share no paths
|
|
279
|
+
// and would invent gaps that are not gaps — and a locale is that same case
|
|
280
|
+
// one level down: `de` and `en` are two collections of the SAME version, so
|
|
281
|
+
// comparing them as versions drew a matrix of identical columns whose
|
|
282
|
+
// headings were all empty, because a translation carries neither a version
|
|
283
|
+
// nor a prefix of its own. Each axis therefore holds the other fixed.
|
|
284
|
+
//
|
|
285
|
+
// A GENERATED SECTION is the same argument a third time, and the one that
|
|
286
|
+
// reads worst when it is got wrong. A docs page and an endpoint page are
|
|
287
|
+
// never the same page — they share no path — so a grid holding both drew a
|
|
288
|
+
// column per collection rather than per version (`v2 | v1.9 | v2 | v1.9`,
|
|
289
|
+
// with nothing saying which pair was the reference) and filled every
|
|
290
|
+
// cross-quadrant cell with the `·` that means MISSING, for pages that were
|
|
291
|
+
// never meant to be there. Each declaration therefore gets a grid of its own.
|
|
292
|
+
const versions = groupBy(
|
|
293
|
+
sources,
|
|
294
|
+
(source) =>
|
|
295
|
+
`${repoKey(source)}\u0000${source.locale ?? ''}\u0000${sectionKey(source)}`
|
|
296
|
+
);
|
|
297
|
+
const locales = groupBy(
|
|
298
|
+
sources,
|
|
299
|
+
(source) =>
|
|
300
|
+
`${repoKey(source)}\u0000${source.version ?? ''}\u0000${sectionKey(source)}`
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const sections = [
|
|
304
|
+
...versions.map((group) =>
|
|
305
|
+
matrix(
|
|
306
|
+
group,
|
|
307
|
+
byCollection,
|
|
308
|
+
(source) =>
|
|
309
|
+
`${escape(source.version || source.ref || source.prefix || '—')}${source.isDefault ? ' *' : ''}`,
|
|
310
|
+
badges(group[0]!, group[0]!.locale),
|
|
311
|
+
'* the version served without a version segment.'
|
|
312
|
+
)
|
|
313
|
+
),
|
|
314
|
+
...locales.map((group) =>
|
|
315
|
+
matrix(
|
|
316
|
+
group,
|
|
317
|
+
byCollection,
|
|
318
|
+
(source) =>
|
|
319
|
+
`${escape(source.locale ?? '—')}${source.isDefaultLocale ? ' *' : ''}`,
|
|
320
|
+
badges(group[0]!, group[0]!.version),
|
|
321
|
+
'* the language served from the source path itself, without a folder.'
|
|
322
|
+
)
|
|
323
|
+
)
|
|
324
|
+
];
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
sections.join('') ||
|
|
328
|
+
'<div class="note">No source is published at more than one version or in more than one language, so there is no matrix to draw. Add a second entry to <code>refs</code> or to <code>locales</code> to see one.</div>'
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const repoKey = (source: DuxtResolvedSource) =>
|
|
333
|
+
source.repo ?? source.repository ?? '';
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Which grid a source belongs in: the docs tree, or one named declaration.
|
|
337
|
+
*
|
|
338
|
+
* The DECLARATION rather than the label or the type, because that is the
|
|
339
|
+
* identity `resolveGeneratedSections` recorded for exactly this question — two
|
|
340
|
+
* sections of one repository may share both of the others.
|
|
341
|
+
*/
|
|
342
|
+
const sectionKey = (source: DuxtResolvedSource) =>
|
|
343
|
+
source.generated ? String(source.generated.declaration) : '';
|
|
344
|
+
|
|
345
|
+
/** What this grid holds, beside the repository it is named after. */
|
|
346
|
+
const badges = (source: DuxtResolvedSource, held?: string) =>
|
|
347
|
+
`${held ? tag(held, 'muted') : ''}${
|
|
348
|
+
source.generated ? ` ${tag(source.generated.label, 'muted')}` : ''
|
|
349
|
+
}`.trim();
|
|
350
|
+
|
|
351
|
+
/** The groups worth a table: a single column compares nothing. */
|
|
352
|
+
function groupBy(
|
|
353
|
+
sources: DuxtResolvedSource[],
|
|
354
|
+
key: (source: DuxtResolvedSource) => string
|
|
355
|
+
): DuxtResolvedSource[][] {
|
|
356
|
+
const groups = new Map<string, DuxtResolvedSource[]>();
|
|
357
|
+
|
|
358
|
+
for (const source of sources) {
|
|
359
|
+
const list = groups.get(key(source)) ?? [];
|
|
360
|
+
list.push(source);
|
|
361
|
+
groups.set(key(source), list);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return [...groups.values()].filter((group) => group.length > 1);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* One grid: every page of the group down the side, the group's members across.
|
|
369
|
+
*
|
|
370
|
+
* The column heading falls through by truthiness rather than `??`, because
|
|
371
|
+
* `prefix` is an EMPTY STRING for the source served without a segment — `??`
|
|
372
|
+
* keeps it, and the column then has no name at all. That is what the Versions
|
|
373
|
+
* tab showed as a row of bare asterisks.
|
|
374
|
+
*/
|
|
375
|
+
function matrix(
|
|
376
|
+
group: DuxtResolvedSource[],
|
|
377
|
+
byCollection: Map<string, string[]>,
|
|
378
|
+
label: (source: DuxtResolvedSource) => string,
|
|
379
|
+
badge: string,
|
|
380
|
+
footnote: string
|
|
381
|
+
): string {
|
|
382
|
+
const { repo, repository } = group[0]!;
|
|
383
|
+
const logical = new Set<string>();
|
|
384
|
+
|
|
385
|
+
for (const source of group) {
|
|
386
|
+
for (const path of byCollection.get(source.collection) ?? []) {
|
|
387
|
+
logical.add(source.prefix ? path.slice(source.prefix.length) : path);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const rows = [...logical].sort().map((path) => {
|
|
392
|
+
const cells = group.map((source) => {
|
|
393
|
+
const has = byCollection
|
|
394
|
+
.get(source.collection)
|
|
395
|
+
?.includes(`${source.prefix}${path}` || '/');
|
|
396
|
+
|
|
397
|
+
return has ? '<td class="mark yes">✓</td>' : '<td class="mark no">·</td>';
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
return `<tr><td>${code(path || '/')}</td>${cells.join('')}</tr>`;
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Through `table()` like every other panel. Built by hand it was the one
|
|
404
|
+
// table in the tab without the scroll container, the border and the column
|
|
405
|
+
// scopes — visibly a different component for no reason.
|
|
406
|
+
// The repository's real name before its URL slug: `acme/sdk` says which
|
|
407
|
+
// project this grid is about, `sdk` only says which segment it took.
|
|
408
|
+
return `<h2>${escape(repository || repo || 'This repository')}${badge ? ` ${badge}` : ''}</h2>${table(
|
|
409
|
+
['Page', ...group.map(label)],
|
|
410
|
+
rows,
|
|
411
|
+
'This repository resolved to sources but to no pages at all.',
|
|
412
|
+
'matrix'
|
|
413
|
+
)}<p class="hint">${escape(footnote)}</p>`;
|
|
414
|
+
}
|