zudoku 0.37.0 → 0.38.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/dist/app/main.js +2 -0
- package/dist/app/main.js.map +1 -1
- package/dist/config/validators/common.d.ts +287 -18
- package/dist/config/validators/common.js +2 -0
- package/dist/config/validators/common.js.map +1 -1
- package/dist/config/validators/validate.d.ts +107 -7
- package/dist/lib/authentication/authentication.d.ts +1 -0
- package/dist/lib/authentication/providers/clerk.js +19 -0
- package/dist/lib/authentication/providers/clerk.js.map +1 -1
- package/dist/lib/authentication/providers/openid.d.ts +1 -0
- package/dist/lib/authentication/providers/openid.js +5 -0
- package/dist/lib/authentication/providers/openid.js.map +1 -1
- package/dist/lib/authentication/providers/supabase.js +5 -0
- package/dist/lib/authentication/providers/supabase.js.map +1 -1
- package/dist/lib/authentication/state.d.ts +0 -26
- package/dist/lib/authentication/state.js +1 -16
- package/dist/lib/authentication/state.js.map +1 -1
- package/dist/lib/components/Layout.js +5 -3
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/Zudoku.js +3 -2
- package/dist/lib/components/Zudoku.js.map +1 -1
- package/dist/lib/core/ZudokuContext.d.ts +7 -0
- package/dist/lib/core/ZudokuContext.js +8 -3
- package/dist/lib/core/ZudokuContext.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +8 -6
- package/dist/lib/plugins/markdown/MdxPage.js +2 -8
- package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
- package/dist/lib/plugins/openapi/Endpoint.js +1 -1
- package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationList.js +1 -1
- package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js +29 -5
- package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
- package/dist/lib/plugins/openapi/interfaces.d.ts +26 -0
- package/dist/lib/plugins/openapi/playground/Playground.js +1 -1
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js +2 -2
- package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js.map +1 -1
- package/dist/lib/plugins/openapi/state.d.ts +25 -0
- package/dist/lib/plugins/openapi/state.js +18 -0
- package/dist/lib/plugins/openapi/state.js.map +1 -0
- package/dist/lib/plugins/search-pagefind/PagefindSearch.js +13 -4
- package/dist/lib/plugins/search-pagefind/PagefindSearch.js.map +1 -1
- package/dist/lib/plugins/search-pagefind/ResultList.js +19 -12
- package/dist/lib/plugins/search-pagefind/ResultList.js.map +1 -1
- package/dist/lib/plugins/search-pagefind/get-results.d.ts +8 -1
- package/dist/lib/plugins/search-pagefind/get-results.js +9 -4
- package/dist/lib/plugins/search-pagefind/get-results.js.map +1 -1
- package/dist/lib/util/traverse.d.ts +2 -8
- package/dist/lib/util/traverse.js.map +1 -1
- package/dist/lib/util/types.d.ts +7 -0
- package/dist/lib/util/types.js +2 -0
- package/dist/lib/util/types.js.map +1 -0
- package/dist/lib/util/useScrollToAnchor.js +18 -12
- package/dist/lib/util/useScrollToAnchor.js.map +1 -1
- package/dist/vite/api/schema-codegen.d.ts +1 -1
- package/dist/vite/api/schema-codegen.js +8 -4
- package/dist/vite/api/schema-codegen.js.map +1 -1
- package/dist/vite/plugin-api.js +5 -2
- package/dist/vite/plugin-api.js.map +1 -1
- package/lib/{AuthenticationPlugin-Cij2tPWa.js → AuthenticationPlugin-Duy_R1TU.js} +3 -3
- package/lib/{AuthenticationPlugin-Cij2tPWa.js.map → AuthenticationPlugin-Duy_R1TU.js.map} +1 -1
- package/lib/{Markdown-DT5Rrq8_.js → Markdown-DIZ8nBVC.js} +742 -738
- package/lib/{Markdown-DT5Rrq8_.js.map → Markdown-DIZ8nBVC.js.map} +1 -1
- package/lib/{MdxPage-D2rD1vC4.js → MdxPage-JEdbfW-f.js} +42 -47
- package/lib/MdxPage-JEdbfW-f.js.map +1 -0
- package/lib/{OasProvider-DdEBf2qS.js → OasProvider-D1A10JeA.js} +4 -4
- package/lib/{OasProvider-DdEBf2qS.js.map → OasProvider-D1A10JeA.js.map} +1 -1
- package/lib/{OperationList-DT4-gm_S.js → OperationList-yOmYzMIp.js} +1128 -1112
- package/lib/OperationList-yOmYzMIp.js.map +1 -0
- package/lib/{Select-z1Lwl0-J.js → Select-fAYcJ0OU.js} +8 -8
- package/lib/{Select-z1Lwl0-J.js.map → Select-fAYcJ0OU.js.map} +1 -1
- package/lib/{SlotletProvider-D8OBnr77.js → SlotletProvider-BEwNY8q0.js} +4 -4
- package/lib/{SlotletProvider-D8OBnr77.js.map → SlotletProvider-BEwNY8q0.js.map} +1 -1
- package/lib/{chunk-HA7DTUK3-ZGg2W6yV.js → chunk-HA7DTUK3-C4gP41vD.js} +5 -5
- package/lib/{chunk-HA7DTUK3-ZGg2W6yV.js.map → chunk-HA7DTUK3-C4gP41vD.js.map} +1 -1
- package/lib/hook-Cge6LiTK.js +1483 -0
- package/lib/hook-Cge6LiTK.js.map +1 -0
- package/lib/{index-DdQSV2RF.js → index-B0y3fTg-.js} +261 -243
- package/lib/index-B0y3fTg-.js.map +1 -0
- package/lib/{mutation-_Z5C2wFZ.js → mutation-EChriCeF.js} +2 -2
- package/lib/{mutation-_Z5C2wFZ.js.map → mutation-EChriCeF.js.map} +1 -1
- package/lib/post-processors/traverse.js.map +1 -1
- package/lib/{useExposedProps-BslIn-FE.js → useExposedProps-B9qXJedG.js} +2 -2
- package/lib/{useExposedProps-BslIn-FE.js.map → useExposedProps-B9qXJedG.js.map} +1 -1
- package/lib/zudoku.auth-auth0.js +1 -1
- package/lib/zudoku.auth-clerk.js +59 -41
- package/lib/zudoku.auth-clerk.js.map +1 -1
- package/lib/zudoku.auth-openid.js +76 -73
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.components.js +370 -354
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.hooks.js +1 -1
- package/lib/zudoku.plugin-api-catalog.js +23 -24
- package/lib/zudoku.plugin-api-catalog.js.map +1 -1
- package/lib/zudoku.plugin-api-keys.js +15 -16
- package/lib/zudoku.plugin-api-keys.js.map +1 -1
- package/lib/zudoku.plugin-custom-pages.js +2 -2
- 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-redirect.js +1 -1
- package/lib/zudoku.plugin-search-pagefind.js +133 -98
- package/lib/zudoku.plugin-search-pagefind.js.map +1 -1
- package/lib/zudoku.plugins.js.map +1 -1
- package/package.json +2 -2
- package/src/app/main.tsx +2 -0
- package/src/lib/authentication/authentication.ts +2 -0
- package/src/lib/authentication/providers/clerk.tsx +20 -0
- package/src/lib/authentication/providers/openid.tsx +6 -0
- package/src/lib/authentication/providers/supabase.tsx +6 -0
- package/src/lib/authentication/state.ts +1 -35
- package/src/lib/components/Layout.tsx +14 -2
- package/src/lib/components/Zudoku.tsx +5 -2
- package/src/lib/core/ZudokuContext.ts +13 -6
- package/src/lib/core/plugins.ts +9 -9
- package/src/lib/plugins/markdown/MdxPage.tsx +1 -8
- package/src/lib/plugins/openapi/Endpoint.tsx +1 -1
- package/src/lib/plugins/openapi/OperationList.tsx +1 -1
- package/src/lib/plugins/openapi/Sidecar.tsx +36 -7
- package/src/lib/plugins/openapi/interfaces.ts +29 -0
- package/src/lib/plugins/openapi/playground/Playground.tsx +1 -1
- package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +2 -1
- package/src/lib/plugins/openapi/state.ts +36 -0
- package/src/lib/plugins/search-pagefind/PagefindSearch.tsx +26 -4
- package/src/lib/plugins/search-pagefind/ResultList.tsx +59 -47
- package/src/lib/plugins/search-pagefind/get-results.tsx +31 -10
- package/src/lib/util/traverse.ts +2 -6
- package/src/lib/util/types.ts +7 -0
- package/src/lib/util/useScrollToAnchor.ts +20 -12
- package/lib/MdxPage-D2rD1vC4.js.map +0 -1
- package/lib/OperationList-DT4-gm_S.js.map +0 -1
- package/lib/hook-DzQC8PzJ.js +0 -355
- package/lib/hook-DzQC8PzJ.js.map +0 -1
- package/lib/index-DdQSV2RF.js.map +0 -1
- package/lib/joinUrl-BjDooT-T.js +0 -1154
- package/lib/joinUrl-BjDooT-T.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
2
2
|
import { Suspense, useEffect, type ReactNode } from "react";
|
|
3
|
-
import { Outlet, useNavigation } from "react-router";
|
|
3
|
+
import { Outlet, useLocation, useNavigation } from "react-router";
|
|
4
4
|
import { useSpinDelay } from "spin-delay";
|
|
5
|
+
import { joinUrl } from "../util/joinUrl.js";
|
|
5
6
|
import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
|
|
6
7
|
import { useScrollToTop } from "../util/useScrollToTop.js";
|
|
7
8
|
import { useZudoku } from "./context/ZudokuContext.js";
|
|
@@ -17,7 +18,8 @@ const LoadingFallback = () => (
|
|
|
17
18
|
);
|
|
18
19
|
|
|
19
20
|
export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
20
|
-
const { meta, authentication } = useZudoku();
|
|
21
|
+
const { meta, authentication, options } = useZudoku();
|
|
22
|
+
const location = useLocation();
|
|
21
23
|
|
|
22
24
|
useScrollToAnchor();
|
|
23
25
|
useScrollToTop();
|
|
@@ -40,6 +42,16 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
40
42
|
<style>{`:root { --top-nav-height: 0px; }`}</style>
|
|
41
43
|
)}
|
|
42
44
|
<Helmet titleTemplate={meta?.title}>
|
|
45
|
+
{options.canonicalUrlOrigin && (
|
|
46
|
+
<link
|
|
47
|
+
rel="canonical"
|
|
48
|
+
href={joinUrl(
|
|
49
|
+
options.canonicalUrlOrigin,
|
|
50
|
+
options.basePath,
|
|
51
|
+
location.pathname,
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
)}
|
|
43
55
|
{meta?.description && (
|
|
44
56
|
<meta name="description" content={meta.description} />
|
|
45
57
|
)}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useState,
|
|
12
12
|
} from "react";
|
|
13
13
|
import { ErrorBoundary } from "react-error-boundary";
|
|
14
|
-
import { Outlet, useNavigation } from "react-router";
|
|
14
|
+
import { Outlet, useLocation, useNavigation } from "react-router";
|
|
15
15
|
import { hasHead, isMdxProviderPlugin } from "../core/plugins.js";
|
|
16
16
|
import {
|
|
17
17
|
ZudokuContext,
|
|
@@ -37,6 +37,7 @@ const ZudokoInner = memo(
|
|
|
37
37
|
[props.overrides],
|
|
38
38
|
);
|
|
39
39
|
|
|
40
|
+
const location = useLocation();
|
|
40
41
|
const mdxComponents = useMemo(() => {
|
|
41
42
|
const componentsFromPlugins = (props.plugins ?? [])
|
|
42
43
|
.filter(isMdxProviderPlugin)
|
|
@@ -74,7 +75,9 @@ const ZudokoInner = memo(
|
|
|
74
75
|
);
|
|
75
76
|
|
|
76
77
|
const heads = props.plugins
|
|
77
|
-
?.flatMap((plugin) =>
|
|
78
|
+
?.flatMap((plugin) =>
|
|
79
|
+
hasHead(plugin) ? (plugin.getHead?.({ location }) ?? []) : [],
|
|
80
|
+
)
|
|
78
81
|
// eslint-disable-next-line react/no-array-index-key
|
|
79
82
|
.map((entry, i) => <Helmet key={i}>{entry}</Helmet>);
|
|
80
83
|
|
|
@@ -5,6 +5,7 @@ import type { Location } from "react-router";
|
|
|
5
5
|
import type { TopNavigationItem } from "../../config/validators/common.js";
|
|
6
6
|
import type { SidebarConfig } from "../../config/validators/SidebarSchema.js";
|
|
7
7
|
import type { AuthenticationProvider } from "../authentication/authentication.js";
|
|
8
|
+
import { type AuthState, useAuthState } from "../authentication/state.js";
|
|
8
9
|
import type { ComponentsContextType } from "../components/context/ComponentsContext.js";
|
|
9
10
|
import type { Slotlets } from "../components/SlotletProvider.js";
|
|
10
11
|
import { joinPath } from "../util/joinPath.js";
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
|
|
22
23
|
export interface ZudokuEvents {
|
|
23
24
|
location: (event: { from?: Location; to: Location }) => void;
|
|
25
|
+
auth: (auth: { prev: AuthState; next: AuthState }) => void;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export interface ApiIdentity {
|
|
@@ -67,6 +69,8 @@ type Page = Partial<{
|
|
|
67
69
|
}>;
|
|
68
70
|
|
|
69
71
|
export type ZudokuContextOptions = {
|
|
72
|
+
basePath?: string;
|
|
73
|
+
canonicalUrlOrigin?: string;
|
|
70
74
|
metadata?: Metadata;
|
|
71
75
|
page?: Page;
|
|
72
76
|
authentication?: AuthenticationProvider;
|
|
@@ -106,7 +110,14 @@ export class ZudokuContext {
|
|
|
106
110
|
if (!isEventConsumerPlugin(plugin)) return;
|
|
107
111
|
|
|
108
112
|
objectEntries(plugin.events).forEach(([event, handler]) => {
|
|
109
|
-
this.emitter.on(event, handler);
|
|
113
|
+
this.emitter.on(event, handler!);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
useAuthState.subscribe((state, prevState) => {
|
|
118
|
+
this.emitEvent("auth", {
|
|
119
|
+
prev: prevState,
|
|
120
|
+
next: state,
|
|
110
121
|
});
|
|
111
122
|
});
|
|
112
123
|
}
|
|
@@ -158,10 +169,6 @@ export class ZudokuContext {
|
|
|
158
169
|
throw new Error("No authentication provider configured");
|
|
159
170
|
}
|
|
160
171
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
request.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
164
|
-
|
|
165
|
-
return request;
|
|
172
|
+
return await this.authentication.signRequest(request);
|
|
166
173
|
};
|
|
167
174
|
}
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { LucideIcon } from "lucide-react";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import type { ReactElement } from "react";
|
|
3
|
+
import type { Location, RouteObject } from "react-router";
|
|
4
4
|
import type { Sidebar } from "../../config/validators/SidebarSchema.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import type { MdxComponentsType } from "../util/MdxComponents.js";
|
|
6
|
+
import type {
|
|
7
|
+
ApiIdentity,
|
|
8
|
+
ZudokuContext,
|
|
9
|
+
ZudokuEvents,
|
|
10
10
|
} from "./ZudokuContext.js";
|
|
11
11
|
|
|
12
12
|
export type ZudokuPlugin =
|
|
@@ -60,12 +60,12 @@ export interface CommonPlugin {
|
|
|
60
60
|
initialize?: (
|
|
61
61
|
context: ZudokuContext,
|
|
62
62
|
) => Promise<void | boolean> | void | boolean;
|
|
63
|
-
getHead?: () => ReactElement | undefined;
|
|
63
|
+
getHead?: ({ location }: { location: Location }) => ReactElement | undefined;
|
|
64
64
|
getMdxComponents?: () => MdxComponentsType;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export type EventConsumerPlugin<Event extends ZudokuEvents = ZudokuEvents> = {
|
|
68
|
-
events: { [K in keyof Event]
|
|
68
|
+
events: { [K in keyof Event]?: Event[K] };
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
export const isEventConsumerPlugin = (
|
|
@@ -2,7 +2,7 @@ import { useMDXComponents } from "@mdx-js/react";
|
|
|
2
2
|
import slugify from "@sindresorhus/slugify";
|
|
3
3
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
4
4
|
import { type PropsWithChildren, useEffect } from "react";
|
|
5
|
-
import { Link
|
|
5
|
+
import { Link } from "react-router";
|
|
6
6
|
import { CategoryHeading } from "../../components/CategoryHeading.js";
|
|
7
7
|
import { Heading } from "../../components/Heading.js";
|
|
8
8
|
import { ProseClasses } from "../../components/Markdown.js";
|
|
@@ -51,12 +51,6 @@ export const MdxPage = ({
|
|
|
51
51
|
}
|
|
52
52
|
>) => {
|
|
53
53
|
const categoryTitle = useCurrentItem()?.categoryLabel;
|
|
54
|
-
let canonicalUrl = null;
|
|
55
|
-
const path = useHref("");
|
|
56
|
-
if (typeof window !== "undefined") {
|
|
57
|
-
const domain = window.location.origin;
|
|
58
|
-
canonicalUrl = `${domain}${path}`;
|
|
59
|
-
}
|
|
60
54
|
|
|
61
55
|
const title = frontmatter.title;
|
|
62
56
|
const category = frontmatter.category ?? categoryTitle;
|
|
@@ -98,7 +92,6 @@ export const MdxPage = ({
|
|
|
98
92
|
<Helmet>
|
|
99
93
|
<title>{pageTitle}</title>
|
|
100
94
|
{excerpt && <meta name="description" content={excerpt} />}
|
|
101
|
-
{canonicalUrl && <link rel="canonical" href={canonicalUrl} />}
|
|
102
95
|
</Helmet>
|
|
103
96
|
<div
|
|
104
97
|
className={cn(
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
2
2
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
3
3
|
import { useState, useTransition } from "react";
|
|
4
|
-
import { useSelectedServer } from "../../authentication/state.js";
|
|
5
4
|
import { InlineCode } from "../../components/InlineCode.js";
|
|
6
5
|
import { Button } from "../../ui/Button.js";
|
|
7
6
|
import { useCreateQuery } from "./client/useCreateQuery.js";
|
|
8
7
|
import { useOasConfig } from "./context.js";
|
|
9
8
|
import { graphql } from "./graphql/index.js";
|
|
10
9
|
import { SimpleSelect } from "./SimpleSelect.js";
|
|
10
|
+
import { useSelectedServer } from "./state.js";
|
|
11
11
|
|
|
12
12
|
const ServersQuery = graphql(/* GraphQL */ `
|
|
13
13
|
query ServersQuery($input: JSON!, $type: SchemaType!) {
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
SelectTrigger,
|
|
16
16
|
SelectValue,
|
|
17
17
|
} from "zudoku/ui/Select.js";
|
|
18
|
-
import { useSelectedServer } from "../../authentication/state.js";
|
|
19
18
|
import { CategoryHeading } from "../../components/CategoryHeading.js";
|
|
20
19
|
import { Heading } from "../../components/Heading.js";
|
|
21
20
|
import { Markdown, ProseClasses } from "../../components/Markdown.js";
|
|
@@ -26,6 +25,7 @@ import { OperationListItem } from "./OperationListItem.js";
|
|
|
26
25
|
import { useCreateQuery } from "./client/useCreateQuery.js";
|
|
27
26
|
import { useOasConfig } from "./context.js";
|
|
28
27
|
import { graphql } from "./graphql/index.js";
|
|
28
|
+
import { useSelectedServer } from "./state.js";
|
|
29
29
|
import { sanitizeMarkdownForMetatag } from "./util/sanitizeMarkdownForMetatag.js";
|
|
30
30
|
|
|
31
31
|
export const OperationsFragment = graphql(/* GraphQL */ `
|
|
@@ -2,7 +2,8 @@ import { useSuspenseQuery } from "@tanstack/react-query";
|
|
|
2
2
|
import { HTTPSnippet } from "@zudoku/httpsnippet";
|
|
3
3
|
import { useMemo, useState, useTransition } from "react";
|
|
4
4
|
import { useSearchParams } from "react-router";
|
|
5
|
-
import {
|
|
5
|
+
import { useZudoku } from "zudoku/components";
|
|
6
|
+
import { useAuthState } from "../../authentication/state.js";
|
|
6
7
|
import { PathRenderer } from "../../components/PathRenderer.js";
|
|
7
8
|
import type { SchemaObject } from "../../oas/parser/index.js";
|
|
8
9
|
import { SyntaxHighlight } from "../../ui/SyntaxHighlight.js";
|
|
@@ -19,6 +20,7 @@ import { RequestBodySidecarBox } from "./RequestBodySidecarBox.js";
|
|
|
19
20
|
import { ResponsesSidecarBox } from "./ResponsesSidecarBox.js";
|
|
20
21
|
import * as SidecarBox from "./SidecarBox.js";
|
|
21
22
|
import { SimpleSelect } from "./SimpleSelect.js";
|
|
23
|
+
import { useSelectedServer } from "./state.js";
|
|
22
24
|
import { generateSchemaExample } from "./util/generateSchemaExample.js";
|
|
23
25
|
import { methodForColor } from "./util/methodToColor.js";
|
|
24
26
|
|
|
@@ -101,8 +103,10 @@ export const Sidecar = ({
|
|
|
101
103
|
onSelectResponse: (response: string) => void;
|
|
102
104
|
}) => {
|
|
103
105
|
const { input, type, options } = useOasConfig();
|
|
106
|
+
const auth = useAuthState();
|
|
104
107
|
const query = useCreateQuery(GetServerQuery, { input, type });
|
|
105
108
|
const result = useSuspenseQuery(query);
|
|
109
|
+
const context = useZudoku();
|
|
106
110
|
|
|
107
111
|
const methodTextColor = methodForColor(operation.method);
|
|
108
112
|
|
|
@@ -115,6 +119,17 @@ export const Sidecar = ({
|
|
|
115
119
|
|
|
116
120
|
const requestBodyContent = operation.requestBody?.content;
|
|
117
121
|
|
|
122
|
+
const transformedRequestBodyContent =
|
|
123
|
+
requestBodyContent && options?.transformExamples
|
|
124
|
+
? options.transformExamples({
|
|
125
|
+
auth,
|
|
126
|
+
type: "request",
|
|
127
|
+
operation,
|
|
128
|
+
content: requestBodyContent,
|
|
129
|
+
context,
|
|
130
|
+
})
|
|
131
|
+
: requestBodyContent;
|
|
132
|
+
|
|
118
133
|
const path = (
|
|
119
134
|
<PathRenderer
|
|
120
135
|
path={operation.path}
|
|
@@ -136,8 +151,10 @@ export const Sidecar = ({
|
|
|
136
151
|
const code = useMemo(() => {
|
|
137
152
|
const example =
|
|
138
153
|
selectedExample ??
|
|
139
|
-
(
|
|
140
|
-
? generateSchemaExample(
|
|
154
|
+
(transformedRequestBodyContent?.[0]?.schema
|
|
155
|
+
? generateSchemaExample(
|
|
156
|
+
transformedRequestBodyContent[0].schema as SchemaObject,
|
|
157
|
+
)
|
|
141
158
|
: undefined);
|
|
142
159
|
|
|
143
160
|
const snippet = new HTTPSnippet({
|
|
@@ -162,7 +179,7 @@ export const Sidecar = ({
|
|
|
162
179
|
return getConverted(snippet, selectedLang);
|
|
163
180
|
}, [
|
|
164
181
|
selectedExample,
|
|
165
|
-
|
|
182
|
+
transformedRequestBodyContent,
|
|
166
183
|
operation.method,
|
|
167
184
|
operation.path,
|
|
168
185
|
selectedServer,
|
|
@@ -232,9 +249,9 @@ export const Sidecar = ({
|
|
|
232
249
|
</>
|
|
233
250
|
)}
|
|
234
251
|
</SidecarBox.Root>
|
|
235
|
-
{isOnScreen &&
|
|
252
|
+
{isOnScreen && transformedRequestBodyContent && (
|
|
236
253
|
<RequestBodySidecarBox
|
|
237
|
-
content={
|
|
254
|
+
content={transformedRequestBodyContent}
|
|
238
255
|
onExampleChange={setSelectedExample}
|
|
239
256
|
/>
|
|
240
257
|
)}
|
|
@@ -242,7 +259,19 @@ export const Sidecar = ({
|
|
|
242
259
|
<ResponsesSidecarBox
|
|
243
260
|
selectedResponse={selectedResponse}
|
|
244
261
|
onSelectResponse={onSelectResponse}
|
|
245
|
-
responses={operation.responses
|
|
262
|
+
responses={operation.responses.map((response) => ({
|
|
263
|
+
...response,
|
|
264
|
+
content:
|
|
265
|
+
response.content && options?.transformExamples
|
|
266
|
+
? options.transformExamples({
|
|
267
|
+
auth,
|
|
268
|
+
type: "response",
|
|
269
|
+
context,
|
|
270
|
+
operation,
|
|
271
|
+
content: response.content,
|
|
272
|
+
})
|
|
273
|
+
: response.content,
|
|
274
|
+
}))}
|
|
246
275
|
/>
|
|
247
276
|
)}
|
|
248
277
|
</aside>
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { AuthState } from "../../authentication/state.js";
|
|
2
|
+
import { ZudokuContext } from "../../core/ZudokuContext.js";
|
|
1
3
|
import type { SchemaImports } from "../../oas/graphql/index.js";
|
|
4
|
+
import { OperationListItemResult } from "./OperationList.js";
|
|
2
5
|
|
|
3
6
|
type DynamicInput = () => Promise<unknown>;
|
|
4
7
|
|
|
@@ -12,6 +15,31 @@ export type ContextOasSource =
|
|
|
12
15
|
| { type: "file"; input: DynamicInput }
|
|
13
16
|
| { type: "raw"; input: string };
|
|
14
17
|
|
|
18
|
+
type Example = {
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string | null;
|
|
21
|
+
externalValue?: string | null;
|
|
22
|
+
value?: any | null;
|
|
23
|
+
summary?: string | null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type Content = {
|
|
27
|
+
mediaType: string;
|
|
28
|
+
schema?: any | null;
|
|
29
|
+
encoding?: Array<{
|
|
30
|
+
name: string;
|
|
31
|
+
}> | null;
|
|
32
|
+
examples?: Array<Example> | null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type transformExamples = (options: {
|
|
36
|
+
content: Content[];
|
|
37
|
+
context: ZudokuContext;
|
|
38
|
+
auth: AuthState;
|
|
39
|
+
operation: OperationListItemResult;
|
|
40
|
+
type: "request" | "response";
|
|
41
|
+
}) => Content[];
|
|
42
|
+
|
|
15
43
|
type BaseOasConfig = {
|
|
16
44
|
server?: string;
|
|
17
45
|
navigationId?: string;
|
|
@@ -23,6 +51,7 @@ type BaseOasConfig = {
|
|
|
23
51
|
disablePlayground?: boolean;
|
|
24
52
|
showVersionSelect?: "always" | "if-available" | "hide";
|
|
25
53
|
expandAllTags?: boolean;
|
|
54
|
+
transformExamples?: transformExamples;
|
|
26
55
|
};
|
|
27
56
|
};
|
|
28
57
|
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
SelectValue,
|
|
14
14
|
} from "zudoku/ui/Select.js";
|
|
15
15
|
import { Textarea } from "zudoku/ui/Textarea.js";
|
|
16
|
-
import { useSelectedServer } from "../../../authentication/state.js";
|
|
17
16
|
import { useApiIdentities } from "../../../components/context/ZudokuContext.js";
|
|
18
17
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
|
|
19
18
|
import { cn } from "../../../util/cn.js";
|
|
@@ -21,6 +20,7 @@ import { objectEntries } from "../../../util/objectEntries.js";
|
|
|
21
20
|
import { useLatest } from "../../../util/useLatest.js";
|
|
22
21
|
import { ColorizedParam } from "../ColorizedParam.js";
|
|
23
22
|
import { type Content } from "../SidecarExamples.js";
|
|
23
|
+
import { useSelectedServer } from "../state.js";
|
|
24
24
|
import { createUrl } from "./createUrl.js";
|
|
25
25
|
import ExamplesDropdown from "./ExamplesDropdown.js";
|
|
26
26
|
import { Headers } from "./Headers.js";
|
|
@@ -32,7 +32,7 @@ export const ResultPanel = ({
|
|
|
32
32
|
}) => {
|
|
33
33
|
const status = ((queryMutation.data?.status ?? 0) / 100).toFixed(0);
|
|
34
34
|
return (
|
|
35
|
-
<div className="min-w-0 p-4 bg-muted/50">
|
|
35
|
+
<div className="min-w-0 p-4 py-8 bg-muted/50">
|
|
36
36
|
{queryMutation.error ? (
|
|
37
37
|
<div className="flex flex-col gap-2">
|
|
38
38
|
{showPathParamsWarning && (
|
|
@@ -99,6 +99,7 @@ export const ResultPanel = ({
|
|
|
99
99
|
>
|
|
100
100
|
Looks like the request is taking longer than expected.
|
|
101
101
|
<Button
|
|
102
|
+
type="button"
|
|
102
103
|
onClick={onCancel}
|
|
103
104
|
size="sm"
|
|
104
105
|
className="w-fit"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { create } from "zustand";
|
|
3
|
+
import { persist } from "zustand/middleware";
|
|
4
|
+
|
|
5
|
+
interface SelectedServerState {
|
|
6
|
+
selectedServer?: string;
|
|
7
|
+
setSelectedServer: (newServer: string) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const useSelectedServerStore = create<SelectedServerState>()(
|
|
11
|
+
persist(
|
|
12
|
+
(set) => ({
|
|
13
|
+
selectedServer: undefined,
|
|
14
|
+
setSelectedServer: (newServer: string) =>
|
|
15
|
+
set({ selectedServer: newServer }),
|
|
16
|
+
}),
|
|
17
|
+
{ name: "zudoku-selected-server" },
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Simple wrapper for `useSelectedServerStore` to fall back to first of the provided servers
|
|
23
|
+
*/
|
|
24
|
+
export const useSelectedServer = (servers: Array<{ url: string }>) => {
|
|
25
|
+
const { selectedServer, setSelectedServer } = useSelectedServerStore();
|
|
26
|
+
|
|
27
|
+
const finalSelectedServer = useMemo(
|
|
28
|
+
() =>
|
|
29
|
+
selectedServer && servers.some((s) => s.url === selectedServer)
|
|
30
|
+
? selectedServer
|
|
31
|
+
: (servers.at(0)?.url ?? ""),
|
|
32
|
+
[selectedServer, servers],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return { selectedServer: finalSelectedServer, setSelectedServer };
|
|
36
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
2
2
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { Button } from "zudoku/ui/Button.js";
|
|
4
5
|
import { Callout } from "zudoku/ui/Callout.js";
|
|
5
6
|
import {
|
|
6
7
|
CommandDialog,
|
|
@@ -8,6 +9,8 @@ import {
|
|
|
8
9
|
CommandInput,
|
|
9
10
|
} from "zudoku/ui/Command.js";
|
|
10
11
|
import { DialogTitle } from "zudoku/ui/Dialog.js";
|
|
12
|
+
import { useAuthState } from "../../authentication/state.js";
|
|
13
|
+
import { useZudoku } from "../../components/context/ZudokuContext.js";
|
|
11
14
|
import { joinUrl } from "../../util/joinUrl.js";
|
|
12
15
|
import { getResults } from "./get-results.js";
|
|
13
16
|
import type { PagefindOptions } from "./index.js";
|
|
@@ -55,7 +58,7 @@ const usePagefind = (options: PagefindOptions) => {
|
|
|
55
58
|
enabled: typeof window !== "undefined",
|
|
56
59
|
});
|
|
57
60
|
|
|
58
|
-
if (result.isError) {
|
|
61
|
+
if (result.isError && result.error.message !== "NOT_BUILT_YET") {
|
|
59
62
|
// eslint-disable-next-line no-console
|
|
60
63
|
console.error(result.error);
|
|
61
64
|
}
|
|
@@ -74,13 +77,16 @@ export const PagefindSearch = ({
|
|
|
74
77
|
}) => {
|
|
75
78
|
const { pagefind, error, isError } = usePagefind(options);
|
|
76
79
|
const [searchTerm, setSearchTerm] = useState("");
|
|
80
|
+
const auth = useAuthState();
|
|
81
|
+
const context = useZudoku();
|
|
82
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
77
83
|
|
|
78
84
|
const { data: searchResults } = useQuery({
|
|
79
85
|
queryKey: ["pagefind-search", searchTerm],
|
|
80
86
|
queryFn: async () => {
|
|
81
87
|
const search = await pagefind?.search(searchTerm);
|
|
82
88
|
if (!search) return [];
|
|
83
|
-
return getResults(search, options);
|
|
89
|
+
return getResults({ search, options, auth, context });
|
|
84
90
|
},
|
|
85
91
|
placeholderData: keepPreviousData,
|
|
86
92
|
enabled: !!pagefind && !!searchTerm,
|
|
@@ -97,13 +103,29 @@ export const PagefindSearch = ({
|
|
|
97
103
|
<DialogTitle>Search</DialogTitle>
|
|
98
104
|
</VisuallyHidden>
|
|
99
105
|
<CommandInput
|
|
106
|
+
ref={inputRef}
|
|
100
107
|
placeholder="Search..."
|
|
101
108
|
value={searchTerm}
|
|
102
109
|
onValueChange={setSearchTerm}
|
|
103
110
|
disabled={isError}
|
|
104
111
|
/>
|
|
105
112
|
<CommandEmpty>
|
|
106
|
-
{searchTerm ?
|
|
113
|
+
{searchTerm ? (
|
|
114
|
+
<div className="flex flex-col items-center">
|
|
115
|
+
No results found.
|
|
116
|
+
<Button
|
|
117
|
+
variant="link"
|
|
118
|
+
onClick={() => {
|
|
119
|
+
setSearchTerm("");
|
|
120
|
+
inputRef.current?.focus();
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
Clear search
|
|
124
|
+
</Button>
|
|
125
|
+
</div>
|
|
126
|
+
) : (
|
|
127
|
+
"Start typing to search"
|
|
128
|
+
)}
|
|
107
129
|
</CommandEmpty>
|
|
108
130
|
{isError ? (
|
|
109
131
|
<div className="p-4 text-sm">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FileTextIcon } from "lucide-react";
|
|
2
|
-
import { useCallback } from "react";
|
|
1
|
+
import { BracketsIcon, FileTextIcon } from "lucide-react";
|
|
2
|
+
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
3
3
|
import { Link, useNavigate } from "react-router";
|
|
4
4
|
import { CommandGroup, CommandItem, CommandList } from "zudoku/ui/Command.js";
|
|
5
5
|
import {
|
|
@@ -35,6 +35,7 @@ export const ResultList = ({
|
|
|
35
35
|
maxSubResults?: number;
|
|
36
36
|
}) => {
|
|
37
37
|
const navigate = useNavigate();
|
|
38
|
+
const commandListRef = useRef<HTMLDivElement | null>(null);
|
|
38
39
|
|
|
39
40
|
const cleanResultUrl = useCallback(
|
|
40
41
|
(url: string) => {
|
|
@@ -46,59 +47,70 @@ export const ResultList = ({
|
|
|
46
47
|
[basePath],
|
|
47
48
|
);
|
|
48
49
|
|
|
50
|
+
useLayoutEffect(() => {
|
|
51
|
+
requestIdleCallback(() => {
|
|
52
|
+
commandListRef.current?.scrollTo({ top: 0 });
|
|
53
|
+
});
|
|
54
|
+
}, [searchTerm]);
|
|
55
|
+
|
|
49
56
|
return (
|
|
50
|
-
<CommandList className="max-h-[450px]">
|
|
57
|
+
<CommandList className="max-h-[450px]" ref={commandListRef}>
|
|
51
58
|
{searchTerm && searchResults.length > 0 && (
|
|
52
59
|
<CommandGroup
|
|
53
60
|
className="text-sm text-muted-foreground"
|
|
54
61
|
heading={`${searchResults.length} results for "${searchTerm}"`}
|
|
55
62
|
/>
|
|
56
63
|
)}
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<CommandItem
|
|
62
|
-
asChild
|
|
63
|
-
value={`${result.meta.title}-${result.url}`}
|
|
64
|
-
className={hoverClassname}
|
|
65
|
-
onSelect={() => {
|
|
66
|
-
void navigate(cleanResultUrl(result.url));
|
|
67
|
-
onClose();
|
|
68
|
-
}}
|
|
64
|
+
{searchTerm &&
|
|
65
|
+
searchResults.map((result) => (
|
|
66
|
+
<CommandGroup
|
|
67
|
+
key={[result.meta.title ?? result.excerpt, result.url].join("-")}
|
|
69
68
|
>
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
{result.meta.title}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
69
|
+
<CommandItem
|
|
70
|
+
asChild
|
|
71
|
+
value={`${result.meta.title}-${result.url}`}
|
|
72
|
+
className={hoverClassname}
|
|
73
|
+
onSelect={() => {
|
|
74
|
+
void navigate(cleanResultUrl(result.url));
|
|
75
|
+
onClose();
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<Link to={cleanResultUrl(result.url)}>
|
|
79
|
+
{result.meta.section === "openapi" ? (
|
|
80
|
+
<BracketsIcon />
|
|
81
|
+
) : (
|
|
82
|
+
<FileTextIcon />
|
|
83
|
+
)}
|
|
84
|
+
{result.meta.title}
|
|
85
|
+
</Link>
|
|
86
|
+
</CommandItem>
|
|
87
|
+
{result.sub_results
|
|
88
|
+
.sort(sortSubResults)
|
|
89
|
+
.slice(0, maxSubResults)
|
|
90
|
+
.map((subResult) => (
|
|
91
|
+
<CommandItem
|
|
92
|
+
asChild
|
|
93
|
+
key={`sub-${result.meta.title}-${subResult.url}`}
|
|
94
|
+
value={`sub-${result.meta.title}-${subResult.url}`}
|
|
95
|
+
className={hoverClassname}
|
|
96
|
+
onSelect={() => {
|
|
97
|
+
void navigate(cleanResultUrl(subResult.url));
|
|
98
|
+
onClose();
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
<Link to={cleanResultUrl(subResult.url)} onClick={onClose}>
|
|
102
|
+
<div className="flex flex-col items-start gap-2 ms-2.5 ps-5 border-l border-muted-foreground/50">
|
|
103
|
+
<span className="font-bold">{subResult.title}</span>
|
|
104
|
+
<span
|
|
105
|
+
className="text-[13px] [&_mark]:bg-primary [&_mark]:text-primary-foreground"
|
|
106
|
+
dangerouslySetInnerHTML={{ __html: subResult.excerpt }}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</Link>
|
|
110
|
+
</CommandItem>
|
|
111
|
+
))}
|
|
112
|
+
</CommandGroup>
|
|
113
|
+
))}
|
|
102
114
|
</CommandList>
|
|
103
115
|
);
|
|
104
116
|
};
|