zudoku 0.1.1-dev.30 → 0.1.1-dev.32
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/App.js +7 -2
- package/dist/app/App.js.map +1 -1
- package/dist/config/config.d.ts +5 -0
- package/dist/lib/components/Header.js +1 -1
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/components/Layout.js +1 -1
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/SyntaxHighlight.d.ts +1 -0
- package/dist/lib/components/SyntaxHighlight.js +1 -1
- package/dist/lib/components/SyntaxHighlight.js.map +1 -1
- package/dist/lib/components/context/DevPortalProvider.js +1 -1
- package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
- package/dist/lib/components/navigation/SideNavigation.js +1 -1
- package/dist/lib/components/navigation/SideNavigation.js.map +1 -1
- package/dist/lib/components/navigation/SideNavigationCategory.js +1 -1
- package/dist/lib/components/navigation/SideNavigationCategory.js.map +1 -1
- package/dist/lib/components/navigation/SideNavigationItem.d.ts +1 -0
- package/dist/lib/components/navigation/SideNavigationItem.js +8 -1
- package/dist/lib/components/navigation/SideNavigationItem.js.map +1 -1
- package/dist/lib/components/navigation/SideNavigationWrapper.d.ts +3 -0
- package/dist/lib/components/navigation/SideNavigationWrapper.js +2 -3
- package/dist/lib/components/navigation/SideNavigationWrapper.js.map +1 -1
- package/dist/lib/core/DevPortalContext.d.ts +1 -0
- package/dist/lib/core/DevPortalContext.js +2 -2
- package/dist/lib/core/DevPortalContext.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +1 -1
- package/dist/lib/core/plugins.js +1 -1
- package/dist/lib/core/plugins.js.map +1 -1
- package/dist/lib/plugins/api-key/CreateApiKeys.d.ts +5 -0
- package/dist/lib/plugins/api-key/CreateApiKeys.js +37 -0
- package/dist/lib/plugins/api-key/CreateApiKeys.js.map +1 -0
- package/dist/lib/plugins/api-key/SettingsApiKeys.d.ts +3 -2
- package/dist/lib/plugins/api-key/SettingsApiKeys.js +30 -4
- package/dist/lib/plugins/api-key/SettingsApiKeys.js.map +1 -1
- package/dist/lib/plugins/api-key/index.d.ts +18 -21
- package/dist/lib/plugins/api-key/index.js +49 -24
- package/dist/lib/plugins/api-key/index.js.map +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
- package/dist/lib/plugins/openapi/MakeRequest.js +22 -12
- package/dist/lib/plugins/openapi/MakeRequest.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js +24 -1
- package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +1 -1
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
- package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +2 -2
- package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js +13 -13
- package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.d.ts +2 -2
- package/dist/lib/plugins/openapi/graphql/gql.js +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/graphql.d.ts +1 -0
- package/dist/lib/plugins/openapi/graphql/graphql.js +4 -0
- package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
- package/dist/lib/plugins/openapi/index.js +2 -0
- package/dist/lib/plugins/openapi/index.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Headers.js +5 -16
- package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/InlineInput.js +1 -1
- package/dist/lib/plugins/openapi/playground/{UrlParts.d.ts → PathParams.d.ts} +2 -2
- package/dist/lib/plugins/openapi/playground/{UrlParts.js → PathParams.js} +4 -4
- package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/Playground.d.ts +7 -4
- package/dist/lib/plugins/openapi/playground/Playground.js +48 -46
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/QueryParams.d.ts +4 -6
- package/dist/lib/plugins/openapi/playground/QueryParams.js +27 -22
- package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/UrlDisplay.d.ts +4 -0
- package/dist/lib/plugins/openapi/playground/UrlDisplay.js +22 -0
- package/dist/lib/plugins/openapi/playground/UrlDisplay.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/createUrl.d.ts +2 -0
- package/dist/lib/plugins/openapi/playground/createUrl.js +15 -0
- package/dist/lib/plugins/openapi/playground/createUrl.js.map +1 -0
- package/dist/lib/plugins/openapi/util/generateSchemaExample.d.ts +1 -1
- package/dist/lib/plugins/openapi/util/generateSchemaExample.js +19 -9
- package/dist/lib/plugins/openapi/util/generateSchemaExample.js.map +1 -1
- package/dist/lib/util/MdxComponents.js +1 -1
- package/dist/lib/util/MdxComponents.js.map +1 -1
- package/dist/vite/plugin-api-keys.d.ts +4 -0
- package/dist/vite/plugin-api-keys.js +33 -0
- package/dist/vite/plugin-api-keys.js.map +1 -0
- package/dist/vite/plugin-component.js +8 -20
- package/dist/vite/plugin-component.js.map +1 -1
- package/dist/vite/plugin-mdx.js +1 -1
- package/dist/vite/plugin-mdx.js.map +1 -1
- package/dist/vite/plugin.js +2 -0
- package/dist/vite/plugin.js.map +1 -1
- package/lib/{urql-B7mLfVog.js → urql-DMlBWUKL.js} +301 -321
- package/lib/{DevPortal-COMmOqxP.js → util-CJko6Ria.js} +3661 -4915
- package/lib/zudoku.components.js +1313 -4
- package/lib/zudoku.openapi-worker.js +1 -1
- package/lib/zudoku.plugins.js +16287 -15966
- package/package.json +1 -1
- package/src/app/App.tsx +7 -2
- package/src/lib/components/Header.tsx +3 -3
- package/src/lib/components/Layout.tsx +6 -1
- package/src/lib/components/SyntaxHighlight.tsx +6 -3
- package/src/lib/components/context/DevPortalProvider.ts +1 -1
- package/src/lib/components/navigation/SideNavigation.tsx +4 -1
- package/src/lib/components/navigation/SideNavigationCategory.tsx +3 -1
- package/src/lib/components/navigation/SideNavigationItem.tsx +8 -1
- package/src/lib/components/navigation/SideNavigationWrapper.tsx +14 -11
- package/src/lib/core/DevPortalContext.ts +3 -2
- package/src/lib/core/plugins.ts +1 -1
- package/src/lib/plugins/api-key/CreateApiKeys.tsx +84 -0
- package/src/lib/plugins/api-key/SettingsApiKeys.tsx +105 -11
- package/src/lib/plugins/api-key/index.tsx +71 -66
- package/src/lib/plugins/markdown/MdxPage.tsx +2 -2
- package/src/lib/plugins/openapi/MakeRequest.tsx +25 -23
- package/src/lib/plugins/openapi/OperationListItem.tsx +137 -1
- package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +1 -1
- package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +1 -2
- package/src/lib/plugins/openapi/Sidecar.tsx +20 -20
- package/src/lib/plugins/openapi/graphql/gql.ts +3 -3
- package/src/lib/plugins/openapi/graphql/graphql.ts +5 -0
- package/src/lib/plugins/openapi/index.tsx +2 -0
- package/src/lib/plugins/openapi/playground/Headers.tsx +14 -20
- package/src/lib/plugins/openapi/playground/InlineInput.tsx +1 -1
- package/src/lib/plugins/openapi/playground/{UrlParts.tsx → PathParams.tsx} +22 -26
- package/src/lib/plugins/openapi/playground/Playground.tsx +205 -155
- package/src/lib/plugins/openapi/playground/QueryParams.tsx +84 -57
- package/src/lib/plugins/openapi/playground/UrlDisplay.tsx +32 -0
- package/src/lib/plugins/openapi/playground/createUrl.ts +22 -0
- package/src/lib/plugins/openapi/util/generateSchemaExample.ts +24 -9
- package/src/lib/util/MdxComponents.tsx +1 -0
- package/dist/lib/plugins/openapi/playground/UrlParts.js.map +0 -1
- package/src/lib/plugins/openapi/queries.graphql +0 -6
|
@@ -9,15 +9,17 @@ import {
|
|
|
9
9
|
import { Card, CardContent } from "../../../ui/Card.js";
|
|
10
10
|
import { Button } from "../../../ui/Button.js";
|
|
11
11
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
|
|
12
|
-
import { Fragment } from "react";
|
|
12
|
+
import { Fragment, useMemo } from "react";
|
|
13
13
|
import { SyntaxHighlight } from "../../../components/SyntaxHighlight.js";
|
|
14
14
|
import { ColorizedParam } from "../ColorizedParam.js";
|
|
15
15
|
import { useMutation } from "@tanstack/react-query";
|
|
16
|
-
import { useForm } from "react-hook-form";
|
|
16
|
+
import { FormProvider, useForm } from "react-hook-form";
|
|
17
17
|
import { Headers } from "./Headers.js";
|
|
18
18
|
import { QueryParams } from "./QueryParams.js";
|
|
19
|
-
import {
|
|
20
|
-
import { LoaderCircle } from "lucide-react";
|
|
19
|
+
import { PathParams } from "./PathParams.js";
|
|
20
|
+
import { CirclePlayIcon, LoaderCircle, MonitorCheckIcon } from "lucide-react";
|
|
21
|
+
import { createUrl } from "./createUrl.js";
|
|
22
|
+
import { UrlDisplay } from "./UrlDisplay.js";
|
|
21
23
|
|
|
22
24
|
function mimeTypeToLanguage(mimeType: string) {
|
|
23
25
|
const mimeTypeMapping = {
|
|
@@ -36,6 +38,19 @@ function mimeTypeToLanguage(mimeType: string) {
|
|
|
36
38
|
)?.[0][1];
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
const statusCodeMap: Record<number, string> = {
|
|
42
|
+
200: "OK",
|
|
43
|
+
201: "Created",
|
|
44
|
+
202: "Accepted",
|
|
45
|
+
204: "No Content",
|
|
46
|
+
400: "Bad Request",
|
|
47
|
+
401: "Unauthorized",
|
|
48
|
+
403: "Forbidden",
|
|
49
|
+
404: "Not Found",
|
|
50
|
+
405: "Method Not Allowed",
|
|
51
|
+
500: "Internal Server Error",
|
|
52
|
+
};
|
|
53
|
+
|
|
39
54
|
export type Header = {
|
|
40
55
|
name: string;
|
|
41
56
|
value: string;
|
|
@@ -43,8 +58,9 @@ export type Header = {
|
|
|
43
58
|
export type QueryParam = {
|
|
44
59
|
name: string;
|
|
45
60
|
value: string;
|
|
61
|
+
active?: boolean;
|
|
46
62
|
};
|
|
47
|
-
export type
|
|
63
|
+
export type PathParam = {
|
|
48
64
|
name: string;
|
|
49
65
|
value: string;
|
|
50
66
|
};
|
|
@@ -52,7 +68,7 @@ export type UrlPart = {
|
|
|
52
68
|
export type PlaygroundForm = {
|
|
53
69
|
body: string;
|
|
54
70
|
queryParams: QueryParam[];
|
|
55
|
-
|
|
71
|
+
pathParams: PathParam[];
|
|
56
72
|
headers: Header[];
|
|
57
73
|
};
|
|
58
74
|
|
|
@@ -60,63 +76,58 @@ const Playground = ({
|
|
|
60
76
|
url,
|
|
61
77
|
host,
|
|
62
78
|
method,
|
|
63
|
-
|
|
79
|
+
headers = [{ name: "", value: "" }],
|
|
80
|
+
queryParams = [],
|
|
81
|
+
pathParams = [],
|
|
64
82
|
}: {
|
|
65
83
|
host: string;
|
|
66
84
|
url: string;
|
|
67
85
|
method: string;
|
|
68
|
-
|
|
86
|
+
headers?: Header[];
|
|
87
|
+
queryParams?: QueryParam[];
|
|
88
|
+
pathParams?: PathParam[];
|
|
69
89
|
}) => {
|
|
70
|
-
const { register, control, handleSubmit, watch } =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
|
|
90
|
+
const { register, control, handleSubmit, watch, ...form } =
|
|
91
|
+
useForm<PlaygroundForm>({
|
|
92
|
+
defaultValues: {
|
|
93
|
+
body: "",
|
|
94
|
+
queryParams,
|
|
95
|
+
headers,
|
|
96
|
+
pathParams,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
82
99
|
const formState = watch();
|
|
83
100
|
|
|
84
|
-
const
|
|
101
|
+
const queryMutation = useMutation({
|
|
85
102
|
mutationFn: async (data: PlaygroundForm) => {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
.split("/")
|
|
90
|
-
.map((v) =>
|
|
91
|
-
v.startsWith("{") && v.endsWith("}")
|
|
92
|
-
? data.urlParts.find((part) => part.name === v.slice(1, -1))
|
|
93
|
-
?.value ?? v
|
|
94
|
-
: v,
|
|
95
|
-
)
|
|
96
|
-
.join("/");
|
|
97
|
-
|
|
98
|
-
const response = await fetch(fullUrl, {
|
|
103
|
+
const requestUrl = createUrl(host, url, data);
|
|
104
|
+
const start = performance.now();
|
|
105
|
+
const response = await fetch(requestUrl, {
|
|
99
106
|
method: method.toUpperCase(),
|
|
100
107
|
headers: Object.fromEntries(
|
|
101
|
-
data.
|
|
108
|
+
data.pathParams
|
|
102
109
|
.filter((h) => h.name)
|
|
103
110
|
.map((header) => [header.name, header.value]),
|
|
104
111
|
),
|
|
105
112
|
});
|
|
106
113
|
|
|
114
|
+
const body = await response.text();
|
|
115
|
+
|
|
107
116
|
return {
|
|
108
117
|
status: response.status,
|
|
109
118
|
headers: response.headers,
|
|
110
|
-
|
|
119
|
+
size: body.length,
|
|
120
|
+
body,
|
|
121
|
+
time: performance.now() - start,
|
|
111
122
|
};
|
|
112
123
|
},
|
|
113
124
|
});
|
|
114
125
|
|
|
115
|
-
const path = url.split("/").map((part) => (
|
|
126
|
+
const path = url.split("/").map((part, i, arr) => (
|
|
116
127
|
<Fragment key={part}>
|
|
117
128
|
{part.startsWith("{") && part.endsWith("}") ? (
|
|
118
129
|
<ColorizedParam
|
|
119
|
-
name={part
|
|
130
|
+
name={part}
|
|
120
131
|
backgroundOpacity="0"
|
|
121
132
|
onClick={() => {
|
|
122
133
|
console.log("asd");
|
|
@@ -124,148 +135,187 @@ const Playground = ({
|
|
|
124
135
|
slug={part.slice(1, -1)}
|
|
125
136
|
>
|
|
126
137
|
{
|
|
127
|
-
formState.
|
|
128
|
-
|
|
129
|
-
p.name,
|
|
130
|
-
part.slice(1, -1),
|
|
131
|
-
p.name === part.slice(1, -1),
|
|
132
|
-
p.value,
|
|
133
|
-
);
|
|
134
|
-
return p.name === part.slice(1, -1);
|
|
135
|
-
})?.value
|
|
138
|
+
formState.pathParams.find((p) => p.name === part.slice(1, -1))
|
|
139
|
+
?.value
|
|
136
140
|
}
|
|
137
141
|
</ColorizedParam>
|
|
138
142
|
) : (
|
|
139
143
|
part
|
|
140
144
|
)}
|
|
141
|
-
{"/"}
|
|
145
|
+
{i < arr.length - 1 && "/"}
|
|
142
146
|
<wbr />
|
|
143
147
|
</Fragment>
|
|
144
148
|
));
|
|
145
149
|
|
|
146
|
-
const lang = mimeTypeToLanguage(
|
|
150
|
+
const lang = mimeTypeToLanguage(
|
|
151
|
+
queryMutation.data?.headers.get("Content-Type") ?? "",
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const headerEntries = Array.from(queryMutation.data?.headers.entries() ?? []);
|
|
155
|
+
|
|
156
|
+
console.log(
|
|
157
|
+
"QP lol",
|
|
158
|
+
formState.queryParams,
|
|
159
|
+
formState.queryParams.filter((p) => p.active),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const urlQueryParams = formState.queryParams
|
|
163
|
+
.filter((p) => p.active)
|
|
164
|
+
.map((p) => (
|
|
165
|
+
<Fragment key={p.name}>
|
|
166
|
+
{p.name}={p.value}
|
|
167
|
+
<wbr />
|
|
168
|
+
</Fragment>
|
|
169
|
+
));
|
|
147
170
|
|
|
148
171
|
return (
|
|
149
172
|
<Dialog>
|
|
150
|
-
<DialogTrigger>
|
|
173
|
+
<DialogTrigger asChild>
|
|
174
|
+
<CirclePlayIcon
|
|
175
|
+
className="cursor-pointer text-primary hover:text-primary/80"
|
|
176
|
+
size={16}
|
|
177
|
+
/>
|
|
178
|
+
</DialogTrigger>
|
|
151
179
|
|
|
152
|
-
<DialogContent className="max-w-screen-xl w-full h-5/6">
|
|
153
|
-
<form
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
180
|
+
<DialogContent className="max-w-screen-xl w-full h-5/6 overflow-auto p-0">
|
|
181
|
+
<FormProvider {...{ register, control, handleSubmit, watch, ...form }}>
|
|
182
|
+
<form
|
|
183
|
+
onSubmit={handleSubmit((data) => {
|
|
184
|
+
queryMutation.mutateAsync(data);
|
|
185
|
+
})}
|
|
186
|
+
>
|
|
187
|
+
<div className="grid grid-cols-2 text-sm h-full">
|
|
188
|
+
<div className="flex flex-col gap-4 p-8 bg-muted/50 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
|
|
189
|
+
<div className="flex gap-2 items-stretch">
|
|
190
|
+
<div className="flex flex-1 items-center w-full border rounded-md border-border">
|
|
191
|
+
<div className="border-r border-border p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono">
|
|
192
|
+
{method.toUpperCase()}
|
|
193
|
+
</div>
|
|
194
|
+
<div className="p-2 font-mono text-xs">
|
|
195
|
+
{path}
|
|
196
|
+
{urlQueryParams.length > 0 ? "?" : ""}
|
|
197
|
+
{urlQueryParams}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
<Button type="submit" className="h-auto flex gap-1">
|
|
201
|
+
Send
|
|
202
|
+
</Button>
|
|
203
|
+
</div>
|
|
204
|
+
<Tabs defaultValue="parameters">
|
|
205
|
+
<TabsList>
|
|
206
|
+
<TabsTrigger value="parameters">Parameters</TabsTrigger>
|
|
207
|
+
<TabsTrigger value="headers">
|
|
208
|
+
Headers{" "}
|
|
209
|
+
{formState.headers.length > 0 &&
|
|
210
|
+
`(${formState.headers.length})`}
|
|
211
|
+
</TabsTrigger>
|
|
212
|
+
<TabsTrigger
|
|
213
|
+
value="body"
|
|
214
|
+
disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
|
|
215
|
+
method.toUpperCase(),
|
|
216
|
+
)}
|
|
217
|
+
>
|
|
218
|
+
Body
|
|
219
|
+
</TabsTrigger>
|
|
220
|
+
</TabsList>
|
|
221
|
+
<TabsContent value="headers">
|
|
222
|
+
<Headers
|
|
223
|
+
control={control}
|
|
224
|
+
register={register}
|
|
225
|
+
headers={formState.headers}
|
|
226
|
+
/>
|
|
227
|
+
</TabsContent>
|
|
228
|
+
<TabsContent value="parameters">
|
|
229
|
+
<div className="grid grid-cols-[min-content_1fr_1fr_auto]">
|
|
230
|
+
{pathParams.length > 0 && (
|
|
231
|
+
<div className="font-semibold my-3 col-span-3">
|
|
232
|
+
Path Parameters
|
|
169
233
|
</div>
|
|
170
|
-
|
|
171
|
-
|
|
234
|
+
)}
|
|
235
|
+
<PathParams control={control} register={register} />
|
|
236
|
+
{queryParams.length > 0 && (
|
|
237
|
+
<span className="font-semibold my-3 col-span-3">
|
|
238
|
+
Query Parameters
|
|
239
|
+
</span>
|
|
240
|
+
)}
|
|
241
|
+
<QueryParams control={control} />
|
|
242
|
+
</div>
|
|
243
|
+
</TabsContent>
|
|
244
|
+
<TabsContent value="body">
|
|
245
|
+
<textarea
|
|
246
|
+
{...register("body")}
|
|
247
|
+
className="border border-border w-full rounded p-2 bg-muted h-40"
|
|
248
|
+
/>
|
|
249
|
+
</TabsContent>
|
|
250
|
+
</Tabs>
|
|
251
|
+
</div>
|
|
252
|
+
<div className="flex flex-col gap-4 p-8 bg-muted/70">
|
|
253
|
+
{queryMutation.error ? (
|
|
254
|
+
<div>{queryMutation.error.message}</div>
|
|
255
|
+
) : queryMutation.data ? (
|
|
256
|
+
<div className="flex flex-col gap-2">
|
|
257
|
+
<div className="flex gap-2">
|
|
258
|
+
<div className="flex text-xs gap-6">
|
|
259
|
+
<div>
|
|
260
|
+
Status: {queryMutation.data.status}{" "}
|
|
261
|
+
{statusCodeMap[queryMutation.data.status] ?? ""}
|
|
172
262
|
</div>
|
|
263
|
+
<div>Time: {queryMutation.data.time.toFixed(0)}ms</div>
|
|
264
|
+
<div>Size: {queryMutation.data.size} B</div>
|
|
173
265
|
</div>
|
|
174
|
-
<Button type="submit">Send</Button>
|
|
175
266
|
</div>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
<CardContent className="border-b border-border py-4">
|
|
267
|
+
{/*<UrlDisplay host={host} path={url} />*/}
|
|
268
|
+
<Tabs defaultValue="response">
|
|
179
269
|
<TabsList>
|
|
180
|
-
<TabsTrigger value="
|
|
270
|
+
<TabsTrigger value="response">Response</TabsTrigger>
|
|
181
271
|
<TabsTrigger value="headers">
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
value="body"
|
|
186
|
-
disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
|
|
187
|
-
method.toUpperCase(),
|
|
188
|
-
)}
|
|
189
|
-
>
|
|
190
|
-
Body
|
|
272
|
+
{headerEntries.length
|
|
273
|
+
? `Headers (${headerEntries.length})`
|
|
274
|
+
: "No headers"}
|
|
191
275
|
</TabsTrigger>
|
|
192
276
|
</TabsList>
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
<QueryParams
|
|
206
|
-
control={control}
|
|
207
|
-
queryParams={formState.queryParams}
|
|
208
|
-
register={register}
|
|
209
|
-
/>
|
|
210
|
-
</TabsContent>
|
|
211
|
-
<TabsContent value="auth">
|
|
212
|
-
<CardContent>
|
|
213
|
-
{url
|
|
214
|
-
.split("/")
|
|
215
|
-
.map((part, i) =>
|
|
216
|
-
part.startsWith("{") && part.endsWith("}")
|
|
217
|
-
? formState.urlParts[i]
|
|
218
|
-
: part,
|
|
219
|
-
)
|
|
220
|
-
.join("/")}
|
|
221
|
-
</CardContent>
|
|
222
|
-
Change your password here.
|
|
277
|
+
|
|
278
|
+
<TabsContent value="response">
|
|
279
|
+
<Card className="shadow-none p-4">
|
|
280
|
+
<SyntaxHighlight
|
|
281
|
+
language={lang ?? "json"}
|
|
282
|
+
noBackground
|
|
283
|
+
className="overflow-x-auto "
|
|
284
|
+
code={
|
|
285
|
+
queryMutation.data?.body ?? JSON.stringify("")
|
|
286
|
+
}
|
|
287
|
+
/>
|
|
288
|
+
</Card>
|
|
223
289
|
</TabsContent>
|
|
224
|
-
<TabsContent value="
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
className="
|
|
228
|
-
|
|
290
|
+
<TabsContent value="headers">
|
|
291
|
+
<Card className="grid grid-cols-2 w-full gap-2.5 font-mono text-xs shadow-none p-4">
|
|
292
|
+
<div className="font-semibold">Key</div>
|
|
293
|
+
<div className="font-semibold">Value</div>
|
|
294
|
+
{headerEntries.map(([key, value]) => (
|
|
295
|
+
<Fragment key={key}>
|
|
296
|
+
<div>{key}</div>
|
|
297
|
+
<div>{value}</div>
|
|
298
|
+
</Fragment>
|
|
299
|
+
))}
|
|
300
|
+
</Card>
|
|
229
301
|
</TabsContent>
|
|
230
|
-
</
|
|
231
|
-
</
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
.map((v) =>
|
|
240
|
-
v.startsWith("{") && v.endsWith("}")
|
|
241
|
-
? formState.urlParts.find((part) => part.name === v)
|
|
242
|
-
?.value ?? v
|
|
243
|
-
: v,
|
|
244
|
-
)
|
|
245
|
-
.join("/")}
|
|
246
|
-
</div>
|
|
247
|
-
|
|
248
|
-
<div className="w-full overflow-auto max-h-80 rounded-xl border border-border p-4 dark:!bg-foreground/10 dark:border-transparent">
|
|
249
|
-
{x.isPending && (
|
|
250
|
-
<>
|
|
251
|
-
<LoaderCircle size={18} className="animate-spins" /> a
|
|
252
|
-
request to see the response."
|
|
253
|
-
</>
|
|
302
|
+
</Tabs>
|
|
303
|
+
</div>
|
|
304
|
+
) : (
|
|
305
|
+
<div className="grid place-items-center h-full">
|
|
306
|
+
<span className="text-[16px] font-semibold text-muted-foreground">
|
|
307
|
+
{queryMutation.isPending ? (
|
|
308
|
+
<LoaderCircle size={18} className="animate-spin" />
|
|
309
|
+
) : (
|
|
310
|
+
"Send a request first to see the response here"
|
|
254
311
|
)}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
copyable={false}
|
|
259
|
-
className="overflow-x-auto "
|
|
260
|
-
code={x.data?.body ?? JSON.stringify("")}
|
|
261
|
-
/>
|
|
262
|
-
</div>
|
|
263
|
-
</CardContent>
|
|
264
|
-
</Card>
|
|
312
|
+
</span>
|
|
313
|
+
</div>
|
|
314
|
+
)}
|
|
265
315
|
</div>
|
|
266
|
-
</
|
|
267
|
-
</
|
|
268
|
-
</
|
|
316
|
+
</div>
|
|
317
|
+
</form>
|
|
318
|
+
</FormProvider>
|
|
269
319
|
</DialogContent>
|
|
270
320
|
</Dialog>
|
|
271
321
|
);
|
|
@@ -2,78 +2,105 @@ import {
|
|
|
2
2
|
Control,
|
|
3
3
|
Controller,
|
|
4
4
|
useFieldArray,
|
|
5
|
-
|
|
5
|
+
useFormContext,
|
|
6
|
+
useWatch,
|
|
6
7
|
} from "react-hook-form";
|
|
7
|
-
import {
|
|
8
|
+
import { XIcon } from "lucide-react";
|
|
8
9
|
import { InlineInput } from "./InlineInput.js";
|
|
9
|
-
import { PlaygroundForm
|
|
10
|
+
import { PlaygroundForm } from "./Playground.js";
|
|
10
11
|
import { useEffect } from "react";
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
const QueryParamActive = ({
|
|
14
|
+
i,
|
|
13
15
|
control,
|
|
14
|
-
register,
|
|
15
|
-
queryParams,
|
|
16
16
|
}: {
|
|
17
|
-
|
|
17
|
+
i: number;
|
|
18
18
|
control: Control<PlaygroundForm>;
|
|
19
|
-
queryParams: QueryParam[];
|
|
20
19
|
}) => {
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
const value = useWatch({ control, name: `queryParams.${i}.value` });
|
|
21
|
+
const active = useWatch({ control, name: `queryParams.${i}.active` });
|
|
22
|
+
const form = useFormContext<PlaygroundForm>();
|
|
23
|
+
const dirty = form.formState.dirtyFields;
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
const queryLastName = queryParams.at(-1)?.name;
|
|
28
|
-
const queryLastValue = queryParams.at(-1)?.value;
|
|
25
|
+
console.log("dirty fields", dirty);
|
|
29
26
|
|
|
30
27
|
useEffect(() => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.log(queryLastName, queryLastValue, queryCount);
|
|
35
|
-
if (queryLastName !== "" || queryLastValue !== "") {
|
|
36
|
-
console.log("appending");
|
|
37
|
-
append({ name: "", value: "" });
|
|
28
|
+
if (value) {
|
|
29
|
+
console.log(`queryParams.${i}.active`, active);
|
|
30
|
+
form.setValue(`queryParams.${i}.active`, true);
|
|
38
31
|
}
|
|
39
|
-
}, [
|
|
32
|
+
}, [value]);
|
|
40
33
|
|
|
41
34
|
return (
|
|
42
|
-
<
|
|
43
|
-
{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
render={({ field }) => {
|
|
52
|
-
return (
|
|
53
|
-
<InlineInput {...field} placeholder="Name" className="peer" />
|
|
54
|
-
);
|
|
55
|
-
}}
|
|
56
|
-
name={`queryParams.${i}.name`}
|
|
57
|
-
/>
|
|
58
|
-
<Controller
|
|
59
|
-
control={control}
|
|
60
|
-
render={({ field }) => {
|
|
61
|
-
return (
|
|
62
|
-
<InlineInput {...field} placeholder="Value" className="peer" />
|
|
63
|
-
);
|
|
64
|
-
}}
|
|
65
|
-
name={`queryParams.${i}.value`}
|
|
35
|
+
<Controller
|
|
36
|
+
name={`queryParams.${i}.active`}
|
|
37
|
+
render={({ field }) => {
|
|
38
|
+
return (
|
|
39
|
+
<input
|
|
40
|
+
type="checkbox"
|
|
41
|
+
id={`queryParams.${i}.active`}
|
|
42
|
+
{...field}
|
|
43
|
+
checked={field.value}
|
|
66
44
|
/>
|
|
45
|
+
);
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
51
|
+
export const QueryParams = ({
|
|
52
|
+
control,
|
|
53
|
+
}: {
|
|
54
|
+
control: Control<PlaygroundForm>;
|
|
55
|
+
}) => {
|
|
56
|
+
const { fields } = useFieldArray<PlaygroundForm>({
|
|
57
|
+
control,
|
|
58
|
+
name: "queryParams",
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return fields.map((field, i) => (
|
|
62
|
+
<div
|
|
63
|
+
key={field.id}
|
|
64
|
+
className="px-2 grid-cols-subgrid col-span-full grid items-center gap-x-2 has-[:focus]:bg-muted hover:bg-accent rounded overflow-hidden group"
|
|
65
|
+
>
|
|
66
|
+
<QueryParamActive i={i} control={control} />
|
|
67
|
+
|
|
68
|
+
<Controller
|
|
69
|
+
control={control}
|
|
70
|
+
render={({ field }) => {
|
|
71
|
+
return (
|
|
72
|
+
<div className="flex items-center px-2 bg-transparent h-6 font-mono text-xs m-2">
|
|
73
|
+
{field.value}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}}
|
|
77
|
+
name={`queryParams.${i}.name`}
|
|
78
|
+
/>
|
|
79
|
+
<Controller
|
|
80
|
+
control={control}
|
|
81
|
+
render={({ field }) => {
|
|
82
|
+
return (
|
|
83
|
+
<InlineInput {...field} placeholder="Value" className="peer" />
|
|
84
|
+
);
|
|
85
|
+
}}
|
|
86
|
+
name={`queryParams.${i}.value`}
|
|
87
|
+
/>
|
|
88
|
+
<Controller
|
|
89
|
+
control={control}
|
|
90
|
+
render={({ field }) => {
|
|
91
|
+
return (
|
|
92
|
+
<button
|
|
93
|
+
className="hover:bg-black/5 p-1 rounded mr-2 text-muted-foreground invisible group-hover:visible peer-focus:visible"
|
|
94
|
+
onClick={() => field.onChange("")}
|
|
95
|
+
>
|
|
96
|
+
<XIcon size={16} />
|
|
97
|
+
</button>
|
|
98
|
+
);
|
|
99
|
+
}}
|
|
100
|
+
name={`queryParams.${i}.value`}
|
|
101
|
+
/>
|
|
102
|
+
|
|
103
|
+
<div className="-mx-2 col-span-full border-b border-border"></div>
|
|
77
104
|
</div>
|
|
78
|
-
);
|
|
105
|
+
));
|
|
79
106
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useFormContext } from "react-hook-form";
|
|
2
|
+
import type { PlaygroundForm } from "./Playground.js";
|
|
3
|
+
|
|
4
|
+
export const UrlDisplay = ({ host, path }: { host: string; path: string }) => {
|
|
5
|
+
const { watch } = useFormContext<PlaygroundForm>();
|
|
6
|
+
const data = watch();
|
|
7
|
+
const url = new URL(
|
|
8
|
+
host +
|
|
9
|
+
path
|
|
10
|
+
.split("/")
|
|
11
|
+
.map((v) =>
|
|
12
|
+
v.startsWith("{") && v.endsWith("}")
|
|
13
|
+
? data.pathParams.find((part) => part.name === v.slice(1, -1))
|
|
14
|
+
?.value ?? v
|
|
15
|
+
: v,
|
|
16
|
+
)
|
|
17
|
+
.join("/"),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
data.queryParams.forEach((param) => {
|
|
21
|
+
if (!param.value) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
url.searchParams.set(param.name, param.value);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="overflow-auto font-mono whitespace-nowrap">
|
|
29
|
+
{url.toString()}
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PlaygroundForm } from "./Playground.js";
|
|
2
|
+
|
|
3
|
+
export const createUrl = (host: string, path: string, data: PlaygroundForm) => {
|
|
4
|
+
const url = new URL(
|
|
5
|
+
host +
|
|
6
|
+
path
|
|
7
|
+
.split("/")
|
|
8
|
+
.map((v) =>
|
|
9
|
+
v.startsWith("{") && v.endsWith("}")
|
|
10
|
+
? data.pathParams.find((part) => part.name === v.slice(1, -1))
|
|
11
|
+
?.value ?? v
|
|
12
|
+
: v,
|
|
13
|
+
)
|
|
14
|
+
.join("/"),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
data.queryParams.forEach((param) => {
|
|
18
|
+
url.searchParams.set(param.name, param.value);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return url;
|
|
22
|
+
};
|