@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,635 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The request the reader has built, written out in the languages they work in.
|
|
3
|
+
*
|
|
4
|
+
* ONE SAMPLE IS A GENERATOR, not a string, because the request changes on every
|
|
5
|
+
* keystroke: the reader edits a path parameter or a body and every open sample
|
|
6
|
+
* has to follow. That is also why these run in the browser and why
|
|
7
|
+
* `code-highlight.ts` needs a grammar per language they use.
|
|
8
|
+
*
|
|
9
|
+
* TYPESCRIPT RATHER THAN JAVASCRIPT, and one group rather than two. The three
|
|
10
|
+
* clients that take a type parameter get the response's component name —
|
|
11
|
+
* `$fetch<Widget>` is the line somebody writes — and `fetch`, which has none,
|
|
12
|
+
* is identical in both languages. Two groups would therefore have carried one
|
|
13
|
+
* pair that never differs and another that differs only where the document
|
|
14
|
+
* named its response, for double the tabs.
|
|
15
|
+
*
|
|
16
|
+
* WHAT A SAMPLE SHOWS IS THE REQUEST, and nothing after it. The response is
|
|
17
|
+
* already on screen, in the panel below the editor — a sample that fetched it a
|
|
18
|
+
* second time would be showing the reader something they can see, at the cost of
|
|
19
|
+
* `defer resp.Body.Close()` and an error branch in every language that has one.
|
|
20
|
+
* The exception is Go, where `err` is not decoration: without the checks it is
|
|
21
|
+
* not Go, and a snippet that does not compile is worse than a long one.
|
|
22
|
+
*
|
|
23
|
+
* THE REGISTRY IS OPEN. A consumer adds its own entry to `duxt.requestSamples`
|
|
24
|
+
* — the API it documents may be consumed in a language nobody here thought of —
|
|
25
|
+
* which is why `DuxtRequestSample` is declared in `app/types/duxt.d.ts` beside
|
|
26
|
+
* the config key rather than exported from here, and why `language` is a plain
|
|
27
|
+
* Shiki id: `modules/config.ts` reads the list at build time and loads exactly
|
|
28
|
+
* the grammars it names.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** The body as data, where it is JSON. Undefined for anything else. */
|
|
32
|
+
function asJson(body?: string): unknown {
|
|
33
|
+
if (!body?.trim()) return undefined;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(body) as unknown;
|
|
37
|
+
} catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const pad = (depth: number) => ' '.repeat(depth);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Whether a list is short enough to stay on one line.
|
|
46
|
+
*
|
|
47
|
+
* `tags: ['good']` over three lines is the clearest tell that a snippet was
|
|
48
|
+
* generated rather than written, and a body of scalars is mostly short lists. So
|
|
49
|
+
* an array of primitives that fits stays inline; anything holding a structure
|
|
50
|
+
* breaks, because that is what a person would do too.
|
|
51
|
+
*/
|
|
52
|
+
function inlineList(items: unknown[], rendered: string[]): string | undefined {
|
|
53
|
+
const primitive = items.every(
|
|
54
|
+
(item) => item === null || typeof item !== 'object'
|
|
55
|
+
);
|
|
56
|
+
if (!primitive) return undefined;
|
|
57
|
+
|
|
58
|
+
const line = rendered.join(', ');
|
|
59
|
+
return line.length <= 60 ? line : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A JavaScript key that needs no quotes. */
|
|
63
|
+
const bareKey = /^[A-Za-z_$][\w$]*$/;
|
|
64
|
+
|
|
65
|
+
/** A value as a JavaScript literal, indented for the depth it sits at. */
|
|
66
|
+
function js(value: unknown, depth = 1): string {
|
|
67
|
+
if (value === null) return 'null';
|
|
68
|
+
if (typeof value === 'string') return `'${value.replaceAll("'", "\\'")}'`;
|
|
69
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
70
|
+
return String(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
if (!value.length) return '[]';
|
|
75
|
+
|
|
76
|
+
const rendered = value.map((item) => js(item, depth + 1));
|
|
77
|
+
const inline = inlineList(value, rendered);
|
|
78
|
+
if (inline) return `[${inline}]`;
|
|
79
|
+
|
|
80
|
+
const items = rendered
|
|
81
|
+
.map((item) => `${pad(depth + 1)}${item}`)
|
|
82
|
+
.join(',\n');
|
|
83
|
+
|
|
84
|
+
return `[\n${items}\n${pad(depth)}]`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
88
|
+
if (!entries.length) return '{}';
|
|
89
|
+
|
|
90
|
+
const written = entries
|
|
91
|
+
.map(([key, item]) => {
|
|
92
|
+
const name = bareKey.test(key) ? key : `'${key}'`;
|
|
93
|
+
return `${pad(depth + 1)}${name}: ${js(item, depth + 1)}`;
|
|
94
|
+
})
|
|
95
|
+
.join(',\n');
|
|
96
|
+
|
|
97
|
+
return `{\n${written}\n${pad(depth)}}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** A value as a Python literal. Python indents with four spaces. */
|
|
101
|
+
function py(value: unknown, depth = 1): string {
|
|
102
|
+
const step = (n: number) => ' '.repeat(n);
|
|
103
|
+
|
|
104
|
+
if (value === null) return 'None';
|
|
105
|
+
if (typeof value === 'boolean') return value ? 'True' : 'False';
|
|
106
|
+
if (typeof value === 'number') return String(value);
|
|
107
|
+
if (typeof value === 'string') return `"${value.replaceAll('"', '\\"')}"`;
|
|
108
|
+
|
|
109
|
+
if (Array.isArray(value)) {
|
|
110
|
+
if (!value.length) return '[]';
|
|
111
|
+
|
|
112
|
+
const rendered = value.map((item) => py(item, depth + 1));
|
|
113
|
+
const inline = inlineList(value, rendered);
|
|
114
|
+
if (inline) return `[${inline}]`;
|
|
115
|
+
|
|
116
|
+
const items = rendered
|
|
117
|
+
.map((item) => `${step(depth + 1)}${item}`)
|
|
118
|
+
.join(',\n');
|
|
119
|
+
|
|
120
|
+
return `[\n${items},\n${step(depth)}]`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
124
|
+
if (!entries.length) return '{}';
|
|
125
|
+
|
|
126
|
+
const written = entries
|
|
127
|
+
.map(
|
|
128
|
+
([key, item]) =>
|
|
129
|
+
`${step(depth + 1)}"${key.replaceAll('"', '\\"')}": ${py(item, depth + 1)}`
|
|
130
|
+
)
|
|
131
|
+
.join(',\n');
|
|
132
|
+
|
|
133
|
+
return `{\n${written},\n${step(depth)}}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** A value as a PHP literal. PHP arrays are the one shape for both kinds. */
|
|
137
|
+
function php(value: unknown, depth = 1): string {
|
|
138
|
+
const step = (n: number) => ' '.repeat(n);
|
|
139
|
+
|
|
140
|
+
if (value === null) return 'null';
|
|
141
|
+
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
142
|
+
if (typeof value === 'number') return String(value);
|
|
143
|
+
if (typeof value === 'string') return `'${value.replaceAll("'", "\\'")}'`;
|
|
144
|
+
|
|
145
|
+
if (Array.isArray(value)) {
|
|
146
|
+
if (!value.length) return '[]';
|
|
147
|
+
|
|
148
|
+
const rendered = value.map((item) => php(item, depth + 1));
|
|
149
|
+
const inline = inlineList(value, rendered);
|
|
150
|
+
if (inline) return `[${inline}]`;
|
|
151
|
+
|
|
152
|
+
const items = rendered
|
|
153
|
+
.map((item) => `${step(depth + 1)}${item}`)
|
|
154
|
+
.join(',\n');
|
|
155
|
+
|
|
156
|
+
return `[\n${items},\n${step(depth)}]`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
160
|
+
if (!entries.length) return '[]';
|
|
161
|
+
|
|
162
|
+
const written = entries
|
|
163
|
+
.map(
|
|
164
|
+
([key, item]) =>
|
|
165
|
+
`${step(depth + 1)}'${key.replaceAll("'", "\\'")}' => ${php(item, depth + 1)}`
|
|
166
|
+
)
|
|
167
|
+
.join(',\n');
|
|
168
|
+
|
|
169
|
+
return `[\n${written},\n${step(depth)}]`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** One argument, safe inside a POSIX shell's single quotes. */
|
|
173
|
+
function shell(value: string): string {
|
|
174
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const hasHeaders = (request: DuxtOpenApiRequest) =>
|
|
178
|
+
Object.keys(request.headers).length > 0;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The request as a `curl` line.
|
|
182
|
+
*
|
|
183
|
+
* Single-quoted with the shell's own escape for an embedded quote, because every
|
|
184
|
+
* part of this — a URL, a header, a JSON body — comes from a document this layer
|
|
185
|
+
* did not write.
|
|
186
|
+
*
|
|
187
|
+
* `-X` ONLY WHERE CURL WOULD NOT GUESS. A bare `curl` is already a GET, and `-d`
|
|
188
|
+
* already makes it a POST, so writing the method out in those two cases is the
|
|
189
|
+
* mark of a generated command rather than one somebody would type.
|
|
190
|
+
*/
|
|
191
|
+
export function openApiCurl(request: DuxtOpenApiRequest): string {
|
|
192
|
+
const implied =
|
|
193
|
+
request.method === 'GET' || (request.method === 'POST' && !!request.body);
|
|
194
|
+
|
|
195
|
+
const lines = [
|
|
196
|
+
implied
|
|
197
|
+
? `curl ${shell(request.url)}`
|
|
198
|
+
: `curl -X ${request.method} ${shell(request.url)}`
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
202
|
+
lines.push(` -H ${shell(`${name}: ${value}`)}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (request.body) lines.push(` -d ${shell(request.body)}`);
|
|
206
|
+
|
|
207
|
+
return lines.join(' \\\n');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Options shared by the three JavaScript clients that take a `RequestInit`-ish
|
|
212
|
+
* object — `method` omitted for GET, which every one of them defaults to.
|
|
213
|
+
*/
|
|
214
|
+
function jsOptions(
|
|
215
|
+
request: DuxtOpenApiRequest,
|
|
216
|
+
body: 'stringify' | 'object'
|
|
217
|
+
): string[] {
|
|
218
|
+
const options: string[] = [];
|
|
219
|
+
|
|
220
|
+
if (request.method !== 'GET') {
|
|
221
|
+
options.push(` method: '${request.method}'`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (hasHeaders(request)) {
|
|
225
|
+
options.push(` headers: ${js(request.headers)}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (request.body) {
|
|
229
|
+
const data = asJson(request.body);
|
|
230
|
+
|
|
231
|
+
if (data === undefined) {
|
|
232
|
+
options.push(` body: ${js(request.body)}`);
|
|
233
|
+
} else if (body === 'object') {
|
|
234
|
+
options.push(` body: ${js(data)}`);
|
|
235
|
+
} else {
|
|
236
|
+
options.push(` body: JSON.stringify(${js(data)})`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return options;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* A call, with the response type where the client has somewhere to put one.
|
|
245
|
+
*
|
|
246
|
+
* `typed` is what separates the TypeScript samples from the JavaScript they
|
|
247
|
+
* would otherwise be character for character: `$fetch`, `useFetch` and axios all
|
|
248
|
+
* take the response shape as a type parameter, and it is the line somebody
|
|
249
|
+
* actually writes. `fetch` has none — it returns a `Response` whatever the body
|
|
250
|
+
* turns out to be — so its sample is the same in either language, and saying so
|
|
251
|
+
* is more honest than inventing an annotation for it.
|
|
252
|
+
*/
|
|
253
|
+
function jsCall(
|
|
254
|
+
call: string,
|
|
255
|
+
request: DuxtOpenApiRequest,
|
|
256
|
+
body: 'stringify' | 'object',
|
|
257
|
+
typed = false
|
|
258
|
+
): string {
|
|
259
|
+
const generic =
|
|
260
|
+
typed && request.responseType ? `<${request.responseType}>` : '';
|
|
261
|
+
|
|
262
|
+
const options = jsOptions(request, body);
|
|
263
|
+
if (!options.length) return `${call}${generic}('${request.url}');`;
|
|
264
|
+
|
|
265
|
+
return `${call}${generic}('${request.url}', {\n${options.join(',\n')}\n});`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** The request as a `fetch` call. A JSON body is written as the data it is. */
|
|
269
|
+
export function openApiFetch(request: DuxtOpenApiRequest): string {
|
|
270
|
+
return `await ${jsCall('fetch', request, 'stringify')}`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** ofetch, which serialises an object body and sets the header itself. */
|
|
274
|
+
export function openApiOfetch(request: DuxtOpenApiRequest): string {
|
|
275
|
+
return `await ${jsCall('$fetch', request, 'object', true)}`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* `useFetch`, which is a component's data dependency rather than a call.
|
|
280
|
+
*
|
|
281
|
+
* Shown beside `$fetch` rather than instead of it: the two are not spellings of
|
|
282
|
+
* one thing. `$fetch` is what a reader pastes into an event handler; `useFetch`
|
|
283
|
+
* is what they paste into a component that needs the data to render, and it
|
|
284
|
+
* carries the destructuring that makes that the point.
|
|
285
|
+
*/
|
|
286
|
+
export function openApiUseFetch(request: DuxtOpenApiRequest): string {
|
|
287
|
+
return `const { data } = await ${jsCall('useFetch', request, 'object', true)}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** axios through its uniform form, so the shape does not change per method. */
|
|
291
|
+
export function openApiAxios(request: DuxtOpenApiRequest): string {
|
|
292
|
+
const options = [
|
|
293
|
+
` method: '${request.method.toLowerCase()}'`,
|
|
294
|
+
` url: '${request.url}'`
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
if (hasHeaders(request)) options.push(` headers: ${js(request.headers)}`);
|
|
298
|
+
|
|
299
|
+
if (request.body) {
|
|
300
|
+
const data = asJson(request.body);
|
|
301
|
+
options.push(` data: ${data === undefined ? js(request.body) : js(data)}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const generic = request.responseType ? `<${request.responseType}>` : '';
|
|
305
|
+
|
|
306
|
+
return `await axios${generic}({\n${options.join(',\n')}\n});`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** The verb as `requests` and `httpx` spell it, or their generic form. */
|
|
310
|
+
const pyVerbs = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD']);
|
|
311
|
+
|
|
312
|
+
function pyCall(module: string, request: DuxtOpenApiRequest): string {
|
|
313
|
+
const known = pyVerbs.has(request.method);
|
|
314
|
+
const call = known
|
|
315
|
+
? `${module}.${request.method.toLowerCase()}(`
|
|
316
|
+
: `${module}.request(`;
|
|
317
|
+
|
|
318
|
+
const args = known
|
|
319
|
+
? [` "${request.url}"`]
|
|
320
|
+
: [` "${request.method}"`, ` "${request.url}"`];
|
|
321
|
+
|
|
322
|
+
if (hasHeaders(request)) args.push(` headers=${py(request.headers)}`);
|
|
323
|
+
|
|
324
|
+
if (request.body) {
|
|
325
|
+
const data = asJson(request.body);
|
|
326
|
+
args.push(
|
|
327
|
+
data === undefined
|
|
328
|
+
? ` data=${py(request.body)}`
|
|
329
|
+
: ` json=${py(data)}`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return `import ${module}\n\n${call}\n${args.join(',\n')},\n)`;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** requests, which is what a Python reader reaches for first. */
|
|
337
|
+
export function openApiRequests(request: DuxtOpenApiRequest): string {
|
|
338
|
+
return pyCall('requests', request);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** httpx, for a reader who wants the same shape with async available. */
|
|
342
|
+
export function openApiHttpx(request: DuxtOpenApiRequest): string {
|
|
343
|
+
return pyCall('httpx', request);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** The standard library, for a snippet that must not add a dependency. */
|
|
347
|
+
export function openApiUrllib(request: DuxtOpenApiRequest): string {
|
|
348
|
+
const imports = request.body
|
|
349
|
+
? 'import json\nimport urllib.request'
|
|
350
|
+
: 'import urllib.request';
|
|
351
|
+
|
|
352
|
+
const args = [` "${request.url}"`, ` method="${request.method}"`];
|
|
353
|
+
|
|
354
|
+
if (hasHeaders(request)) args.push(` headers=${py(request.headers)}`);
|
|
355
|
+
|
|
356
|
+
if (request.body) {
|
|
357
|
+
const data = asJson(request.body);
|
|
358
|
+
args.push(
|
|
359
|
+
data === undefined
|
|
360
|
+
? ` data=${py(request.body)}.encode()`
|
|
361
|
+
: ` data=json.dumps(${py(data)}).encode()`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return [
|
|
366
|
+
imports,
|
|
367
|
+
'',
|
|
368
|
+
'request = urllib.request.Request(',
|
|
369
|
+
args.join(',\n') + ',',
|
|
370
|
+
')',
|
|
371
|
+
'',
|
|
372
|
+
'urllib.request.urlopen(request)'
|
|
373
|
+
].join('\n');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Go's standard library.
|
|
378
|
+
*
|
|
379
|
+
* The one sample that keeps its error branches. `err` is not ceremony in Go —
|
|
380
|
+
* `http.NewRequest` and `Do` both return one, and a snippet that ignores them
|
|
381
|
+
* does not compile, which is a worse thing to hand a reader than four extra
|
|
382
|
+
* lines.
|
|
383
|
+
*/
|
|
384
|
+
export function openApiGo(request: DuxtOpenApiRequest): string {
|
|
385
|
+
const lines: string[] = [];
|
|
386
|
+
|
|
387
|
+
if (request.body) {
|
|
388
|
+
lines.push(
|
|
389
|
+
`body := strings.NewReader(\`${request.body.replaceAll('`', '` + "`" + `')}\`)`,
|
|
390
|
+
''
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
lines.push(
|
|
395
|
+
`req, err := http.NewRequest("${request.method}", "${request.url}", ${request.body ? 'body' : 'nil'})`,
|
|
396
|
+
'if err != nil {',
|
|
397
|
+
'\treturn err',
|
|
398
|
+
'}'
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
if (hasHeaders(request)) {
|
|
402
|
+
lines.push('');
|
|
403
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
404
|
+
lines.push(`req.Header.Set("${name}", "${value}")`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
lines.push('', 'resp, err := http.DefaultClient.Do(req)');
|
|
409
|
+
|
|
410
|
+
return lines.join('\n');
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Guzzle, the client a PHP project almost always already has. */
|
|
414
|
+
export function openApiGuzzle(request: DuxtOpenApiRequest): string {
|
|
415
|
+
const options: string[] = [];
|
|
416
|
+
|
|
417
|
+
if (hasHeaders(request)) {
|
|
418
|
+
options.push(` 'headers' => ${php(request.headers, 1)}`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (request.body) {
|
|
422
|
+
const data = asJson(request.body);
|
|
423
|
+
options.push(
|
|
424
|
+
data === undefined
|
|
425
|
+
? ` 'body' => ${php(request.body)}`
|
|
426
|
+
: ` 'json' => ${php(data, 1)}`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const client = '$client = new \\GuzzleHttp\\Client();';
|
|
431
|
+
const call = options.length
|
|
432
|
+
? `$client->request('${request.method}', '${request.url}', [\n${options.join(',\n')},\n]);`
|
|
433
|
+
: `$client->request('${request.method}', '${request.url}');`;
|
|
434
|
+
|
|
435
|
+
return `${client}\n\n${call}`;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** Laravel's `Http` facade, which is Guzzle with the ceremony removed. */
|
|
439
|
+
export function openApiLaravelHttp(request: DuxtOpenApiRequest): string {
|
|
440
|
+
const verb = request.method.toLowerCase();
|
|
441
|
+
const data = request.body ? asJson(request.body) : undefined;
|
|
442
|
+
|
|
443
|
+
const head = hasHeaders(request)
|
|
444
|
+
? `Http::withHeaders(${php(request.headers, 0)})\n ->`
|
|
445
|
+
: 'Http::';
|
|
446
|
+
|
|
447
|
+
const payload = request.body
|
|
448
|
+
? `, ${data === undefined ? php(request.body) : php(data, 1)}`
|
|
449
|
+
: '';
|
|
450
|
+
|
|
451
|
+
return `${head}${verb}('${request.url}'${payload});`;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** The curl extension, for a project with no HTTP client at all. */
|
|
455
|
+
export function openApiPhpCurl(request: DuxtOpenApiRequest): string {
|
|
456
|
+
const options = [` CURLOPT_CUSTOMREQUEST => '${request.method}'`];
|
|
457
|
+
|
|
458
|
+
if (hasHeaders(request)) {
|
|
459
|
+
const headers = Object.entries(request.headers).map(
|
|
460
|
+
([name, value]) => `${name}: ${value}`
|
|
461
|
+
);
|
|
462
|
+
options.push(` CURLOPT_HTTPHEADER => ${php(headers, 1)}`);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (request.body) {
|
|
466
|
+
options.push(` CURLOPT_POSTFIELDS => ${php(request.body)}`);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
options.push(' CURLOPT_RETURNTRANSFER => true');
|
|
470
|
+
|
|
471
|
+
return [
|
|
472
|
+
`$ch = curl_init('${request.url}');`,
|
|
473
|
+
'',
|
|
474
|
+
'curl_setopt_array($ch, [',
|
|
475
|
+
options.join(',\n') + ',',
|
|
476
|
+
']);',
|
|
477
|
+
'',
|
|
478
|
+
'curl_exec($ch);'
|
|
479
|
+
].join('\n');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Every sample the layer ships, in the order they are offered when a site takes
|
|
484
|
+
* the default.
|
|
485
|
+
*
|
|
486
|
+
* `group` is the first level of the picker and `label` the second, so a language
|
|
487
|
+
* with three clients costs one control rather than three tabs — which is what
|
|
488
|
+
* makes an open registry survivable at all.
|
|
489
|
+
*
|
|
490
|
+
* The names are proper nouns and stay untranslated, which is why nothing here is
|
|
491
|
+
* an i18n key.
|
|
492
|
+
*/
|
|
493
|
+
export const duxtRequestSamples: DuxtRequestSample[] = [
|
|
494
|
+
{
|
|
495
|
+
id: 'curl',
|
|
496
|
+
icon: 'simple-icons:curl',
|
|
497
|
+
group: 'curl',
|
|
498
|
+
label: 'curl',
|
|
499
|
+
language: 'bash',
|
|
500
|
+
generate: openApiCurl
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
id: 'fetch',
|
|
504
|
+
icon: 'vscode-icons:file-type-typescript',
|
|
505
|
+
group: 'TypeScript',
|
|
506
|
+
label: 'fetch',
|
|
507
|
+
language: 'typescript',
|
|
508
|
+
generate: openApiFetch
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
id: 'ofetch',
|
|
512
|
+
icon: 'vscode-icons:file-type-typescript',
|
|
513
|
+
group: 'TypeScript',
|
|
514
|
+
label: '$fetch',
|
|
515
|
+
language: 'typescript',
|
|
516
|
+
generate: openApiOfetch
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
id: 'use-fetch',
|
|
520
|
+
icon: 'vscode-icons:file-type-typescript',
|
|
521
|
+
group: 'TypeScript',
|
|
522
|
+
label: 'useFetch',
|
|
523
|
+
language: 'typescript',
|
|
524
|
+
generate: openApiUseFetch
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
id: 'axios',
|
|
528
|
+
icon: 'vscode-icons:file-type-typescript',
|
|
529
|
+
group: 'TypeScript',
|
|
530
|
+
label: 'axios',
|
|
531
|
+
language: 'typescript',
|
|
532
|
+
generate: openApiAxios
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
id: 'python-requests',
|
|
536
|
+
icon: 'vscode-icons:file-type-python',
|
|
537
|
+
group: 'Python',
|
|
538
|
+
label: 'requests',
|
|
539
|
+
language: 'python',
|
|
540
|
+
generate: openApiRequests
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
id: 'python-httpx',
|
|
544
|
+
icon: 'vscode-icons:file-type-python',
|
|
545
|
+
group: 'Python',
|
|
546
|
+
label: 'httpx',
|
|
547
|
+
language: 'python',
|
|
548
|
+
generate: openApiHttpx
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
id: 'python-urllib',
|
|
552
|
+
icon: 'vscode-icons:file-type-python',
|
|
553
|
+
group: 'Python',
|
|
554
|
+
label: 'urllib',
|
|
555
|
+
language: 'python',
|
|
556
|
+
generate: openApiUrllib
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
id: 'go',
|
|
560
|
+
icon: 'vscode-icons:file-type-go',
|
|
561
|
+
group: 'Go',
|
|
562
|
+
label: 'net/http',
|
|
563
|
+
language: 'go',
|
|
564
|
+
generate: openApiGo
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
id: 'php-guzzle',
|
|
568
|
+
icon: 'vscode-icons:file-type-php',
|
|
569
|
+
group: 'PHP',
|
|
570
|
+
label: 'Guzzle',
|
|
571
|
+
language: 'php',
|
|
572
|
+
generate: openApiGuzzle
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
id: 'php-laravel',
|
|
576
|
+
icon: 'vscode-icons:file-type-php',
|
|
577
|
+
group: 'PHP',
|
|
578
|
+
label: 'Laravel Http',
|
|
579
|
+
language: 'php',
|
|
580
|
+
generate: openApiLaravelHttp
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
id: 'php-curl',
|
|
584
|
+
icon: 'vscode-icons:file-type-php',
|
|
585
|
+
group: 'PHP',
|
|
586
|
+
label: 'curl extension',
|
|
587
|
+
language: 'php',
|
|
588
|
+
generate: openApiPhpCurl
|
|
589
|
+
}
|
|
590
|
+
];
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* What a site shows without saying otherwise.
|
|
594
|
+
*
|
|
595
|
+
* Seven of the twelve. `useFetch`, `axios`, `httpx`, `urllib` and PHP's curl
|
|
596
|
+
* extension are shipped but off: they are the second client for a language that
|
|
597
|
+
* already has one on screen, and a picker is for choosing, not for listing
|
|
598
|
+
* everything that exists.
|
|
599
|
+
*/
|
|
600
|
+
export const duxtDefaultRequestSamples = [
|
|
601
|
+
'curl',
|
|
602
|
+
'fetch',
|
|
603
|
+
'ofetch',
|
|
604
|
+
'python-requests',
|
|
605
|
+
'go',
|
|
606
|
+
'php-guzzle',
|
|
607
|
+
'php-laravel'
|
|
608
|
+
];
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* The samples a site offers, resolved from what it configured.
|
|
612
|
+
*
|
|
613
|
+
* ONE KEY DOES BOTH JOBS, because they are the same sentence. A string picks a
|
|
614
|
+
* shipped sample by id; an object adds one of the site's own, or replaces a
|
|
615
|
+
* shipped one by reusing its id. So a PHP API writes
|
|
616
|
+
* `['curl', 'php-guzzle', 'php-laravel']` and a site with a Ruby client writes
|
|
617
|
+
* its generator inline, in the one list that also fixes the order.
|
|
618
|
+
*
|
|
619
|
+
* Unset means the default seven. Arrays REPLACE rather than concatenate here,
|
|
620
|
+
* the same rule the rest of `duxtDefaults` follows: a configured list is the
|
|
621
|
+
* list, or a consumer could never drop `curl`.
|
|
622
|
+
*/
|
|
623
|
+
export function resolveRequestSamples(
|
|
624
|
+
configured?: (string | DuxtRequestSample)[]
|
|
625
|
+
): DuxtRequestSample[] {
|
|
626
|
+
const shipped = new Map(
|
|
627
|
+
duxtRequestSamples.map((sample) => [sample.id, sample])
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
const wanted = configured ?? duxtDefaultRequestSamples;
|
|
631
|
+
|
|
632
|
+
return wanted
|
|
633
|
+
.map((entry) => (typeof entry === 'string' ? shipped.get(entry) : entry))
|
|
634
|
+
.filter((sample): sample is DuxtRequestSample => sample !== undefined);
|
|
635
|
+
}
|