zudoku 0.1.1-dev.18 → 0.1.1-dev.19
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/lib/plugins/openapi/worker/createSharedWorkerClient.js +2 -3
- package/dist/lib/plugins/openapi/worker/createSharedWorkerClient.js.map +1 -1
- package/dist/lib/plugins/openapi/worker/shared-worker.d.ts +1 -0
- package/dist/lib/plugins/openapi/worker/shared-worker.js +6 -0
- package/dist/lib/plugins/openapi/worker/shared-worker.js.map +1 -0
- package/dist/vite/config.d.ts +1 -1
- package/dist/vite/config.js +13 -13
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/dev-server.js +1 -1
- package/dist/vite/dev-server.js.map +1 -1
- package/lib/zudoku.openapi-worker.js +12 -0
- package/lib/zudoku.plugins.js +1318 -1323
- package/package.json +5 -2
- package/src/cli/build/handler.ts +14 -0
- package/src/cli/cli.ts +77 -0
- package/src/cli/cmds/build.ts +24 -0
- package/src/cli/cmds/dev.ts +29 -0
- package/src/cli/common/analytics/lib.ts +89 -0
- package/src/cli/common/constants.ts +10 -0
- package/src/cli/common/logger.ts +5 -0
- package/src/cli/common/machine-id/lib.ts +85 -0
- package/src/cli/common/outdated.ts +102 -0
- package/src/cli/common/output.ts +86 -0
- package/src/cli/common/utils/box.license.txt +202 -0
- package/src/cli/common/utils/box.ts +116 -0
- package/src/cli/common/utils/ports.ts +21 -0
- package/src/cli/common/validators/lib.ts +43 -0
- package/src/cli/common/xdg/lib.ts +36 -0
- package/src/cli/dev/handler.ts +42 -0
- package/src/config/config.ts +56 -0
- package/src/index.ts +8 -0
- package/src/lib/DevPortal.tsx +93 -0
- package/src/lib/Heading.tsx +60 -0
- package/src/lib/Router.tsx +28 -0
- package/src/lib/auth.ts +1 -0
- package/src/lib/authentication/authentication.ts +18 -0
- package/src/lib/authentication/clerk.ts +45 -0
- package/src/lib/authentication/openid.ts +192 -0
- package/src/lib/components/AnchorLink.tsx +19 -0
- package/src/lib/components/CategoryHeading.tsx +16 -0
- package/src/lib/components/Dialog.tsx +119 -0
- package/src/lib/components/DynamicIcon.tsx +60 -0
- package/src/lib/components/Header.tsx +69 -0
- package/src/lib/components/Input.tsx +24 -0
- package/src/lib/components/Layout.tsx +56 -0
- package/src/lib/components/Markdown.tsx +37 -0
- package/src/lib/components/SyntaxHighlight.tsx +94 -0
- package/src/lib/components/TopNavigation.tsx +32 -0
- package/src/lib/components/context/ComponentsContext.tsx +24 -0
- package/src/lib/components/context/DevPortalProvider.ts +54 -0
- package/src/lib/components/context/PluginSystem.ts +0 -0
- package/src/lib/components/context/ThemeContext.tsx +46 -0
- package/src/lib/components/context/ViewportAnchorContext.tsx +139 -0
- package/src/lib/components/navigation/SideNavigation.tsx +18 -0
- package/src/lib/components/navigation/SideNavigationCategory.tsx +74 -0
- package/src/lib/components/navigation/SideNavigationItem.tsx +143 -0
- package/src/lib/components/navigation/SideNavigationWrapper.tsx +15 -0
- package/src/lib/components/navigation/useNavigationCollapsibleState.ts +27 -0
- package/src/lib/components/navigation/util.ts +38 -0
- package/src/lib/components.ts +3 -0
- package/src/lib/core/DevPortalContext.ts +164 -0
- package/src/lib/core/helmet.ts +5 -0
- package/src/lib/core/icons.tsx +1 -0
- package/src/lib/core/plugins.ts +43 -0
- package/src/lib/core/router.tsx +1 -0
- package/src/lib/core/types/combine.ts +16 -0
- package/src/lib/oas/graphql/index.ts +422 -0
- package/src/lib/oas/graphql/server.ts +10 -0
- package/src/lib/oas/parser/dereference/index.ts +59 -0
- package/src/lib/oas/parser/dereference/resolveRef.ts +32 -0
- package/src/lib/oas/parser/index.ts +94 -0
- package/src/lib/oas/parser/schemas/v3.0.json +1489 -0
- package/src/lib/oas/parser/schemas/v3.1.json +1298 -0
- package/src/lib/oas/parser/upgrade/index.ts +108 -0
- package/src/lib/plugins/api-key/SettingsApiKeys.tsx +22 -0
- package/src/lib/plugins/api-key/index.tsx +123 -0
- package/src/lib/plugins/markdown/MdxPage.tsx +128 -0
- package/src/lib/plugins/markdown/Toc.tsx +122 -0
- package/src/lib/plugins/markdown/generateRoutes.tsx +72 -0
- package/src/lib/plugins/markdown/index.tsx +31 -0
- package/src/lib/plugins/openapi/ColorizedParam.tsx +82 -0
- package/src/lib/plugins/openapi/MakeRequest.tsx +49 -0
- package/src/lib/plugins/openapi/MethodBadge.tsx +36 -0
- package/src/lib/plugins/openapi/OperationList.tsx +117 -0
- package/src/lib/plugins/openapi/OperationListItem.tsx +55 -0
- package/src/lib/plugins/openapi/ParameterList.tsx +32 -0
- package/src/lib/plugins/openapi/ParameterListItem.tsx +60 -0
- package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +51 -0
- package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +60 -0
- package/src/lib/plugins/openapi/Select.tsx +35 -0
- package/src/lib/plugins/openapi/Sidecar.tsx +160 -0
- package/src/lib/plugins/openapi/SidecarBox.tsx +36 -0
- package/src/lib/plugins/openapi/graphql/fragment-masking.ts +111 -0
- package/src/lib/plugins/openapi/graphql/gql.ts +70 -0
- package/src/lib/plugins/openapi/graphql/graphql.ts +795 -0
- package/src/lib/plugins/openapi/graphql/index.ts +2 -0
- package/src/lib/plugins/openapi/index.tsx +142 -0
- package/src/lib/plugins/openapi/playground/Playground.tsx +309 -0
- package/src/lib/plugins/openapi/queries.graphql +6 -0
- package/src/lib/plugins/openapi/util/generateSchemaExample.ts +59 -0
- package/src/lib/plugins/openapi/util/urql.ts +8 -0
- package/src/lib/plugins/openapi/worker/createSharedWorkerClient.ts +60 -0
- package/src/lib/plugins/openapi/worker/shared-worker.ts +5 -0
- package/src/lib/plugins/openapi/worker/worker.ts +30 -0
- package/src/lib/plugins/redirect/index.tsx +20 -0
- package/src/lib/plugins.ts +5 -0
- package/src/lib/ui/Button.tsx +56 -0
- package/src/lib/ui/Callout.tsx +87 -0
- package/src/lib/ui/Card.tsx +82 -0
- package/src/lib/ui/Note.tsx +58 -0
- package/src/lib/ui/Tabs.tsx +52 -0
- package/src/lib/util/MdxComponents.tsx +70 -0
- package/src/lib/util/cn.ts +6 -0
- package/src/lib/util/createVariantComponent.tsx +30 -0
- package/src/lib/util/createWaitForNotify.ts +18 -0
- package/src/lib/util/groupBy.ts +24 -0
- package/src/lib/util/joinPath.tsx +10 -0
- package/src/lib/util/pastellize.ts +25 -0
- package/src/lib/util/slugify.ts +3 -0
- package/src/lib/util/traverseNavigation.ts +55 -0
- package/src/lib/util/useScrollToAnchor.ts +38 -0
- package/src/lib/util/useScrollToTop.ts +13 -0
- package/src/ts.ts +94 -0
- package/src/types.d.ts +24 -0
- package/src/vite/build.ts +33 -0
- package/src/vite/config.test.ts +10 -0
- package/src/vite/config.ts +183 -0
- package/src/vite/dev-server.ts +64 -0
- package/src/vite/html.ts +37 -0
- package/src/vite/plugin-api.ts +57 -0
- package/src/vite/plugin-auth.ts +32 -0
- package/src/vite/plugin-component.ts +26 -0
- package/src/vite/plugin-config.ts +31 -0
- package/src/vite/plugin-docs.test.ts +32 -0
- package/src/vite/plugin-docs.ts +52 -0
- package/src/vite/plugin-html.ts +50 -0
- package/src/vite/plugin-mdx.ts +74 -0
- package/src/vite/plugin-metadata.ts +30 -0
- package/src/vite/plugin.ts +23 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useApiIdentities } from "../../components/context/DevPortalProvider.js";
|
|
2
|
+
import type { OperationListItemResult } from "./OperationList.js";
|
|
3
|
+
import { Playground } from "./playground/Playground.js";
|
|
4
|
+
import { useOasConfig } from "./index.js";
|
|
5
|
+
import { gql, useQuery } from "urql";
|
|
6
|
+
|
|
7
|
+
const getServerQuery = gql`
|
|
8
|
+
query getServerQuery($input: JSON!, $type: SchemaType!) {
|
|
9
|
+
schema(input: $input, type: $type) {
|
|
10
|
+
url
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const MakeRequest = ({
|
|
16
|
+
operation,
|
|
17
|
+
}: {
|
|
18
|
+
operation: OperationListItemResult;
|
|
19
|
+
}) => {
|
|
20
|
+
const identities = useApiIdentities();
|
|
21
|
+
const variables = useOasConfig();
|
|
22
|
+
const [server] = useQuery({ query: getServerQuery, variables });
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
<Playground
|
|
27
|
+
host={server.data?.schema.url}
|
|
28
|
+
method={operation.method}
|
|
29
|
+
url={operation.path}
|
|
30
|
+
defaultHeaders={[]}
|
|
31
|
+
/>
|
|
32
|
+
{identities.data.map((identity) => (
|
|
33
|
+
<button
|
|
34
|
+
key={identity.id}
|
|
35
|
+
onClick={() => {
|
|
36
|
+
const test = new Request(
|
|
37
|
+
"https://zudoku-customer-main-b36fa2f.d2.zuplo.dev" +
|
|
38
|
+
operation.path,
|
|
39
|
+
);
|
|
40
|
+
void fetch(identity.authorizeRequest(test));
|
|
41
|
+
}}
|
|
42
|
+
className="p-2 border border-border rounded hover:bg-accent"
|
|
43
|
+
>
|
|
44
|
+
Test Request {operation.path} ({identity.name})
|
|
45
|
+
</button>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cn } from "../../util/cn.js";
|
|
2
|
+
|
|
3
|
+
export const MethodTextColorMap = {
|
|
4
|
+
get: "text-green-600",
|
|
5
|
+
post: "text-sky-600",
|
|
6
|
+
put: "text-yellow-600",
|
|
7
|
+
delete: "text-red-600",
|
|
8
|
+
patch: "text-purple-600",
|
|
9
|
+
options: "text-indigo-600",
|
|
10
|
+
head: "text-gray-600",
|
|
11
|
+
trace: "text-gray-600",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const MethodColorMap = {
|
|
15
|
+
get: "bg-green-400 dark:bg-green-800",
|
|
16
|
+
post: "bg-sky-400 dark:bg-sky-800",
|
|
17
|
+
put: "bg-yellow-400 dark:bg-yellow-800",
|
|
18
|
+
delete: "bg-red-400 dark:bg-red-800",
|
|
19
|
+
patch: "bg-purple-400 dark:bg-purple-600",
|
|
20
|
+
options: "bg-indigo-400 dark:bg-indigo-600",
|
|
21
|
+
head: "bg-gray-400 dark:bg-gray-600",
|
|
22
|
+
trace: "bg-gray-400 dark:bg-gray-600",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const MethodBadge = ({ method }: { method: string }) => {
|
|
26
|
+
return (
|
|
27
|
+
<span
|
|
28
|
+
className={cn(
|
|
29
|
+
"mt-0.5 flex items-center duration-200 transition-opacity text-center uppercase font-mono text-[0.65rem] font-bold rounded text-background dark:text-zinc-50 h-4 px-1",
|
|
30
|
+
MethodColorMap[method as keyof typeof MethodColorMap],
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
{method}
|
|
34
|
+
</span>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Heading } from "../../Heading.js";
|
|
2
|
+
import { CategoryHeading } from "../../components/CategoryHeading.js";
|
|
3
|
+
import { Markdown, ProseClasses } from "../../components/Markdown.js";
|
|
4
|
+
import { cn } from "../../util/cn.js";
|
|
5
|
+
import { OperationListItem } from "./OperationListItem.js";
|
|
6
|
+
import { useOasConfig } from "./index.js";
|
|
7
|
+
import { graphql } from "./graphql/index.js";
|
|
8
|
+
import { useQuery } from "./util/urql.js";
|
|
9
|
+
import { ResultOf } from "@graphql-typed-document-node/core";
|
|
10
|
+
|
|
11
|
+
export const OperationsFragment = graphql(/* GraphQL */ `
|
|
12
|
+
fragment OperationsFragment on OperationItem {
|
|
13
|
+
slug
|
|
14
|
+
summary
|
|
15
|
+
method
|
|
16
|
+
description
|
|
17
|
+
operationId
|
|
18
|
+
contentTypes
|
|
19
|
+
path
|
|
20
|
+
parameters {
|
|
21
|
+
name
|
|
22
|
+
in
|
|
23
|
+
description
|
|
24
|
+
required
|
|
25
|
+
schema
|
|
26
|
+
style
|
|
27
|
+
}
|
|
28
|
+
requestBody {
|
|
29
|
+
content {
|
|
30
|
+
mediaType
|
|
31
|
+
encoding {
|
|
32
|
+
name
|
|
33
|
+
}
|
|
34
|
+
schema
|
|
35
|
+
}
|
|
36
|
+
description
|
|
37
|
+
required
|
|
38
|
+
}
|
|
39
|
+
responses {
|
|
40
|
+
statusCode
|
|
41
|
+
links
|
|
42
|
+
description
|
|
43
|
+
content {
|
|
44
|
+
mediaType
|
|
45
|
+
encoding {
|
|
46
|
+
name
|
|
47
|
+
}
|
|
48
|
+
schema
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
`);
|
|
53
|
+
|
|
54
|
+
export type OperationListItemResult = ResultOf<typeof OperationsFragment>;
|
|
55
|
+
|
|
56
|
+
const AllOperationsQuery = graphql(/* GraphQL */ `
|
|
57
|
+
query AllOperations($input: JSON!, $type: SchemaType!) {
|
|
58
|
+
schema(input: $input, type: $type) {
|
|
59
|
+
description
|
|
60
|
+
title
|
|
61
|
+
url
|
|
62
|
+
version
|
|
63
|
+
tags {
|
|
64
|
+
name
|
|
65
|
+
description
|
|
66
|
+
operations {
|
|
67
|
+
slug
|
|
68
|
+
...OperationsFragment
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
`);
|
|
74
|
+
|
|
75
|
+
export const OperationList = () => {
|
|
76
|
+
const { type, input } = useOasConfig();
|
|
77
|
+
|
|
78
|
+
const [result] = useQuery({
|
|
79
|
+
query: AllOperationsQuery,
|
|
80
|
+
variables: { type, input },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (!result.data) return null;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div className="pt-[--padding-content-top]">
|
|
87
|
+
<div className={cn(ProseClasses, "mb-16")}>
|
|
88
|
+
<CategoryHeading>Overview</CategoryHeading>
|
|
89
|
+
<Heading level={1} id="description" registerSidebarAnchor>
|
|
90
|
+
{result.data.schema.title}
|
|
91
|
+
</Heading>
|
|
92
|
+
<Markdown content={result.data.schema.description ?? ""} />
|
|
93
|
+
</div>
|
|
94
|
+
{result.data?.schema.tags
|
|
95
|
+
.filter((tag) => tag.operations.length > 0)
|
|
96
|
+
.map((tag) => (
|
|
97
|
+
<div key={tag.name}>
|
|
98
|
+
{tag.name && <CategoryHeading>{tag.name}</CategoryHeading>}
|
|
99
|
+
{tag.description && (
|
|
100
|
+
<Markdown
|
|
101
|
+
className={`${ProseClasses} mt-2 mb-12`}
|
|
102
|
+
content={tag.description}
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
105
|
+
<div className="operation mb-12">
|
|
106
|
+
{tag.operations.map((fragment) => (
|
|
107
|
+
<OperationListItem
|
|
108
|
+
key={fragment.slug}
|
|
109
|
+
operationFragment={fragment}
|
|
110
|
+
/>
|
|
111
|
+
))}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Heading } from "../../Heading.js";
|
|
2
|
+
import { Markdown, ProseClasses } from "../../components/Markdown.js";
|
|
3
|
+
import { groupBy } from "../../util/groupBy.js";
|
|
4
|
+
import { OperationsFragment } from "./OperationList.js";
|
|
5
|
+
import { ParameterList } from "./ParameterList.js";
|
|
6
|
+
import { Sidecar } from "./Sidecar.js";
|
|
7
|
+
import { FragmentType, useFragment } from "./graphql/index.js";
|
|
8
|
+
|
|
9
|
+
export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
|
|
10
|
+
export type ParameterGroup = (typeof PARAM_GROUPS)[number];
|
|
11
|
+
|
|
12
|
+
export const OperationListItem = ({
|
|
13
|
+
operationFragment,
|
|
14
|
+
}: {
|
|
15
|
+
operationFragment: FragmentType<typeof OperationsFragment>;
|
|
16
|
+
}) => {
|
|
17
|
+
const operation = useFragment(OperationsFragment, operationFragment);
|
|
18
|
+
const groupedParameters = groupBy(operation?.parameters ?? [], "in");
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
key={operation.operationId}
|
|
23
|
+
className="grid grid-cols-2 gap-8 items-start border-b-2 mb-16 pb-16 border-border"
|
|
24
|
+
>
|
|
25
|
+
<div className={ProseClasses}>
|
|
26
|
+
<Heading
|
|
27
|
+
level={2}
|
|
28
|
+
className="mt-0"
|
|
29
|
+
id={operation.slug}
|
|
30
|
+
registerSidebarAnchor
|
|
31
|
+
>
|
|
32
|
+
{operation.summary}
|
|
33
|
+
</Heading>
|
|
34
|
+
{operation.description && <Markdown content={operation.description} />}
|
|
35
|
+
{operation.parameters && operation.parameters.length > 0 && (
|
|
36
|
+
<div className="mt-4">
|
|
37
|
+
{PARAM_GROUPS.flatMap((group) =>
|
|
38
|
+
groupedParameters?.[group]?.length ? (
|
|
39
|
+
<ParameterList
|
|
40
|
+
key={group}
|
|
41
|
+
id={operation.slug}
|
|
42
|
+
groupedParameters={groupedParameters}
|
|
43
|
+
group={group}
|
|
44
|
+
/>
|
|
45
|
+
) : (
|
|
46
|
+
[]
|
|
47
|
+
),
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
<Sidecar operation={operation} />
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Heading } from "../../Heading.js";
|
|
2
|
+
import type { ParameterGroup } from "./OperationListItem.js";
|
|
3
|
+
import {
|
|
4
|
+
ParameterListItem,
|
|
5
|
+
type ParameterListItemResult,
|
|
6
|
+
} from "./ParameterListItem.js";
|
|
7
|
+
|
|
8
|
+
export const ParameterList = ({
|
|
9
|
+
groupedParameters,
|
|
10
|
+
group,
|
|
11
|
+
id,
|
|
12
|
+
}: {
|
|
13
|
+
groupedParameters: Record<ParameterGroup, ParameterListItemResult[]>;
|
|
14
|
+
group: ParameterGroup;
|
|
15
|
+
id: string;
|
|
16
|
+
}) => (
|
|
17
|
+
<>
|
|
18
|
+
<Heading level={3} id={`${id}/${group}-parameters`} className="capitalize">
|
|
19
|
+
{group === "header" ? "Headers" : `${group} Parameters`}
|
|
20
|
+
</Heading>
|
|
21
|
+
<ul className="list-none m-0 px-0 overflow-hidden">
|
|
22
|
+
{groupedParameters[group].map((parameter) => (
|
|
23
|
+
<ParameterListItem
|
|
24
|
+
key={`${parameter.name}-${parameter.in}`}
|
|
25
|
+
parameter={parameter}
|
|
26
|
+
id={id}
|
|
27
|
+
group={group}
|
|
28
|
+
/>
|
|
29
|
+
))}
|
|
30
|
+
</ul>
|
|
31
|
+
</>
|
|
32
|
+
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Markdown } from "../../components/Markdown.js";
|
|
2
|
+
import { type SchemaObject } from "../../oas/graphql/index.js";
|
|
3
|
+
import { ColorizedParam } from "./ColorizedParam.js";
|
|
4
|
+
import type { OperationListItemResult } from "./OperationList.js";
|
|
5
|
+
import type { ParameterGroup } from "./OperationListItem.js";
|
|
6
|
+
|
|
7
|
+
const getParameterSchema = (
|
|
8
|
+
parameter: ParameterListItemResult,
|
|
9
|
+
): SchemaObject => {
|
|
10
|
+
if (parameter.schema != null && typeof parameter.schema === "object") {
|
|
11
|
+
return parameter.schema;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
type: "string",
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ParameterListItemResult = NonNullable<
|
|
19
|
+
OperationListItemResult["parameters"]
|
|
20
|
+
>[number];
|
|
21
|
+
|
|
22
|
+
export const ParameterListItem = ({
|
|
23
|
+
parameter,
|
|
24
|
+
group,
|
|
25
|
+
id,
|
|
26
|
+
}: {
|
|
27
|
+
parameter: ParameterListItemResult;
|
|
28
|
+
group: ParameterGroup;
|
|
29
|
+
id: string;
|
|
30
|
+
}) => (
|
|
31
|
+
<li className="not-prose px-2 py-4 border-t border-border bg-border/20">
|
|
32
|
+
<div className="flex items-center gap-2">
|
|
33
|
+
<code>
|
|
34
|
+
{group === "path" ? (
|
|
35
|
+
<ColorizedParam
|
|
36
|
+
name={parameter.name}
|
|
37
|
+
backgroundOpacity="15%"
|
|
38
|
+
className="px-1"
|
|
39
|
+
slug={id + "-" + parameter.name.toLocaleLowerCase()}
|
|
40
|
+
/>
|
|
41
|
+
) : (
|
|
42
|
+
parameter.name
|
|
43
|
+
)}
|
|
44
|
+
</code>
|
|
45
|
+
{parameter.required && (
|
|
46
|
+
<span className="py-px px-1.5 font-medium text-xs bg-primary/75 text-muted rounded-lg">
|
|
47
|
+
required
|
|
48
|
+
</span>
|
|
49
|
+
)}
|
|
50
|
+
{getParameterSchema(parameter).type && (
|
|
51
|
+
<span className="text-xs text-muted-foreground">
|
|
52
|
+
{getParameterSchema(parameter).type}
|
|
53
|
+
</span>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
{parameter.description && (
|
|
57
|
+
<Markdown content={parameter.description} className="prose-p:my-1" />
|
|
58
|
+
)}
|
|
59
|
+
</li>
|
|
60
|
+
);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
|
|
3
|
+
import { type SchemaObject } from "../../oas/graphql/index.js";
|
|
4
|
+
import type { OperationListItemResult } from "./OperationList.js";
|
|
5
|
+
import { Select } from "./Select.js";
|
|
6
|
+
import * as SidecarBox from "./SidecarBox.js";
|
|
7
|
+
import { generateSchemaExample } from "./util/generateSchemaExample.js";
|
|
8
|
+
|
|
9
|
+
type Content = NonNullable<
|
|
10
|
+
NonNullable<OperationListItemResult["requestBody"]>["content"]
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
// @todo should we handle multiple content types?
|
|
14
|
+
export const RequestBodySidecarBox = ({ content }: { content: Content }) => {
|
|
15
|
+
const [selected, setSelected] = useState("example");
|
|
16
|
+
|
|
17
|
+
if (!content.length) return null;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<div>lol</div>
|
|
22
|
+
<SidecarBox.Root>
|
|
23
|
+
<SidecarBox.Head className="text-xs flex justify-between items-center">
|
|
24
|
+
<span className="font-mono">Request Body</span>
|
|
25
|
+
<Select
|
|
26
|
+
onChange={(e) => setSelected(e.target.value)}
|
|
27
|
+
options={[
|
|
28
|
+
{ value: "example", label: "Example" },
|
|
29
|
+
{ value: "schema", label: "Schema" },
|
|
30
|
+
]}
|
|
31
|
+
/>
|
|
32
|
+
</SidecarBox.Head>
|
|
33
|
+
<SidecarBox.Body>
|
|
34
|
+
<SyntaxHighlight
|
|
35
|
+
language="json"
|
|
36
|
+
noBackground
|
|
37
|
+
copyable={false}
|
|
38
|
+
className="text-xs"
|
|
39
|
+
code={JSON.stringify(
|
|
40
|
+
selected === "example"
|
|
41
|
+
? generateSchemaExample(content[0].schema as SchemaObject)
|
|
42
|
+
: content[0].schema,
|
|
43
|
+
null,
|
|
44
|
+
2,
|
|
45
|
+
)}
|
|
46
|
+
/>
|
|
47
|
+
</SidecarBox.Body>
|
|
48
|
+
</SidecarBox.Root>
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
|
|
3
|
+
import { type SchemaObject } from "../../oas/graphql/index.js";
|
|
4
|
+
import { cn } from "../../util/cn.js";
|
|
5
|
+
import type { OperationListItemResult } from "./OperationList.js";
|
|
6
|
+
import * as SidecarBox from "./SidecarBox.js";
|
|
7
|
+
import { generateSchemaExample } from "./util/generateSchemaExample.js";
|
|
8
|
+
|
|
9
|
+
type Responses = OperationListItemResult["responses"];
|
|
10
|
+
export const ResponsesSidecarBox = ({
|
|
11
|
+
responses,
|
|
12
|
+
}: {
|
|
13
|
+
responses: Responses;
|
|
14
|
+
}) => {
|
|
15
|
+
const [tabIndex, setTabIndex] = useState(0);
|
|
16
|
+
|
|
17
|
+
const activeTab = responses[tabIndex];
|
|
18
|
+
const schema = activeTab?.content?.[0]?.schema as SchemaObject | undefined;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<SidecarBox.Root>
|
|
22
|
+
<SidecarBox.Head className="text-xs grid grid-rows-2 pb-0">
|
|
23
|
+
<span className="font-mono">Responses</span>
|
|
24
|
+
<div className="flex gap-2">
|
|
25
|
+
{responses.map((response, index) => (
|
|
26
|
+
<div
|
|
27
|
+
key={response.statusCode}
|
|
28
|
+
onClick={() => setTabIndex(index)}
|
|
29
|
+
className={cn(
|
|
30
|
+
"text-xs font-mono px-1.5 py-1 pb-px translate-y-px border-b-2 border-transparent rounded-t cursor-pointer",
|
|
31
|
+
tabIndex === index
|
|
32
|
+
? "text-primary dark:text-inherit border-primary"
|
|
33
|
+
: "hover:border-accent-foreground/25",
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{response.statusCode}
|
|
37
|
+
</div>
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
</SidecarBox.Head>
|
|
41
|
+
<SidecarBox.Body>
|
|
42
|
+
{schema ? (
|
|
43
|
+
<SyntaxHighlight
|
|
44
|
+
language="json"
|
|
45
|
+
noBackground
|
|
46
|
+
copyable={false}
|
|
47
|
+
className="text-xs"
|
|
48
|
+
code={JSON.stringify(generateSchemaExample(schema), null, 2)}
|
|
49
|
+
/>
|
|
50
|
+
) : (
|
|
51
|
+
<span className="text-muted-foreground font-mono italic text-xs">
|
|
52
|
+
Empty Response
|
|
53
|
+
</span>
|
|
54
|
+
)}
|
|
55
|
+
<hr className="border-border my-1" />
|
|
56
|
+
<div className="text-xs">{responses[tabIndex].description}</div>
|
|
57
|
+
</SidecarBox.Body>
|
|
58
|
+
</SidecarBox.Root>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ChangeEventHandler } from "react";
|
|
2
|
+
import { ChevronsUpDownIcon } from "../../core/icons.js";
|
|
3
|
+
import { cn } from "../../util/cn.js";
|
|
4
|
+
|
|
5
|
+
export const Select = ({
|
|
6
|
+
onChange,
|
|
7
|
+
className,
|
|
8
|
+
options,
|
|
9
|
+
}: {
|
|
10
|
+
onChange?: ChangeEventHandler<HTMLSelectElement>;
|
|
11
|
+
className?: string;
|
|
12
|
+
options: {
|
|
13
|
+
value: string;
|
|
14
|
+
label: string;
|
|
15
|
+
}[];
|
|
16
|
+
}) => (
|
|
17
|
+
<div className={cn("grid", className)}>
|
|
18
|
+
<select
|
|
19
|
+
className={cn(
|
|
20
|
+
"row-start-1 col-start-1 border border-input text-foreground px-2 py-1 pe-6",
|
|
21
|
+
"rounded-md appearance-none bg-zinc-50 hover:bg-white dark:bg-zinc-800 hover:dark:bg-zinc-800/75",
|
|
22
|
+
)}
|
|
23
|
+
onChange={onChange}
|
|
24
|
+
>
|
|
25
|
+
{options.map((option) => (
|
|
26
|
+
<option value={option.value} key={option.value}>
|
|
27
|
+
{option.label}
|
|
28
|
+
</option>
|
|
29
|
+
))}
|
|
30
|
+
</select>
|
|
31
|
+
<div className="row-start-1 col-start-1 self-center justify-self-end relative end-2 pointer-events-none">
|
|
32
|
+
<ChevronsUpDownIcon size={14} />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Fragment, useMemo, useState } from "react";
|
|
2
|
+
import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
|
|
3
|
+
import { cn } from "../../util/cn.js";
|
|
4
|
+
import { ColorizedParam } from "./ColorizedParam.js";
|
|
5
|
+
import { MakeRequest } from "./MakeRequest.js";
|
|
6
|
+
import { MethodTextColorMap } from "./MethodBadge.js";
|
|
7
|
+
import type { OperationListItemResult } from "./OperationList.js";
|
|
8
|
+
import { RequestBodySidecarBox } from "./RequestBodySidecarBox.js";
|
|
9
|
+
import { ResponsesSidecarBox } from "./ResponsesSidecarBox.js";
|
|
10
|
+
import { Select } from "./Select.js";
|
|
11
|
+
import * as SidecarBox from "./SidecarBox.js";
|
|
12
|
+
import { HTTPSnippet } from "@zudoku/httpsnippet";
|
|
13
|
+
import { generateSchemaExample } from "./util/generateSchemaExample.js";
|
|
14
|
+
import type { SchemaObject } from "../../oas/parser/index.js";
|
|
15
|
+
|
|
16
|
+
const getConverted = (snippet: HTTPSnippet, option: string) => {
|
|
17
|
+
let converted;
|
|
18
|
+
switch (option) {
|
|
19
|
+
case "shell":
|
|
20
|
+
converted = snippet.convert("shell", "curl");
|
|
21
|
+
break;
|
|
22
|
+
case "js":
|
|
23
|
+
converted = snippet.convert("javascript", "fetch");
|
|
24
|
+
break;
|
|
25
|
+
case "python":
|
|
26
|
+
converted = snippet.convert("python", "requests");
|
|
27
|
+
break;
|
|
28
|
+
case "java":
|
|
29
|
+
converted = snippet.convert("java", "okhttp");
|
|
30
|
+
break;
|
|
31
|
+
case "go":
|
|
32
|
+
converted = snippet.convert("go", "native");
|
|
33
|
+
break;
|
|
34
|
+
case "csharp":
|
|
35
|
+
converted = snippet.convert("csharp", "httpclient");
|
|
36
|
+
break;
|
|
37
|
+
case "kotlin":
|
|
38
|
+
converted = snippet.convert("kotlin", "okhttp");
|
|
39
|
+
break;
|
|
40
|
+
case "objc":
|
|
41
|
+
converted = snippet.convert("objc", "nsurlsession");
|
|
42
|
+
break;
|
|
43
|
+
case "php":
|
|
44
|
+
converted = snippet.convert("php", "http2");
|
|
45
|
+
break;
|
|
46
|
+
case "ruby":
|
|
47
|
+
converted = snippet.convert("ruby");
|
|
48
|
+
break;
|
|
49
|
+
case "swift":
|
|
50
|
+
converted = snippet.convert("swift");
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
converted = snippet.convert("shell");
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return converted ? converted[0] : "";
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Sidecar = ({
|
|
61
|
+
operation,
|
|
62
|
+
}: {
|
|
63
|
+
operation: OperationListItemResult;
|
|
64
|
+
}) => {
|
|
65
|
+
const methodTextColor =
|
|
66
|
+
MethodTextColorMap[operation.method as keyof typeof MethodTextColorMap];
|
|
67
|
+
const [option, setOption] = useState("curl");
|
|
68
|
+
const requestBodyContent = operation.requestBody?.content;
|
|
69
|
+
|
|
70
|
+
const path = operation.path.split("/").map((part) => (
|
|
71
|
+
<Fragment key={part}>
|
|
72
|
+
{part.startsWith("{") && part.endsWith("}") ? (
|
|
73
|
+
<ColorizedParam
|
|
74
|
+
name={part.slice(1, -1)}
|
|
75
|
+
backgroundOpacity="0"
|
|
76
|
+
// same as in `ParameterListItem`
|
|
77
|
+
slug={operation.slug + "-" + part.slice(1, -1).toLocaleLowerCase()}
|
|
78
|
+
>
|
|
79
|
+
{part}
|
|
80
|
+
</ColorizedParam>
|
|
81
|
+
) : (
|
|
82
|
+
part
|
|
83
|
+
)}
|
|
84
|
+
/
|
|
85
|
+
<wbr />
|
|
86
|
+
</Fragment>
|
|
87
|
+
));
|
|
88
|
+
|
|
89
|
+
const code = useMemo(() => {
|
|
90
|
+
const example = requestBodyContent?.[0]?.schema
|
|
91
|
+
? generateSchemaExample(requestBodyContent[0].schema as SchemaObject)
|
|
92
|
+
: undefined;
|
|
93
|
+
|
|
94
|
+
const snippet = new HTTPSnippet(
|
|
95
|
+
{
|
|
96
|
+
method: operation.method.toLocaleUpperCase(),
|
|
97
|
+
url: operation.path.replaceAll("{", ":").replaceAll("}", ""),
|
|
98
|
+
headers: [{ name: "Authorization", value: "Bearer <token>" }],
|
|
99
|
+
postData: example
|
|
100
|
+
? {
|
|
101
|
+
text: JSON.stringify(example, null, 2),
|
|
102
|
+
mimeType: "application/json",
|
|
103
|
+
}
|
|
104
|
+
: {},
|
|
105
|
+
} as never, // 👈 never touch this
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return getConverted(snippet, option) ?? "";
|
|
109
|
+
}, [option, operation.method, operation.path, requestBodyContent]);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<aside className="flex flex-col overflow-hidden sticky top-[--scroll-padding] gap-4">
|
|
113
|
+
<SidecarBox.Root>
|
|
114
|
+
<MakeRequest operation={operation} />
|
|
115
|
+
<SidecarBox.Head className="flex justify-between items-center flex-nowrap p-2 gap-2 text-xs">
|
|
116
|
+
<span className="font-mono break-words">
|
|
117
|
+
<span className={cn("font-semibold", methodTextColor)}>
|
|
118
|
+
{operation.method.toLocaleUpperCase()}
|
|
119
|
+
</span>
|
|
120
|
+
|
|
121
|
+
{path}
|
|
122
|
+
</span>
|
|
123
|
+
<Select
|
|
124
|
+
className="self-start"
|
|
125
|
+
onChange={(e) => setOption(e.target.value)}
|
|
126
|
+
options={[
|
|
127
|
+
{ value: "shell", label: "cURL" },
|
|
128
|
+
{ value: "js", label: "Javascript" },
|
|
129
|
+
{ value: "python", label: "Python" },
|
|
130
|
+
{ value: "java", label: "Java" },
|
|
131
|
+
{ value: "go", label: "Go" },
|
|
132
|
+
{ value: "csharp", label: "C#" },
|
|
133
|
+
{ value: "kotlin", label: "Kotlin" },
|
|
134
|
+
{ value: "objc", label: "Objective C" },
|
|
135
|
+
{ value: "php", label: "PHP" },
|
|
136
|
+
{ value: "ruby", label: "Ruby" },
|
|
137
|
+
{ value: "swift", label: "Swift" },
|
|
138
|
+
]}
|
|
139
|
+
/>
|
|
140
|
+
</SidecarBox.Head>
|
|
141
|
+
<SidecarBox.Body>
|
|
142
|
+
<SyntaxHighlight
|
|
143
|
+
language={option}
|
|
144
|
+
copyable={false}
|
|
145
|
+
noBackground
|
|
146
|
+
className="text-xs"
|
|
147
|
+
code={code}
|
|
148
|
+
/>
|
|
149
|
+
</SidecarBox.Body>
|
|
150
|
+
</SidecarBox.Root>
|
|
151
|
+
{/*<MakeRequest />*/}
|
|
152
|
+
{requestBodyContent && (
|
|
153
|
+
<RequestBodySidecarBox content={requestBodyContent} />
|
|
154
|
+
)}
|
|
155
|
+
{operation.responses.length > 0 && (
|
|
156
|
+
<ResponsesSidecarBox responses={operation.responses} />
|
|
157
|
+
)}
|
|
158
|
+
</aside>
|
|
159
|
+
);
|
|
160
|
+
};
|