@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,352 @@
|
|
|
1
|
+
import { duxtDefaults, mergeDuxtConfig } from '../../../app/utils/duxt-config';
|
|
2
|
+
import {
|
|
3
|
+
code,
|
|
4
|
+
dim,
|
|
5
|
+
escape,
|
|
6
|
+
filter,
|
|
7
|
+
json,
|
|
8
|
+
row,
|
|
9
|
+
stat,
|
|
10
|
+
stats,
|
|
11
|
+
table,
|
|
12
|
+
tag
|
|
13
|
+
} from '../shell';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The panels about the machinery around the content: the merged config, the
|
|
17
|
+
* message catalogues, Content's download cache and the redirects that shipped.
|
|
18
|
+
*
|
|
19
|
+
* Each answers a question whose answer exists only as a side effect somewhere —
|
|
20
|
+
* inside a defu call, across seven JSON files, in a directory nobody opens, in
|
|
21
|
+
* a route-rule table generated at build time. The reading of those side effects
|
|
22
|
+
* lives in `../system.ts`; this half turns what was read into a table.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/* -------------------------------------------------------------------------- */
|
|
26
|
+
/* Config */
|
|
27
|
+
/* -------------------------------------------------------------------------- */
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The merged config, with the side of the merge each value came from.
|
|
31
|
+
*
|
|
32
|
+
* `mergeDuxtConfig` replaces arrays and merges objects, which is the subtlest
|
|
33
|
+
* rule in the layer: a consumer's `navigation` is the navigation, but its
|
|
34
|
+
* `footer.note` leaves `footer.columns` alone. "Why is duxt's entry still in my
|
|
35
|
+
* navbar" takes one look here instead of a session.
|
|
36
|
+
*/
|
|
37
|
+
export function renderConfig(consumer: Record<string, unknown>): string {
|
|
38
|
+
const merged = mergeDuxtConfig(
|
|
39
|
+
consumer as unknown as Partial<DuxtConfig>,
|
|
40
|
+
duxtDefaults
|
|
41
|
+
) as unknown as Record<string, unknown>;
|
|
42
|
+
|
|
43
|
+
const rows: string[] = [];
|
|
44
|
+
const counted: Origins = { consumer: 0, layer: 0, build: 0 };
|
|
45
|
+
collect(
|
|
46
|
+
merged,
|
|
47
|
+
consumer,
|
|
48
|
+
duxtDefaults as unknown as Record<string, unknown>,
|
|
49
|
+
'',
|
|
50
|
+
rows,
|
|
51
|
+
counted
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const summary = stats([
|
|
55
|
+
stat(rows.length, 'keys'),
|
|
56
|
+
stat(counted.consumer, 'from the site', 'ok'),
|
|
57
|
+
stat(counted.layer, 'from the layer'),
|
|
58
|
+
stat(counted.build, 'computed')
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
return `${summary}<div class="note">Arrays are replaced whole, objects merge key by key. A row marked ${tag(
|
|
62
|
+
'build'
|
|
63
|
+
)} was computed by <code>modules/config.ts</code> and is written nowhere.</div>${filter(
|
|
64
|
+
'Filter keys'
|
|
65
|
+
)}${table(['Key', 'From', 'Value'], rows)}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const BUILT = new Set(['resolvedSources']);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Tallied while the rows are built, not counted back out of them.
|
|
72
|
+
*
|
|
73
|
+
* The first version of this figure filtered the rendered rows for the string
|
|
74
|
+
* `>consumer<`, which is a search of the config's own VALUES as much as of its
|
|
75
|
+
* origin badges — a label reading "consumer" would have counted itself.
|
|
76
|
+
*/
|
|
77
|
+
interface Origins {
|
|
78
|
+
consumer: number;
|
|
79
|
+
layer: number;
|
|
80
|
+
build: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A value the merge descends into: an object, and neither an array nor null.
|
|
85
|
+
*
|
|
86
|
+
* Checked on BOTH sides before recursing, not just for the merged value. The
|
|
87
|
+
* two sides of a key need not have the same shape — `aside.title` is a
|
|
88
|
+
* translation key in the layer's defaults and a per-locale object in a site
|
|
89
|
+
* that translates it — and the earlier version tested only whether the KEY was
|
|
90
|
+
* present, then walked into the string with `in`, which throws. A panel that
|
|
91
|
+
* crashes on a translated string is a panel nobody can open on the site that
|
|
92
|
+
* needs it most.
|
|
93
|
+
*/
|
|
94
|
+
const branches = (value: unknown): value is Record<string, unknown> =>
|
|
95
|
+
Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
96
|
+
|
|
97
|
+
function collect(
|
|
98
|
+
merged: Record<string, unknown>,
|
|
99
|
+
consumer: Record<string, unknown> | undefined,
|
|
100
|
+
defaults: Record<string, unknown> | undefined,
|
|
101
|
+
prefix: string,
|
|
102
|
+
rows: string[],
|
|
103
|
+
counted: Origins
|
|
104
|
+
): void {
|
|
105
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
106
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
107
|
+
const inConsumer = branches(consumer) ? key in consumer : false;
|
|
108
|
+
|
|
109
|
+
// Recurse only where both sides are objects: an array is a single value
|
|
110
|
+
// here, by the same rule the merge itself follows, and a key that is an
|
|
111
|
+
// object on one side and a string on the other is a REPLACED value — which
|
|
112
|
+
// is precisely what the reader opened this panel to see.
|
|
113
|
+
if (
|
|
114
|
+
branches(value) &&
|
|
115
|
+
branches(consumer?.[key]) &&
|
|
116
|
+
branches(defaults?.[key])
|
|
117
|
+
) {
|
|
118
|
+
collect(
|
|
119
|
+
value as Record<string, unknown>,
|
|
120
|
+
consumer?.[key] as Record<string, unknown>,
|
|
121
|
+
defaults?.[key] as Record<string, unknown>,
|
|
122
|
+
path,
|
|
123
|
+
rows,
|
|
124
|
+
counted
|
|
125
|
+
);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const kind = BUILT.has(key) ? 'build' : inConsumer ? 'consumer' : 'layer';
|
|
130
|
+
counted[kind] += 1;
|
|
131
|
+
|
|
132
|
+
const origin =
|
|
133
|
+
kind === 'build'
|
|
134
|
+
? tag('build')
|
|
135
|
+
: kind === 'consumer'
|
|
136
|
+
? tag('consumer', 'ok')
|
|
137
|
+
: tag('layer', 'muted');
|
|
138
|
+
|
|
139
|
+
rows.push(row([code(path), origin, json(value)]));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* -------------------------------------------------------------------------- */
|
|
144
|
+
/* i18n */
|
|
145
|
+
/* -------------------------------------------------------------------------- */
|
|
146
|
+
|
|
147
|
+
export interface LocaleFile {
|
|
148
|
+
/** The directory under `i18n/locales`, which is the locale's own name. */
|
|
149
|
+
dir: string;
|
|
150
|
+
/** Every message key it defines, flattened to `duxt.nav.open`. */
|
|
151
|
+
keys: string[];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface I18nData {
|
|
155
|
+
files: LocaleFile[];
|
|
156
|
+
/** The locales the site actually serves, which is a different list. */
|
|
157
|
+
locales: string[];
|
|
158
|
+
defaultLocale?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Message keys per locale, against the base language.
|
|
163
|
+
*
|
|
164
|
+
* Seven locales and one JSON file per namespace make drift a matter of time,
|
|
165
|
+
* and i18n answers a missing key by printing the key — so a gap shows up as a
|
|
166
|
+
* label reading `duxt.nav.open` on a page nobody opened in that language.
|
|
167
|
+
*/
|
|
168
|
+
export function renderI18n({
|
|
169
|
+
files,
|
|
170
|
+
locales,
|
|
171
|
+
defaultLocale
|
|
172
|
+
}: I18nData): string {
|
|
173
|
+
if (!files.length) return '<div class="note">No locales found.</div>';
|
|
174
|
+
|
|
175
|
+
const base = files.find((file) => file.dir === 'en') ?? files[0]!;
|
|
176
|
+
const baseKeys = new Set(base.keys);
|
|
177
|
+
|
|
178
|
+
const compared = files.map((file) => {
|
|
179
|
+
const keys = new Set(file.keys);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
dir: file.dir,
|
|
183
|
+
keys,
|
|
184
|
+
missing: [...baseKeys].filter((key) => !keys.has(key)),
|
|
185
|
+
extra: [...keys].filter((key) => !baseKeys.has(key))
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const rows = compared.map(({ dir, keys, missing, extra }) =>
|
|
190
|
+
// A locale file may deliberately hold only its overrides — pt-BR against
|
|
191
|
+
// pt-PT is the case in this repo — so "missing" is a fact, not a verdict.
|
|
192
|
+
row([
|
|
193
|
+
code(dir) + (dir === base.dir ? ` ${tag('base')}` : ''),
|
|
194
|
+
String(keys.size),
|
|
195
|
+
missing.length
|
|
196
|
+
? `${tag(String(missing.length), 'warn')} ${dim(truncate(missing.join(', '), 120))}`
|
|
197
|
+
: tag('complete', 'ok'),
|
|
198
|
+
extra.length
|
|
199
|
+
? `${tag(String(extra.length), 'warn')} ${dim(truncate(extra.join(', '), 120))}`
|
|
200
|
+
: dim('—')
|
|
201
|
+
])
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const served = locales.length
|
|
205
|
+
? `<p class="hint">Served locales: ${locales.map((locale) => code(locale)).join(' ')}${defaultLocale ? `, default ${code(defaultLocale)}` : ''}.</p>`
|
|
206
|
+
: '';
|
|
207
|
+
|
|
208
|
+
const gaps = compared.filter(
|
|
209
|
+
(entry) => entry.missing.length || entry.extra.length
|
|
210
|
+
).length;
|
|
211
|
+
|
|
212
|
+
const summary = stats([
|
|
213
|
+
stat(files.length, 'locale files'),
|
|
214
|
+
stat(baseKeys.size, `keys in ${base.dir}`),
|
|
215
|
+
stat(gaps, 'with a gap', gaps ? 'warn' : 'ok')
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
return `${summary}${served}${table(
|
|
219
|
+
['Directory', 'Keys', 'Missing vs base', 'Not in base'],
|
|
220
|
+
rows
|
|
221
|
+
)}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const truncate = (value: string, length: number) =>
|
|
225
|
+
value.length > length ? `${value.slice(0, length)}…` : value;
|
|
226
|
+
|
|
227
|
+
/* -------------------------------------------------------------------------- */
|
|
228
|
+
/* Cache */
|
|
229
|
+
/* -------------------------------------------------------------------------- */
|
|
230
|
+
|
|
231
|
+
export interface CacheEntry {
|
|
232
|
+
name: string;
|
|
233
|
+
/** Bytes on disk, summed over the directory. */
|
|
234
|
+
size: number;
|
|
235
|
+
modified: Date;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface CacheData {
|
|
239
|
+
dataDir: string;
|
|
240
|
+
/** `undefined` when the directory does not exist at all. */
|
|
241
|
+
entries?: CacheEntry[];
|
|
242
|
+
/** What the last drop did, if this render follows one. */
|
|
243
|
+
message?: string;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Content's download cache, and a way to drop one entry.
|
|
248
|
+
*
|
|
249
|
+
* A stale directory here cost real time once: a tag was fetched as a branch,
|
|
250
|
+
* failed, and the half-written `github.com-<owner>-<repo>-<ref>` folder kept
|
|
251
|
+
* answering for the fixed config until it was deleted by hand. Nothing in the
|
|
252
|
+
* build mentions this directory, so nothing suggests the fix.
|
|
253
|
+
*/
|
|
254
|
+
export function renderCache({ dataDir, entries, message }: CacheData): string {
|
|
255
|
+
if (!entries) {
|
|
256
|
+
return '<div class="note">Nothing downloaded yet — <code>.data/content</code> does not exist.</div>';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const rows = entries.map((entry) =>
|
|
260
|
+
row([
|
|
261
|
+
code(entry.name),
|
|
262
|
+
megabytes(entry.size),
|
|
263
|
+
entry.modified.toISOString().replace('T', ' ').slice(0, 16),
|
|
264
|
+
`<form method="post" style="margin:0">
|
|
265
|
+
<input type="hidden" name="drop" value="${escape(entry.name)}">
|
|
266
|
+
<button type="submit">Drop</button>
|
|
267
|
+
</form>`
|
|
268
|
+
])
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const note = message ? `<div class="note">${escape(message)}</div>` : '';
|
|
272
|
+
|
|
273
|
+
const summary = stats([
|
|
274
|
+
stat(entries.length, 'cached sources'),
|
|
275
|
+
stat(
|
|
276
|
+
megabytes(entries.reduce((sum, entry) => sum + entry.size, 0)),
|
|
277
|
+
'on disk'
|
|
278
|
+
)
|
|
279
|
+
]);
|
|
280
|
+
|
|
281
|
+
return `${note}${summary}<p class="hint">${escape(dataDir)}</p>${table(
|
|
282
|
+
['Entry', 'Size', 'Modified', ''],
|
|
283
|
+
rows,
|
|
284
|
+
'The cache directory exists but holds no source. Nothing remote has been downloaded yet.'
|
|
285
|
+
)}<div class="note">Dropping an entry makes the next build download it again. Content keys a directory by repository <em>and</em> ref, so an entry for a ref you have renamed is never reused and never cleaned up either.</div>`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const megabytes = (bytes: number) =>
|
|
289
|
+
bytes > 1024 * 1024
|
|
290
|
+
? `${(bytes / 1024 / 1024).toFixed(1)} MB`
|
|
291
|
+
: `${Math.max(1, Math.round(bytes / 1024))} kB`;
|
|
292
|
+
|
|
293
|
+
/* -------------------------------------------------------------------------- */
|
|
294
|
+
/* Redirects */
|
|
295
|
+
/* -------------------------------------------------------------------------- */
|
|
296
|
+
|
|
297
|
+
export interface RedirectRule {
|
|
298
|
+
from: string;
|
|
299
|
+
to: string;
|
|
300
|
+
status: number | string;
|
|
301
|
+
/** True for a rule `redirectFrom` generated, false for another module's. */
|
|
302
|
+
mine: boolean;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The route rules that ship, and which of them are the layer's.
|
|
307
|
+
*
|
|
308
|
+
* Read rather than recomputed on purpose: a rule that was generated and a rule
|
|
309
|
+
* that shipped are different claims, and only the second one redirects
|
|
310
|
+
* anybody. Reading them back means reading EVERY module's — `@nuxtjs/sitemap`
|
|
311
|
+
* puts `/sitemap.xml` in the table on any multi-locale site — so the rules the
|
|
312
|
+
* layer wrote are marked as such, from the list `modules/redirects.ts` records
|
|
313
|
+
* beside them. Nothing in a route rule says who wrote it.
|
|
314
|
+
*/
|
|
315
|
+
export function renderRedirects(rules?: RedirectRule[]): string {
|
|
316
|
+
if (!rules) {
|
|
317
|
+
return '<div class="note">Nitro exposed no route rules to read.</div>';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// The layer's own first: they are what this tab is opened for, and on a site
|
|
321
|
+
// with one moved page they would otherwise sit under a module's boilerplate.
|
|
322
|
+
const sorted = [...rules].sort(
|
|
323
|
+
(a, b) => Number(b.mine) - Number(a.mine) || a.from.localeCompare(b.from)
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const rows = sorted.map((rule) =>
|
|
327
|
+
row([
|
|
328
|
+
code(rule.from),
|
|
329
|
+
code(rule.to),
|
|
330
|
+
typeof rule.status === 'string'
|
|
331
|
+
? dim(rule.status)
|
|
332
|
+
: escape(String(rule.status)),
|
|
333
|
+
rule.mine ? tag('redirectFrom', 'ok') : tag('another module', 'muted')
|
|
334
|
+
])
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
const mine = sorted.filter((rule) => rule.mine).length;
|
|
338
|
+
|
|
339
|
+
return `${stats([
|
|
340
|
+
stat(rows.length, 'rules'),
|
|
341
|
+
stat(mine, 'from redirectFrom', mine ? 'ok' : ''),
|
|
342
|
+
stat(rows.length - mine, 'from elsewhere')
|
|
343
|
+
])}${table(
|
|
344
|
+
['From', 'To', 'Status', 'Written by'],
|
|
345
|
+
rows,
|
|
346
|
+
'The server shipped no redirect at all — not the layer’s, not a module’s.'
|
|
347
|
+
)}<div class="note">${
|
|
348
|
+
mine
|
|
349
|
+
? 'One rule per prefix a page is served under — the repository segment, the version segment and every locale — because an old URL was bookmarked under exactly one of them and nothing says which.'
|
|
350
|
+
: 'No page carries <code>redirectFrom</code>, so the layer generated nothing; every rule above belongs to another module.'
|
|
351
|
+
}</div>`;
|
|
352
|
+
}
|