zudoku 0.35.6 → 0.37.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/README.md +1 -1
- package/dist/app/entry.server.js +5 -1
- package/dist/app/entry.server.js.map +1 -1
- package/dist/config/validators/common.d.ts +428 -428
- package/dist/config/validators/common.js +12 -7
- package/dist/config/validators/common.js.map +1 -1
- package/dist/config/validators/validate.d.ts +158 -158
- package/dist/lib/components/InlineCode.d.ts +2 -1
- package/dist/lib/components/InlineCode.js +1 -1
- package/dist/lib/components/InlineCode.js.map +1 -1
- package/dist/lib/components/Layout.js +3 -14
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/MobileTopNavigation.js +1 -1
- package/dist/lib/components/MobileTopNavigation.js.map +1 -1
- package/dist/lib/components/TopNavigation.d.ts +2 -2
- package/dist/lib/components/TopNavigation.js +9 -12
- package/dist/lib/components/TopNavigation.js.map +1 -1
- package/dist/lib/components/Zudoku.js +3 -1
- package/dist/lib/components/Zudoku.js.map +1 -1
- package/dist/lib/components/cache.d.ts +7 -0
- package/dist/lib/components/cache.js +7 -0
- package/dist/lib/components/cache.js.map +1 -1
- package/dist/lib/components/context/ViewportAnchorContext.js +3 -6
- package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
- package/dist/lib/components/context/ZudokuContext.d.ts +2 -2
- package/dist/lib/components/context/ZudokuContext.js +13 -7
- package/dist/lib/components/context/ZudokuContext.js.map +1 -1
- package/dist/lib/components/navigation/SidebarCategory.d.ts +2 -2
- package/dist/lib/components/navigation/SidebarCategory.js +10 -6
- package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
- package/dist/lib/components/navigation/SidebarItem.js +2 -2
- package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
- package/dist/lib/components/navigation/SidebarWrapper.js +1 -1
- package/dist/lib/components/navigation/SidebarWrapper.js.map +1 -1
- package/dist/lib/core/ZudokuContext.d.ts +8 -6
- package/dist/lib/core/ZudokuContext.js +4 -2
- package/dist/lib/core/ZudokuContext.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +3 -3
- package/dist/lib/hooks/useEvent.test.js +1 -1
- package/dist/lib/hooks/useEvent.test.js.map +1 -1
- package/dist/lib/oas/graphql/index.d.ts +13 -2
- package/dist/lib/oas/graphql/index.js +59 -39
- package/dist/lib/oas/graphql/index.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationList.js +19 -5
- package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/ParamInfos.js +12 -4
- package/dist/lib/plugins/openapi/ParamInfos.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js +2 -2
- package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.js +2 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.d.ts +4 -4
- package/dist/lib/plugins/openapi/graphql/gql.js +3 -3
- package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/graphql.d.ts +33 -44
- package/dist/lib/plugins/openapi/graphql/graphql.js +19 -29
- package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
- package/dist/lib/plugins/openapi/index.d.ts +5 -10
- package/dist/lib/plugins/openapi/index.js +29 -60
- package/dist/lib/plugins/openapi/index.js.map +1 -1
- package/dist/lib/plugins/openapi/interfaces.d.ts +3 -1
- package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js +3 -3
- package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/SchemaView.js +13 -6
- package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
- package/dist/lib/plugins/openapi/util/createSidebarCategory.js +5 -7
- package/dist/lib/plugins/openapi/util/createSidebarCategory.js.map +1 -1
- package/dist/lib/ui/Badge.d.ts +1 -1
- package/dist/lib/ui/Button.d.ts +1 -1
- package/dist/lib/ui/Button.js +1 -1
- package/dist/lib/ui/Button.js.map +1 -1
- package/dist/lib/ui/Command.d.ts +6 -6
- package/dist/lib/util/joinPath.d.ts +3 -0
- package/dist/lib/util/joinPath.js +3 -0
- package/dist/lib/util/joinPath.js.map +1 -1
- package/dist/lib/util/traverse.js +2 -2
- package/dist/lib/util/traverse.js.map +1 -1
- package/dist/lib/util/useScrollToAnchor.js +2 -0
- package/dist/lib/util/useScrollToAnchor.js.map +1 -1
- package/dist/vite/api/schema-codegen.js +19 -4
- package/dist/vite/api/schema-codegen.js.map +1 -1
- package/dist/vite/api/schema-codegen.test.js +61 -0
- package/dist/vite/api/schema-codegen.test.js.map +1 -1
- package/dist/vite/config.js +1 -1
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/plugin-api.js +4 -12
- package/dist/vite/plugin-api.js.map +1 -1
- package/dist/vite/plugin-docs.d.ts +1 -1
- package/dist/vite/plugin-docs.js +18 -1
- package/dist/vite/plugin-docs.js.map +1 -1
- package/lib/{AuthenticationPlugin-4ip08maU.js → AuthenticationPlugin-Cij2tPWa.js} +2 -2
- package/lib/{AuthenticationPlugin-4ip08maU.js.map → AuthenticationPlugin-Cij2tPWa.js.map} +1 -1
- package/lib/{Spinner-C6n4eOvh.js → Button-Fp19CMUr.js} +15 -18
- package/lib/Button-Fp19CMUr.js.map +1 -0
- package/lib/{Markdown-C0eXdzGn.js → Markdown-DT5Rrq8_.js} +3526 -3264
- package/lib/Markdown-DT5Rrq8_.js.map +1 -0
- package/lib/{MdxPage-BKkG1cm1.js → MdxPage-D2rD1vC4.js} +3 -3
- package/lib/{MdxPage-BKkG1cm1.js.map → MdxPage-D2rD1vC4.js.map} +1 -1
- package/lib/{OasProvider-CwhKwrwl.js → OasProvider-DdEBf2qS.js} +3 -3
- package/lib/{OasProvider-CwhKwrwl.js.map → OasProvider-DdEBf2qS.js.map} +1 -1
- package/lib/{OperationList-DGYoFitT.js → OperationList-DT4-gm_S.js} +1122 -1093
- package/lib/OperationList-DT4-gm_S.js.map +1 -0
- package/lib/{Select-FAYHOYTy.js → Select-z1Lwl0-J.js} +3 -3
- package/lib/{Select-FAYHOYTy.js.map → Select-z1Lwl0-J.js.map} +1 -1
- package/lib/{SlotletProvider-BJC58V32.js → SlotletProvider-D8OBnr77.js} +2 -2
- package/lib/{SlotletProvider-BJC58V32.js.map → SlotletProvider-D8OBnr77.js.map} +1 -1
- package/lib/Spinner-CE68iCm0.js +7 -0
- package/lib/Spinner-CE68iCm0.js.map +1 -0
- package/lib/{circular-v7K6lDDh.js → circular-ByJI6Mci.js} +4887 -4419
- package/lib/circular-ByJI6Mci.js.map +1 -0
- package/lib/{createServer-CbL1Uh2Q.js → createServer-DjgKDpGV.js} +3301 -3747
- package/lib/createServer-DjgKDpGV.js.map +1 -0
- package/lib/{hook-CfCFKZ-2.js → hook-DzQC8PzJ.js} +78 -73
- package/lib/hook-DzQC8PzJ.js.map +1 -0
- package/lib/{index-Dm1QJHVl.js → index-DdQSV2RF.js} +593 -633
- package/lib/index-DdQSV2RF.js.map +1 -0
- package/lib/{useQuery-CQUwWR9i.js → joinUrl-BjDooT-T.js} +240 -223
- package/lib/joinUrl-BjDooT-T.js.map +1 -0
- package/lib/{mutation-B81DztCT.js → mutation-_Z5C2wFZ.js} +2 -2
- package/lib/{mutation-B81DztCT.js.map → mutation-_Z5C2wFZ.js.map} +1 -1
- package/lib/post-processors/traverse.js +2 -2
- package/lib/post-processors/traverse.js.map +1 -1
- package/lib/ui/ActionButton.js +11 -10
- package/lib/ui/ActionButton.js.map +1 -1
- package/lib/ui/Button.js +1 -1
- package/lib/ui/Button.js.map +1 -1
- package/lib/zudoku.auth-auth0.js +1 -1
- package/lib/zudoku.auth-clerk.js +2 -2
- package/lib/zudoku.auth-openid.js +3 -3
- package/lib/zudoku.components.js +438 -444
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.hooks.js +1 -1
- package/lib/zudoku.plugin-api-catalog.js +3 -3
- package/lib/zudoku.plugin-api-keys.js +4 -4
- package/lib/zudoku.plugin-custom-pages.js +1 -1
- package/lib/zudoku.plugin-markdown.js +1 -1
- package/lib/zudoku.plugin-openapi.js +5 -6
- package/lib/zudoku.plugin-openapi.js.map +1 -1
- package/lib/zudoku.plugin-search-pagefind.js +15 -16
- package/lib/zudoku.plugin-search-pagefind.js.map +1 -1
- package/lib/zudoku.plugins.js.map +1 -1
- package/package.json +3 -1
- package/src/app/entry.server.tsx +7 -1
- package/src/lib/components/InlineCode.tsx +3 -1
- package/src/lib/components/Layout.tsx +3 -16
- package/src/lib/components/MobileTopNavigation.tsx +1 -1
- package/src/lib/components/TopNavigation.tsx +12 -16
- package/src/lib/components/Zudoku.tsx +5 -1
- package/src/lib/components/cache.ts +8 -0
- package/src/lib/components/context/ViewportAnchorContext.tsx +3 -6
- package/src/lib/components/context/ZudokuContext.ts +17 -8
- package/src/lib/components/navigation/SidebarCategory.tsx +15 -12
- package/src/lib/components/navigation/SidebarItem.tsx +2 -2
- package/src/lib/components/navigation/SidebarWrapper.tsx +2 -2
- package/src/lib/core/ZudokuContext.ts +11 -8
- package/src/lib/core/plugins.ts +4 -4
- package/src/lib/hooks/useEvent.test.tsx +1 -1
- package/src/lib/oas/graphql/index.ts +104 -64
- package/src/lib/plugins/openapi/OperationList.tsx +30 -36
- package/src/lib/plugins/openapi/OperationListItem.tsx +1 -1
- package/src/lib/plugins/openapi/ParamInfos.tsx +27 -4
- package/src/lib/plugins/openapi/ParameterListItem.tsx +5 -1
- package/src/lib/plugins/openapi/Sidecar.tsx +2 -2
- package/src/lib/plugins/openapi/client/useCreateQuery.ts +2 -1
- package/src/lib/plugins/openapi/graphql/gql.ts +17 -17
- package/src/lib/plugins/openapi/graphql/graphql.ts +57 -75
- package/src/lib/plugins/openapi/index.tsx +40 -84
- package/src/lib/plugins/openapi/interfaces.ts +4 -1
- package/src/lib/plugins/openapi/schema/SchemaPropertyItem.tsx +5 -2
- package/src/lib/plugins/openapi/schema/SchemaView.tsx +48 -35
- package/src/lib/plugins/openapi/util/createSidebarCategory.tsx +5 -7
- package/src/lib/ui/Button.tsx +1 -1
- package/src/lib/util/joinPath.tsx +3 -0
- package/src/lib/util/traverse.ts +2 -2
- package/src/lib/util/useScrollToAnchor.ts +2 -0
- package/lib/Markdown-C0eXdzGn.js.map +0 -1
- package/lib/OperationList-DGYoFitT.js.map +0 -1
- package/lib/Spinner-C6n4eOvh.js.map +0 -1
- package/lib/circular-v7K6lDDh.js.map +0 -1
- package/lib/createServer-CbL1Uh2Q.js.map +0 -1
- package/lib/hook-CfCFKZ-2.js.map +0 -1
- package/lib/index-Dm1QJHVl.js.map +0 -1
- package/lib/joinUrl-10po2Jdj.js +0 -20
- package/lib/joinUrl-10po2Jdj.js.map +0 -1
- package/lib/useQuery-CQUwWR9i.js.map +0 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
2
|
+
import { deepEqual } from "fast-equals";
|
|
2
3
|
import { ChevronRightIcon } from "lucide-react";
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
4
|
+
import { memo, useEffect, useState } from "react";
|
|
4
5
|
import { NavLink, useMatch } from "react-router";
|
|
6
|
+
import { Button } from "zudoku/ui/Button.js";
|
|
5
7
|
import type { SidebarItemCategory } from "../../../config/validators/SidebarSchema.js";
|
|
6
8
|
import { cn } from "../../util/cn.js";
|
|
7
9
|
import { joinPath } from "../../util/joinPath.js";
|
|
8
10
|
import { navigationListItem, SidebarItem } from "./SidebarItem.js";
|
|
9
11
|
import { useIsCategoryOpen } from "./utils.js";
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
const SidebarCategoryInner = ({
|
|
12
14
|
category,
|
|
13
15
|
onRequestClose,
|
|
14
16
|
}: {
|
|
@@ -35,13 +37,15 @@ export const SidebarCategory = ({
|
|
|
35
37
|
}, [isCategoryOpen]);
|
|
36
38
|
|
|
37
39
|
const ToggleButton = isCollapsible && (
|
|
38
|
-
<
|
|
39
|
-
type="button"
|
|
40
|
+
<Button
|
|
40
41
|
onClick={(e) => {
|
|
41
42
|
e.preventDefault();
|
|
42
43
|
setOpen((prev) => !prev);
|
|
43
44
|
setHasInteracted(true);
|
|
44
45
|
}}
|
|
46
|
+
variant="ghost"
|
|
47
|
+
size="icon"
|
|
48
|
+
className="size-6 hover:bg-[hsl(from_hsl(var(--accent))_h_s_calc(l-5))] hover:dark:bg-[hsl(from_hsl(var(--accent))_h_s_calc(l+5))]"
|
|
45
49
|
>
|
|
46
50
|
<ChevronRightIcon
|
|
47
51
|
size={16}
|
|
@@ -50,7 +54,7 @@ export const SidebarCategory = ({
|
|
|
50
54
|
"shrink-0 group-data-[state=open]:rotate-90",
|
|
51
55
|
)}
|
|
52
56
|
/>
|
|
53
|
-
</
|
|
57
|
+
</Button>
|
|
54
58
|
);
|
|
55
59
|
|
|
56
60
|
const icon = category.icon && (
|
|
@@ -62,7 +66,7 @@ export const SidebarCategory = ({
|
|
|
62
66
|
|
|
63
67
|
const styles = navigationListItem({
|
|
64
68
|
className: [
|
|
65
|
-
"text-start font-medium",
|
|
69
|
+
"group text-start font-medium",
|
|
66
70
|
isCollapsible || typeof category.link !== "undefined"
|
|
67
71
|
? "cursor-pointer"
|
|
68
72
|
: "cursor-default hover:bg-transparent",
|
|
@@ -90,12 +94,7 @@ export const SidebarCategory = ({
|
|
|
90
94
|
}}
|
|
91
95
|
>
|
|
92
96
|
{icon}
|
|
93
|
-
<div
|
|
94
|
-
className={cn(
|
|
95
|
-
"flex items-center gap-2 justify-between w-full",
|
|
96
|
-
isActive ? "text-primary" : "text-foreground/80",
|
|
97
|
-
)}
|
|
98
|
-
>
|
|
97
|
+
<div className="flex items-center gap-2 justify-between w-full text-foreground/80 group-aria-[current='page']:text-primary">
|
|
99
98
|
<div className="truncate">{category.label}</div>
|
|
100
99
|
{ToggleButton}
|
|
101
100
|
</div>
|
|
@@ -135,3 +134,7 @@ export const SidebarCategory = ({
|
|
|
135
134
|
</Collapsible.Root>
|
|
136
135
|
);
|
|
137
136
|
};
|
|
137
|
+
|
|
138
|
+
export const SidebarCategory = memo(SidebarCategoryInner, deepEqual);
|
|
139
|
+
|
|
140
|
+
SidebarCategory.displayName = "SidebarCategory";
|
|
@@ -10,11 +10,11 @@ import { SidebarBadge } from "./SidebarBadge.js";
|
|
|
10
10
|
import { SidebarCategory } from "./SidebarCategory.js";
|
|
11
11
|
|
|
12
12
|
export const navigationListItem = cva(
|
|
13
|
-
"flex items-center gap-2 px-[--padding-nav-item] py-1.5 rounded-lg hover:bg-accent",
|
|
13
|
+
"flex items-center gap-2 px-[--padding-nav-item] my-0.5 py-1.5 rounded-lg hover:bg-accent",
|
|
14
14
|
{
|
|
15
15
|
variants: {
|
|
16
16
|
isActive: {
|
|
17
|
-
true: "
|
|
17
|
+
true: "bg-accent font-medium",
|
|
18
18
|
false: "text-foreground/80",
|
|
19
19
|
},
|
|
20
20
|
isMuted: {
|
|
@@ -11,9 +11,9 @@ export const SidebarWrapper = ({
|
|
|
11
11
|
}>) => (
|
|
12
12
|
<nav
|
|
13
13
|
className={cn(
|
|
14
|
-
"hidden lg:flex h-full scrollbar
|
|
14
|
+
"hidden lg:flex h-full scrollbar flex-col overflow-y-auto shrink-0 text-sm border-r pr-6",
|
|
15
15
|
"sticky top-[--header-height] h-[calc(100vh-var(--header-height))]",
|
|
16
|
-
"-mx-[--padding-nav-item] max-w-[--side-nav-width] pb-
|
|
16
|
+
"-mx-[--padding-nav-item] max-w-[--side-nav-width] pb-6 pt-[--padding-content-top] scroll-pt-2 gap-2",
|
|
17
17
|
className,
|
|
18
18
|
)}
|
|
19
19
|
ref={ref}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import type { QueryClient } from "@tanstack/react-query";
|
|
1
2
|
import { createNanoEvents } from "nanoevents";
|
|
2
|
-
import { ReactNode } from "react";
|
|
3
|
-
import { Location } from "react-router";
|
|
4
|
-
import { TopNavigationItem } from "../../config/validators/common.js";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type { Location } from "react-router";
|
|
5
|
+
import type { TopNavigationItem } from "../../config/validators/common.js";
|
|
5
6
|
import type { SidebarConfig } from "../../config/validators/SidebarSchema.js";
|
|
6
|
-
import {
|
|
7
|
+
import type { AuthenticationProvider } from "../authentication/authentication.js";
|
|
7
8
|
import type { ComponentsContextType } from "../components/context/ComponentsContext.js";
|
|
8
|
-
import { Slotlets } from "../components/SlotletProvider.js";
|
|
9
|
+
import type { Slotlets } from "../components/SlotletProvider.js";
|
|
9
10
|
import { joinPath } from "../util/joinPath.js";
|
|
10
11
|
import type { MdxComponentsType } from "../util/MdxComponents.js";
|
|
11
12
|
import { objectEntries } from "../util/objectEntries.js";
|
|
@@ -90,7 +91,10 @@ export class ZudokuContext {
|
|
|
90
91
|
private readonly navigationPlugins: NavigationPlugin[];
|
|
91
92
|
private emitter = createNanoEvents<ZudokuEvents>();
|
|
92
93
|
|
|
93
|
-
constructor(
|
|
94
|
+
constructor(
|
|
95
|
+
public readonly options: ZudokuContextOptions,
|
|
96
|
+
public readonly queryClient: QueryClient,
|
|
97
|
+
) {
|
|
94
98
|
this.plugins = options.plugins ?? [];
|
|
95
99
|
this.topNavigation = options.topNavigation ?? [];
|
|
96
100
|
this.sidebars = options.sidebars ?? {};
|
|
@@ -98,7 +102,6 @@ export class ZudokuContext {
|
|
|
98
102
|
this.authentication = options.authentication;
|
|
99
103
|
this.meta = options.metadata;
|
|
100
104
|
this.page = options.page;
|
|
101
|
-
|
|
102
105
|
this.plugins.forEach((plugin) => {
|
|
103
106
|
if (!isEventConsumerPlugin(plugin)) return;
|
|
104
107
|
|
|
@@ -143,7 +146,7 @@ export class ZudokuContext {
|
|
|
143
146
|
getPluginSidebar = async (path: string) => {
|
|
144
147
|
const navigations = await Promise.all(
|
|
145
148
|
this.navigationPlugins.map((plugin) =>
|
|
146
|
-
plugin.getSidebar?.(joinPath(path)),
|
|
149
|
+
plugin.getSidebar?.(joinPath(path), this),
|
|
147
150
|
),
|
|
148
151
|
);
|
|
149
152
|
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -2,11 +2,11 @@ import type { LucideIcon } from "lucide-react";
|
|
|
2
2
|
import { type ReactElement } from "react";
|
|
3
3
|
import { type RouteObject } from "react-router";
|
|
4
4
|
import type { Sidebar } from "../../config/validators/SidebarSchema.js";
|
|
5
|
-
import { MdxComponentsType } from "../util/MdxComponents.js";
|
|
5
|
+
import { type MdxComponentsType } from "../util/MdxComponents.js";
|
|
6
6
|
import {
|
|
7
|
-
ZudokuContext,
|
|
8
|
-
ZudokuEvents,
|
|
9
7
|
type ApiIdentity,
|
|
8
|
+
type ZudokuContext,
|
|
9
|
+
type ZudokuEvents,
|
|
10
10
|
} from "./ZudokuContext.js";
|
|
11
11
|
|
|
12
12
|
export type ZudokuPlugin =
|
|
@@ -21,7 +21,7 @@ export type { RouteObject };
|
|
|
21
21
|
|
|
22
22
|
export interface NavigationPlugin {
|
|
23
23
|
getRoutes: () => RouteObject[];
|
|
24
|
-
getSidebar?: (path: string) => Promise<Sidebar>;
|
|
24
|
+
getSidebar?: (path: string, context: ZudokuContext) => Promise<Sidebar>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const createApiIdentityPlugin = (
|
|
@@ -12,8 +12,8 @@ import { useEvent } from "./useEvent.js";
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const createTestContext = () => {
|
|
15
|
-
const context = new ZudokuContext({});
|
|
16
15
|
const queryClient = new QueryClient();
|
|
16
|
+
const context = new ZudokuContext({}, queryClient);
|
|
17
17
|
const wrapper = ({ children }: PropsWithChildren) => (
|
|
18
18
|
<QueryClientProvider client={queryClient}>
|
|
19
19
|
<ZudokuProvider context={context}>{children}</ZudokuProvider>
|
|
@@ -51,17 +51,19 @@ export const createOperationSlug = (
|
|
|
51
51
|
return slugify(summary);
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
type SchemaImport = () => Promise<{
|
|
54
|
+
export type SchemaImport = () => Promise<{
|
|
55
|
+
schema: OpenAPIDocument;
|
|
56
|
+
slugs: ReturnType<typeof getAllSlugs>;
|
|
57
|
+
}>;
|
|
55
58
|
|
|
56
59
|
export type SchemaImports = Record<string, SchemaImport>;
|
|
57
60
|
|
|
58
61
|
type Context = {
|
|
59
62
|
schema: OpenAPIDocument;
|
|
60
63
|
operations: GraphQLOperationObject[];
|
|
61
|
-
tags: TagObject[];
|
|
62
64
|
schemaImports?: SchemaImports;
|
|
63
|
-
|
|
64
|
-
slugs:
|
|
65
|
+
tags: ReturnType<typeof getAllTags>;
|
|
66
|
+
slugs: ReturnType<typeof getAllSlugs>;
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
const builder = new SchemaBuilder<{
|
|
@@ -87,17 +89,15 @@ const JSONScalar = builder.addScalarType("JSON", GraphQLJSON);
|
|
|
87
89
|
const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
|
|
88
90
|
const JSONSchemaScalar = builder.addScalarType("JSONSchema", GraphQLJSONSchema);
|
|
89
91
|
|
|
90
|
-
const resolveExtensions = (obj: Record<string, any>) =>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
extensions[key] = value;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return extensions;
|
|
98
|
-
};
|
|
92
|
+
const resolveExtensions = (obj: Record<string, any>) =>
|
|
93
|
+
Object.fromEntries(
|
|
94
|
+
Object.entries(obj).filter(([key]) => key.startsWith("x-")),
|
|
95
|
+
);
|
|
99
96
|
|
|
100
|
-
export const getAllTags = (
|
|
97
|
+
export const getAllTags = (
|
|
98
|
+
schema: OpenAPIDocument,
|
|
99
|
+
slugs: ReturnType<typeof getAllSlugs>["tags"],
|
|
100
|
+
): Array<TagObject & { slug?: string }> => {
|
|
101
101
|
const rootTags = schema.tags ?? [];
|
|
102
102
|
const operationTags = new Set(
|
|
103
103
|
Object.values(schema.paths ?? {})
|
|
@@ -107,24 +107,42 @@ export const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
|
|
|
107
107
|
|
|
108
108
|
return [
|
|
109
109
|
// Keep root tags that are actually used in operations
|
|
110
|
-
...rootTags
|
|
110
|
+
...rootTags
|
|
111
|
+
.filter((tag) => operationTags.has(tag.name))
|
|
112
|
+
.map((tag) => ({ ...tag, slug: slugs[tag.name] })),
|
|
111
113
|
// Add tags found in operations but not defined in root tags
|
|
112
114
|
...[...operationTags]
|
|
113
115
|
.filter((tag) => !rootTags.some((rt) => rt.name === tag))
|
|
114
|
-
.map((tag) => ({ name: tag })),
|
|
116
|
+
.map((tag) => ({ name: tag, slug: slugs[tag] })),
|
|
115
117
|
];
|
|
116
118
|
};
|
|
117
119
|
|
|
118
|
-
const getAllSlugs = (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
export const getAllSlugs = (
|
|
121
|
+
ops: GraphQLOperationObject[],
|
|
122
|
+
schemaTags: TagObject[] = [],
|
|
123
|
+
) => {
|
|
124
|
+
const slugify = slugifyWithCounter();
|
|
125
|
+
|
|
126
|
+
const tags = Array.from(
|
|
127
|
+
new Set([
|
|
128
|
+
...ops.flatMap((op) => op.tags ?? []),
|
|
129
|
+
...schemaTags.map((tag) => tag.name),
|
|
123
130
|
]),
|
|
124
131
|
);
|
|
125
132
|
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
return {
|
|
134
|
+
operations: Object.fromEntries(
|
|
135
|
+
ops.map((op) => [
|
|
136
|
+
getOperationSlugKey(op),
|
|
137
|
+
createOperationSlug(slugify, op),
|
|
138
|
+
]),
|
|
139
|
+
),
|
|
140
|
+
tags: Object.fromEntries(tags.map((tag) => [tag, slugify(tag)])),
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const getOperationSlugKey = (op: GraphQLOperationObject) =>
|
|
145
|
+
[op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
|
|
128
146
|
|
|
129
147
|
export const getAllOperations = (
|
|
130
148
|
paths?: PathsObject,
|
|
@@ -163,30 +181,38 @@ export const getAllOperations = (
|
|
|
163
181
|
return operations;
|
|
164
182
|
};
|
|
165
183
|
|
|
166
|
-
const SchemaTag = builder
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
const SchemaTag = builder
|
|
185
|
+
.objectRef<
|
|
186
|
+
Omit<TagObject, "name"> & { name?: string; slug?: string }
|
|
187
|
+
>("SchemaTag")
|
|
188
|
+
.implement({
|
|
189
|
+
fields: (t) => ({
|
|
190
|
+
name: t.exposeString("name", { nullable: true }),
|
|
191
|
+
slug: t.exposeString("slug", { nullable: true }),
|
|
192
|
+
description: t.exposeString("description", { nullable: true }),
|
|
193
|
+
operations: t.field({
|
|
194
|
+
type: [OperationItem],
|
|
195
|
+
resolve: (parent, _args, ctx) => {
|
|
196
|
+
const rootTags = ctx.tags.map((tag) => tag.name);
|
|
197
|
+
|
|
198
|
+
return ctx.operations
|
|
199
|
+
.filter((item) =>
|
|
200
|
+
parent.name
|
|
201
|
+
? item.tags?.includes(parent.name)
|
|
202
|
+
: item.tags?.length === 0 ||
|
|
203
|
+
// If none of the tags are present in the root tags, then show them here
|
|
204
|
+
item.tags?.every((tag) => !rootTags.includes(tag)),
|
|
205
|
+
)
|
|
206
|
+
.map((item) => ({ ...item, parentTag: parent.name }));
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
extensions: t.field({
|
|
210
|
+
type: JSONObjectScalar,
|
|
211
|
+
resolve: (parent) => resolveExtensions(parent),
|
|
212
|
+
nullable: true,
|
|
213
|
+
}),
|
|
187
214
|
}),
|
|
188
|
-
})
|
|
189
|
-
});
|
|
215
|
+
});
|
|
190
216
|
|
|
191
217
|
const ServerItem = builder.objectRef<ServerObject>("Server").implement({
|
|
192
218
|
fields: (t) => ({
|
|
@@ -350,7 +376,16 @@ const OperationItem = builder
|
|
|
350
376
|
fields: (t) => ({
|
|
351
377
|
slug: t.field({
|
|
352
378
|
type: "String",
|
|
353
|
-
resolve: (parent, _, ctx) =>
|
|
379
|
+
resolve: (parent, _, ctx) => {
|
|
380
|
+
const slug = ctx.slugs.operations[getOperationSlugKey(parent)];
|
|
381
|
+
|
|
382
|
+
if (!slug) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`No slug found for operation: ${getOperationSlugKey(parent)}`,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
return slug;
|
|
388
|
+
},
|
|
354
389
|
}),
|
|
355
390
|
path: t.exposeString("path"),
|
|
356
391
|
method: t.exposeString("method"),
|
|
@@ -465,10 +500,17 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
|
|
|
465
500
|
name: t.arg.string(),
|
|
466
501
|
},
|
|
467
502
|
type: [SchemaTag],
|
|
468
|
-
resolve: (
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
503
|
+
resolve: (_root, args, ctx) => {
|
|
504
|
+
if (args.name) {
|
|
505
|
+
return ctx.tags.filter((tag) => tag.name === args.name);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Append empty tag which will be used to display untagged operations
|
|
509
|
+
if (ctx.operations.some((op) => !op.tags?.length)) {
|
|
510
|
+
return [...ctx.tags, { name: undefined, slug: undefined }];
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return ctx.tags;
|
|
472
514
|
},
|
|
473
515
|
}),
|
|
474
516
|
operations: t.field({
|
|
@@ -512,27 +554,25 @@ builder.queryType({
|
|
|
512
554
|
input: t.arg({ type: JSONScalar, required: true }),
|
|
513
555
|
},
|
|
514
556
|
resolve: async (_, args, ctx) => {
|
|
515
|
-
let schema: OpenAPIDocument;
|
|
516
|
-
|
|
517
557
|
if (args.type === "file" && typeof args.input === "string") {
|
|
518
558
|
const loadSchema = ctx.schemaImports?.[args.input];
|
|
519
559
|
|
|
520
560
|
if (!loadSchema) {
|
|
521
561
|
throw new Error(`No schema loader found for path: ${args.input}`);
|
|
522
562
|
}
|
|
523
|
-
const
|
|
524
|
-
schema =
|
|
563
|
+
const { schema, slugs } = await loadSchema();
|
|
564
|
+
ctx.schema = schema;
|
|
565
|
+
ctx.operations = getAllOperations(schema.paths);
|
|
566
|
+
ctx.slugs = slugs;
|
|
567
|
+
ctx.tags = getAllTags(schema, ctx.slugs.tags);
|
|
525
568
|
} else {
|
|
526
|
-
schema = await validate(args.input as string);
|
|
569
|
+
ctx.schema = await validate(args.input as string);
|
|
570
|
+
ctx.operations = getAllOperations(ctx.schema.paths);
|
|
571
|
+
ctx.slugs = getAllSlugs(ctx.operations);
|
|
572
|
+
ctx.tags = getAllTags(ctx.schema, ctx.slugs.tags);
|
|
527
573
|
}
|
|
528
574
|
|
|
529
|
-
ctx.schema
|
|
530
|
-
ctx.operations = getAllOperations(schema.paths);
|
|
531
|
-
ctx.slugify = slugifyWithCounter();
|
|
532
|
-
ctx.tags = getAllTags(schema);
|
|
533
|
-
ctx.slugs = getAllSlugs(ctx.operations);
|
|
534
|
-
|
|
535
|
-
return schema;
|
|
575
|
+
return ctx.schema;
|
|
536
576
|
},
|
|
537
577
|
}),
|
|
538
578
|
}),
|
|
@@ -542,4 +582,4 @@ export const schema = builder.toSchema();
|
|
|
542
582
|
|
|
543
583
|
export const createGraphQLServer = (
|
|
544
584
|
options?: Omit<YogaServerOptions<any, any>, "schema">,
|
|
545
|
-
) => createYoga({ schema, ...options });
|
|
585
|
+
) => createYoga({ schema, batching: true, ...options });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ResultOf } from "@graphql-typed-document-node/core";
|
|
2
|
-
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
2
|
+
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
|
|
3
3
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
4
4
|
import { ChevronsDownUpIcon, ChevronsUpDownIcon } from "lucide-react";
|
|
5
5
|
import { useNavigate } from "react-router";
|
|
@@ -97,8 +97,16 @@ export const OperationsFragment = graphql(/* GraphQL */ `
|
|
|
97
97
|
|
|
98
98
|
export type OperationListItemResult = ResultOf<typeof OperationsFragment>;
|
|
99
99
|
|
|
100
|
-
const
|
|
101
|
-
query
|
|
100
|
+
const SchemaWarmupQuery = graphql(/* GraphQL */ `
|
|
101
|
+
query SchemaWarmup($input: JSON!, $type: SchemaType!) {
|
|
102
|
+
schema(input: $input, type: $type) {
|
|
103
|
+
openapi
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
`);
|
|
107
|
+
|
|
108
|
+
const OperationsForTagQuery = graphql(/* GraphQL */ `
|
|
109
|
+
query OperationsForTag(
|
|
102
110
|
$input: JSON!
|
|
103
111
|
$type: SchemaType!
|
|
104
112
|
$tag: String
|
|
@@ -133,7 +141,7 @@ export const OperationList = ({
|
|
|
133
141
|
untagged?: boolean;
|
|
134
142
|
}) => {
|
|
135
143
|
const { input, type, versions, version, options } = useOasConfig();
|
|
136
|
-
const query = useCreateQuery(
|
|
144
|
+
const query = useCreateQuery(OperationsForTagQuery, {
|
|
137
145
|
input,
|
|
138
146
|
type,
|
|
139
147
|
tag,
|
|
@@ -151,6 +159,14 @@ export const OperationList = ({
|
|
|
151
159
|
const operations = schema.operations;
|
|
152
160
|
const tagDescription = schema.tags.find((t) => t.name === tag)?.description;
|
|
153
161
|
|
|
162
|
+
// This is to warmup (i.e. load the schema in the background) the schema on the client, if the page has been rendered on the server
|
|
163
|
+
const warmupQuery = useCreateQuery(SchemaWarmupQuery, { input, type });
|
|
164
|
+
useQuery({
|
|
165
|
+
...warmupQuery,
|
|
166
|
+
enabled: typeof window !== "undefined",
|
|
167
|
+
notifyOnChangeProps: [],
|
|
168
|
+
});
|
|
169
|
+
|
|
154
170
|
// Prefetch for Playground
|
|
155
171
|
useApiIdentities();
|
|
156
172
|
|
|
@@ -269,38 +285,16 @@ export const OperationList = ({
|
|
|
269
285
|
<div className="my-4 flex items-center justify-end gap-4">
|
|
270
286
|
<Endpoint />
|
|
271
287
|
</div>
|
|
272
|
-
{
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
// px, -mx is so that `content-visibility` doesn't cut off overflown heading anchor links '#'
|
|
283
|
-
<div key={tag.name} className="px-6 -mx-6 [content-visibility:auto]">
|
|
284
|
-
{tag.name && <CategoryHeading>{tag.name}</CategoryHeading>}
|
|
285
|
-
{tag.description && (
|
|
286
|
-
<Markdown
|
|
287
|
-
className={`${ProseClasses} max-w-full prose-img:max-w-prose w-full mt-2 mb-12`}
|
|
288
|
-
content={tag.description}
|
|
289
|
-
/>
|
|
290
|
-
)}
|
|
291
|
-
<div className="operation mb-12">
|
|
292
|
-
<StaggeredRender>
|
|
293
|
-
{tag.operations.map((fragment) => (
|
|
294
|
-
<OperationListItem
|
|
295
|
-
serverUrl={selectedServer ?? schema.url}
|
|
296
|
-
key={fragment.slug}
|
|
297
|
-
operationFragment={fragment}
|
|
298
|
-
/>
|
|
299
|
-
))}
|
|
300
|
-
</StaggeredRender>
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
))} */}
|
|
288
|
+
{/* px, -mx is so that `content-visibility` doesn't cut off overflown heading anchor links '#' */}
|
|
289
|
+
<div className="px-6 -mx-6 [content-visibility:auto]">
|
|
290
|
+
{operations.map((fragment) => (
|
|
291
|
+
<OperationListItem
|
|
292
|
+
serverUrl={selectedServer}
|
|
293
|
+
key={fragment.slug}
|
|
294
|
+
operationFragment={fragment}
|
|
295
|
+
/>
|
|
296
|
+
))}
|
|
297
|
+
</div>
|
|
304
298
|
</div>
|
|
305
299
|
);
|
|
306
300
|
};
|
|
@@ -63,7 +63,7 @@ export const OperationListItem = ({
|
|
|
63
63
|
<SelectOnClick className="max-w-full truncate flex cursor-pointer">
|
|
64
64
|
{serverUrl && (
|
|
65
65
|
<div className="text-neutral-400 dark:text-neutral-500 truncate">
|
|
66
|
-
{serverUrl}
|
|
66
|
+
{serverUrl.replace(/\/$/, "")}
|
|
67
67
|
</div>
|
|
68
68
|
)}
|
|
69
69
|
<div className="text-neutral-900 dark:text-neutral-200">
|
|
@@ -1,6 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ChevronsLeftRightIcon } from "lucide-react";
|
|
2
|
+
import { isValidElement, useState } from "react";
|
|
2
3
|
import { InlineCode } from "../../components/InlineCode.js";
|
|
3
4
|
import { type SchemaObject } from "../../oas/parser/index.js";
|
|
5
|
+
import { cn } from "../../util/cn.js";
|
|
6
|
+
|
|
7
|
+
const Pattern = ({ pattern }: { pattern: string }) => {
|
|
8
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
9
|
+
const isExpandable = pattern.length > 20;
|
|
10
|
+
const shortPattern = isExpandable ? `${pattern.slice(0, 20)}…` : pattern;
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<InlineCode
|
|
14
|
+
className={cn("text-xs", isExpandable && "cursor-pointer")}
|
|
15
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
16
|
+
selectOnClick={false}
|
|
17
|
+
>
|
|
18
|
+
{isExpanded ? pattern : shortPattern}
|
|
19
|
+
{isExpandable && (
|
|
20
|
+
<button type="button" className="p-1 translate-y-[2px]">
|
|
21
|
+
{!isExpanded && <ChevronsLeftRightIcon size={12} />}
|
|
22
|
+
</button>
|
|
23
|
+
)}
|
|
24
|
+
</InlineCode>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
4
27
|
|
|
5
28
|
const getSchemaInfos = (schema?: SchemaObject) => {
|
|
6
29
|
if (!schema) return [];
|
|
@@ -28,7 +51,7 @@ const getSchemaInfos = (schema?: SchemaObject) => {
|
|
|
28
51
|
schema.deprecated && "deprecated",
|
|
29
52
|
schema.pattern && (
|
|
30
53
|
<>
|
|
31
|
-
pattern: <
|
|
54
|
+
pattern: <Pattern pattern={schema.pattern} />
|
|
32
55
|
</>
|
|
33
56
|
),
|
|
34
57
|
];
|
|
@@ -48,7 +71,7 @@ export const ParamInfos = ({
|
|
|
48
71
|
);
|
|
49
72
|
|
|
50
73
|
return (
|
|
51
|
-
<
|
|
74
|
+
<span className={className}>
|
|
52
75
|
{filteredItems.map((item, index) => (
|
|
53
76
|
<span className="text-muted-foreground" key={index}>
|
|
54
77
|
{item}
|
|
@@ -59,6 +82,6 @@ export const ParamInfos = ({
|
|
|
59
82
|
)}
|
|
60
83
|
</span>
|
|
61
84
|
))}
|
|
62
|
-
</
|
|
85
|
+
</span>
|
|
63
86
|
);
|
|
64
87
|
};
|
|
@@ -67,7 +67,11 @@ export const ParameterListItem = ({
|
|
|
67
67
|
className="text-sm prose-p:my-1 prose-code:whitespace-pre-line"
|
|
68
68
|
/>
|
|
69
69
|
)}
|
|
70
|
-
{paramSchema.
|
|
70
|
+
{paramSchema.type === "array" && paramSchema.items.enum ? (
|
|
71
|
+
<EnumValues values={paramSchema.items.enum} />
|
|
72
|
+
) : (
|
|
73
|
+
paramSchema.enum && <EnumValues values={paramSchema.enum} />
|
|
74
|
+
)}
|
|
71
75
|
</li>
|
|
72
76
|
);
|
|
73
77
|
};
|
|
@@ -173,9 +173,9 @@ export const Sidecar = ({
|
|
|
173
173
|
const showPlayground =
|
|
174
174
|
isOnScreen &&
|
|
175
175
|
(operation.extensions["x-explorer-enabled"] === true ||
|
|
176
|
-
operation.extensions["x-playground-enabled"] === true ||
|
|
176
|
+
operation.extensions["x-zudoku-playground-enabled"] === true ||
|
|
177
177
|
(operation.extensions["x-explorer-enabled"] === undefined &&
|
|
178
|
-
operation.extensions["x-playground-enabled"] === undefined &&
|
|
178
|
+
operation.extensions["x-zudoku-playground-enabled"] === undefined &&
|
|
179
179
|
!options?.disablePlayground));
|
|
180
180
|
|
|
181
181
|
return (
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stripIgnoredCharacters } from "graphql";
|
|
1
2
|
import { useContext } from "react";
|
|
2
3
|
import type { TypedDocumentString } from "../graphql/graphql.js";
|
|
3
4
|
import { GraphQLContext } from "./GraphQLContext.js";
|
|
@@ -13,6 +14,6 @@ export const useCreateQuery = <TResult, TVariables>(
|
|
|
13
14
|
|
|
14
15
|
return {
|
|
15
16
|
queryFn: () => graphQLClient.fetch(query, ...variables),
|
|
16
|
-
queryKey: [query, variables[0]],
|
|
17
|
+
queryKey: [stripIgnoredCharacters(query.toString()), variables[0]],
|
|
17
18
|
} as const;
|
|
18
19
|
};
|