zudoku 0.25.2 → 0.25.3
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/demo.js +0 -1
- package/dist/app/demo.js.map +1 -1
- package/dist/app/standalone.js +0 -1
- package/dist/app/standalone.js.map +1 -1
- package/dist/config/validators/InputSidebarSchema.d.ts +2 -2
- package/dist/config/validators/validate.d.ts +4 -4
- package/dist/lib/oas/graphql/index.d.ts +3 -0
- package/dist/lib/oas/graphql/index.js +12 -13
- package/dist/lib/oas/graphql/index.js.map +1 -1
- package/dist/lib/plugins/openapi/ColorizedParam.d.ts +10 -2
- package/dist/lib/plugins/openapi/ColorizedParam.js +16 -7
- package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js +3 -2
- package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +2 -0
- package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.d.ts +1 -1
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +2 -5
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
- package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +2 -6
- package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
- package/dist/lib/plugins/openapi/{ExampleDisplay.d.ts → SidecarExamples.d.ts} +2 -6
- package/dist/lib/plugins/openapi/SidecarExamples.js +62 -0
- package/dist/lib/plugins/openapi/SidecarExamples.js.map +1 -0
- package/dist/lib/plugins/openapi/client/GraphQLClient.d.ts +1 -1
- package/dist/lib/plugins/openapi/client/GraphQLClient.js +22 -93
- package/dist/lib/plugins/openapi/client/GraphQLClient.js.map +1 -1
- package/dist/lib/plugins/openapi/client/createServer.d.ts +2 -1
- package/dist/lib/plugins/openapi/client/createServer.js +5 -2
- package/dist/lib/plugins/openapi/client/createServer.js.map +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.js +2 -13
- package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
- package/dist/lib/plugins/openapi/index.d.ts +2 -1
- package/dist/lib/plugins/openapi/index.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/EnumSelector.d.ts +8 -0
- package/dist/lib/plugins/openapi/playground/EnumSelector.js +21 -0
- package/dist/lib/plugins/openapi/playground/EnumSelector.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/PathParams.js +9 -4
- package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Playground.d.ts +3 -0
- package/dist/lib/plugins/openapi/playground/Playground.js +5 -2
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/QueryParams.js +23 -8
- package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/SchemaComponents.js +2 -1
- package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +1 -1
- package/dist/lib/plugins/openapi/util/generateSchemaExample.js +19 -11
- package/dist/lib/plugins/openapi/util/generateSchemaExample.js.map +1 -1
- package/dist/lib/ui/Badge.js +1 -1
- package/dist/lib/ui/Badge.js.map +1 -1
- package/dist/lib/ui/Button.d.ts +1 -1
- package/dist/lib/ui/Checkbox.d.ts +8 -2
- package/dist/lib/ui/Checkbox.js +13 -1
- package/dist/lib/ui/Checkbox.js.map +1 -1
- package/dist/lib/util/traverse.d.ts +8 -1
- package/dist/lib/util/traverse.js +7 -3
- package/dist/lib/util/traverse.js.map +1 -1
- package/dist/vite/api/schema-codegen.d.ts +12 -0
- package/dist/vite/api/schema-codegen.js +62 -0
- package/dist/vite/api/schema-codegen.js.map +1 -0
- package/dist/vite/api/schema-codegen.test.d.ts +1 -0
- package/dist/vite/api/schema-codegen.test.js +247 -0
- package/dist/vite/api/schema-codegen.test.js.map +1 -0
- package/dist/vite/config.js +0 -7
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/plugin-api.js +110 -82
- package/dist/vite/plugin-api.js.map +1 -1
- package/dist/vite/plugin-component.js +0 -1
- package/dist/vite/plugin-component.js.map +1 -1
- package/lib/Command-9x_kZHr4.js +611 -0
- package/lib/Command-9x_kZHr4.js.map +1 -0
- package/lib/{OperationList-BLdHAQ39.js → OperationList-B8bHMKme.js} +1440 -1434
- package/lib/OperationList-B8bHMKme.js.map +1 -0
- package/lib/{createServer-Bf5_6o6G.js → createServer-BznDkeSA.js} +4227 -5154
- package/lib/createServer-BznDkeSA.js.map +1 -0
- package/lib/index-TaRXY2w1.js +43 -0
- package/lib/index-TaRXY2w1.js.map +1 -0
- package/lib/index-sD8L1_Dl.js +1292 -0
- package/lib/index-sD8L1_Dl.js.map +1 -0
- package/lib/post-processors/traverse.js +11 -8
- package/lib/post-processors/traverse.js.map +1 -1
- package/lib/ui/Badge.js +1 -1
- package/lib/ui/Badge.js.map +1 -1
- package/lib/ui/Checkbox.js +25 -14
- package/lib/ui/Checkbox.js.map +1 -1
- package/lib/ui/Command.js +14 -550
- package/lib/ui/Command.js.map +1 -1
- package/lib/zudoku.plugin-openapi.js +1 -1
- package/package.json +1 -6
- package/src/app/demo.tsx +0 -1
- package/src/app/standalone.tsx +0 -1
- package/src/lib/oas/graphql/index.ts +19 -15
- package/src/lib/plugins/openapi/ColorizedParam.tsx +29 -12
- package/src/lib/plugins/openapi/ParameterListItem.tsx +9 -7
- package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +2 -0
- package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -7
- package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +5 -8
- package/src/lib/plugins/openapi/SidecarExamples.tsx +155 -0
- package/src/lib/plugins/openapi/client/GraphQLClient.tsx +28 -120
- package/src/lib/plugins/openapi/client/createServer.ts +6 -2
- package/src/lib/plugins/openapi/client/useCreateQuery.ts +2 -17
- package/src/lib/plugins/openapi/index.tsx +2 -1
- package/src/lib/plugins/openapi/playground/EnumSelector.tsx +86 -0
- package/src/lib/plugins/openapi/playground/PathParams.tsx +72 -64
- package/src/lib/plugins/openapi/playground/Playground.tsx +26 -13
- package/src/lib/plugins/openapi/playground/QueryParams.tsx +102 -73
- package/src/lib/plugins/openapi/schema/SchemaComponents.tsx +4 -7
- package/src/lib/plugins/openapi/util/generateSchemaExample.ts +26 -11
- package/src/lib/ui/Badge.tsx +1 -1
- package/src/lib/ui/Checkbox.tsx +24 -7
- package/src/lib/util/traverse.ts +15 -5
- package/dist/lib/plugins/openapi/ExampleDisplay.js +0 -78
- package/dist/lib/plugins/openapi/ExampleDisplay.js.map +0 -1
- package/dist/lib/plugins/openapi/client/worker.d.ts +0 -4
- package/dist/lib/plugins/openapi/client/worker.js +0 -29
- package/dist/lib/plugins/openapi/client/worker.js.map +0 -1
- package/dist/lib/plugins/openapi-worker.d.ts +0 -1
- package/dist/lib/plugins/openapi-worker.js +0 -8
- package/dist/lib/plugins/openapi-worker.js.map +0 -1
- package/lib/Dialog-Bxv1yEIg.js +0 -67
- package/lib/Dialog-Bxv1yEIg.js.map +0 -1
- package/lib/OperationList-BLdHAQ39.js.map +0 -1
- package/lib/assets/index-C7jnHK4b.js +0 -4841
- package/lib/assets/index-C7jnHK4b.js.map +0 -1
- package/lib/assets/worker-Cbp2r2BQ.js +0 -18592
- package/lib/assets/worker-Cbp2r2BQ.js.map +0 -1
- package/lib/createServer-Bf5_6o6G.js.map +0 -1
- package/lib/index-BNx95gkf.js +0 -1284
- package/lib/index-BNx95gkf.js.map +0 -1
- package/lib/index-DyBL--Kz.js +0 -826
- package/lib/index-DyBL--Kz.js.map +0 -1
- package/lib/zudoku.openapi-worker.js +0 -15
- package/lib/zudoku.openapi-worker.js.map +0 -1
- package/src/lib/plugins/openapi/ExampleDisplay.tsx +0 -163
- package/src/lib/plugins/openapi/client/worker.ts +0 -44
- package/src/lib/plugins/openapi-worker.ts +0 -11
|
@@ -1,34 +1,16 @@
|
|
|
1
1
|
import type { GraphQLError } from "graphql/error/index.js";
|
|
2
|
-
import { ulid } from "ulidx";
|
|
3
|
-
import { initializeWorker } from "zudoku/openapi-worker";
|
|
4
2
|
import { ZudokuError } from "../../../util/invariant.js";
|
|
5
3
|
import type { TypedDocumentString } from "../graphql/graphql.js";
|
|
6
4
|
import type { OpenApiPluginOptions } from "../index.js";
|
|
7
5
|
import type { LocalServer } from "./createServer.js";
|
|
8
|
-
import type { WorkerGraphQLMessage } from "./worker.js";
|
|
9
6
|
|
|
10
7
|
let localServerPromise: Promise<LocalServer> | undefined;
|
|
11
|
-
let worker: SharedWorker | undefined;
|
|
12
8
|
|
|
13
9
|
type GraphQLResponse<TResult> = {
|
|
14
10
|
errors?: GraphQLError[];
|
|
15
11
|
data: TResult;
|
|
16
12
|
};
|
|
17
13
|
|
|
18
|
-
const resolveVariables = async (variables?: unknown) => {
|
|
19
|
-
if (!variables) return;
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
typeof variables === "object" &&
|
|
23
|
-
"type" in variables &&
|
|
24
|
-
variables.type === "file" &&
|
|
25
|
-
"input" in variables &&
|
|
26
|
-
typeof variables.input === "function"
|
|
27
|
-
) {
|
|
28
|
-
variables.input = await variables.input();
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
14
|
const throwIfError = (response: GraphQLResponse<unknown>) => {
|
|
33
15
|
if (!response.errors?.[0]) return;
|
|
34
16
|
|
|
@@ -39,119 +21,45 @@ const throwIfError = (response: GraphQLResponse<unknown>) => {
|
|
|
39
21
|
};
|
|
40
22
|
|
|
41
23
|
export class GraphQLClient {
|
|
42
|
-
readonly
|
|
43
|
-
#pendingRequests = new Map<string, (value: any) => void>();
|
|
44
|
-
#port: MessagePort | undefined;
|
|
24
|
+
constructor(private readonly config: OpenApiPluginOptions) {}
|
|
45
25
|
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} else {
|
|
52
|
-
this.#mode = "worker";
|
|
26
|
+
#getLocalServer = async () => {
|
|
27
|
+
if (!localServerPromise) {
|
|
28
|
+
localServerPromise = import("./createServer.js").then((m) =>
|
|
29
|
+
m.createServer(this.config),
|
|
30
|
+
);
|
|
53
31
|
}
|
|
54
|
-
|
|
32
|
+
return localServerPromise;
|
|
33
|
+
};
|
|
55
34
|
|
|
56
|
-
#
|
|
57
|
-
|
|
35
|
+
#executeFetch = async (init: RequestInit): Promise<Response> => {
|
|
36
|
+
if (this.config.server) {
|
|
37
|
+
return fetch(this.config.server, init);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const localServer = await this.#getLocalServer();
|
|
41
|
+
return localServer.fetch("http://localhost/graphql", init);
|
|
42
|
+
};
|
|
58
43
|
|
|
59
44
|
fetch = async <TResult, TVariables>(
|
|
60
45
|
query: TypedDocumentString<TResult, TVariables>,
|
|
61
46
|
...[variables]: TVariables extends Record<string, never> ? [] : [TVariables]
|
|
62
|
-
) => {
|
|
47
|
+
): Promise<TResult> => {
|
|
63
48
|
const operationName = query.match(/query (\w+)/)?.[1];
|
|
64
49
|
|
|
65
|
-
await
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
case "remote": {
|
|
71
|
-
const response = await fetch(this.config.server!, {
|
|
72
|
-
method: "POST",
|
|
73
|
-
body,
|
|
74
|
-
headers: { "Content-Type": "application/json" },
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
if (!response.ok) {
|
|
78
|
-
throw new Error("Network response was not ok");
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const result = (await response.json()) as GraphQLResponse<TResult>;
|
|
82
|
-
throwIfError(result);
|
|
83
|
-
|
|
84
|
-
return result.data;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
case "in-memory": {
|
|
88
|
-
if (!localServerPromise) {
|
|
89
|
-
localServerPromise = this.#initializeLocalServer();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const localServer = await localServerPromise;
|
|
93
|
-
if (!localServer) throw new Error("Local server not initialized");
|
|
50
|
+
const response = await this.#executeFetch({
|
|
51
|
+
method: "POST",
|
|
52
|
+
body: JSON.stringify({ query, variables, operationName }),
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
});
|
|
94
55
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
body,
|
|
99
|
-
headers: { "Content-Type": "application/json" },
|
|
100
|
-
}),
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
if (!response.ok) {
|
|
104
|
-
throw new Error("Network response was not ok");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const result = (await response.json()) as GraphQLResponse<TResult>;
|
|
108
|
-
throwIfError(result);
|
|
109
|
-
|
|
110
|
-
return result.data;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
case "worker": {
|
|
114
|
-
if (!worker) {
|
|
115
|
-
worker = initializeWorker();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (!this.#port) {
|
|
119
|
-
const channel = new MessageChannel();
|
|
120
|
-
|
|
121
|
-
worker.port.postMessage({ port: channel.port2 }, [channel.port2]);
|
|
122
|
-
|
|
123
|
-
this.#port = channel.port1;
|
|
124
|
-
|
|
125
|
-
this.#port.onmessage = (e: MessageEvent<WorkerGraphQLMessage>) => {
|
|
126
|
-
const { id, body } = e.data;
|
|
127
|
-
const resolve = this.#pendingRequests.get(id);
|
|
128
|
-
if (resolve) {
|
|
129
|
-
const result = JSON.parse(body);
|
|
130
|
-
resolve(result);
|
|
131
|
-
this.#pendingRequests.delete(id);
|
|
132
|
-
} else {
|
|
133
|
-
// eslint-disable-next-line no-console
|
|
134
|
-
console.error(`No pending request found for id: ${id}`);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
this.#port.start();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const id = ulid();
|
|
142
|
-
|
|
143
|
-
const resultPromise = new Promise<GraphQLResponse<TResult>>(
|
|
144
|
-
(resolve) => {
|
|
145
|
-
this.#pendingRequests.set(id, resolve);
|
|
146
|
-
this.#port!.postMessage({ id, body } as WorkerGraphQLMessage);
|
|
147
|
-
},
|
|
148
|
-
);
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new Error("Network response was not ok");
|
|
58
|
+
}
|
|
149
59
|
|
|
150
|
-
|
|
151
|
-
|
|
60
|
+
const result = (await response.json()) as GraphQLResponse<TResult>;
|
|
61
|
+
throwIfError(result);
|
|
152
62
|
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
}
|
|
63
|
+
return result.data;
|
|
156
64
|
};
|
|
157
65
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { useLogger } from "@envelop/core";
|
|
2
2
|
import { createGraphQLServer } from "../../../oas/graphql/index.js";
|
|
3
|
+
import type { OpenApiPluginOptions } from "../index.js";
|
|
3
4
|
|
|
4
5
|
const map = new Map<string, number>();
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Creates the GraphQL server
|
|
8
9
|
*/
|
|
9
|
-
export const createServer = () =>
|
|
10
|
+
export const createServer = (config: OpenApiPluginOptions) =>
|
|
10
11
|
createGraphQLServer({
|
|
12
|
+
context: {
|
|
13
|
+
schemaImports: config.schemaImports,
|
|
14
|
+
},
|
|
11
15
|
plugins: [
|
|
12
16
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
13
17
|
useLogger({
|
|
@@ -22,7 +26,7 @@ export const createServer = () =>
|
|
|
22
26
|
if (start) {
|
|
23
27
|
// eslint-disable-next-line no-console
|
|
24
28
|
console.log(
|
|
25
|
-
|
|
29
|
+
`[zudoku:debug] ${args.operationName} query took ${performance.now() - start}ms`,
|
|
26
30
|
);
|
|
27
31
|
map.delete(`${startEvent}-${args.operationName}`);
|
|
28
32
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { useContext, useMemo } from "react";
|
|
1
|
+
import { useContext } from "react";
|
|
3
2
|
import type { TypedDocumentString } from "../graphql/graphql.js";
|
|
4
3
|
import { GraphQLContext } from "./GraphQLContext.js";
|
|
5
4
|
|
|
@@ -12,22 +11,8 @@ export const useCreateQuery = <TResult, TVariables>(
|
|
|
12
11
|
throw new Error("useGraphQL must be used within a GraphQLProvider");
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
const hash = useMemo(() => {
|
|
16
|
-
if (
|
|
17
|
-
typeof variables[0] === "object" &&
|
|
18
|
-
variables[0] != null &&
|
|
19
|
-
"input" in variables[0] &&
|
|
20
|
-
typeof variables[0].input === "function"
|
|
21
|
-
) {
|
|
22
|
-
// This is a pre-hashed name to ensure that the query key is consistent across server and client
|
|
23
|
-
return variables[0].input.name;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return hashit(variables[0] ?? {});
|
|
27
|
-
}, [variables]);
|
|
28
|
-
|
|
29
14
|
return {
|
|
30
15
|
queryFn: () => graphQLClient.fetch(query, ...variables),
|
|
31
|
-
queryKey: [query,
|
|
16
|
+
queryKey: [query, variables[0]],
|
|
32
17
|
} as const;
|
|
33
18
|
};
|
|
@@ -7,6 +7,7 @@ import { CirclePlayIcon, LogInIcon } from "lucide-react";
|
|
|
7
7
|
import type { SidebarItem } from "../../../config/validators/SidebarSchema.js";
|
|
8
8
|
import { useAuth } from "../../authentication/hook.js";
|
|
9
9
|
import { ColorMap } from "../../components/navigation/SidebarBadge.js";
|
|
10
|
+
import type { SchemaImports } from "../../oas/graphql/index.js";
|
|
10
11
|
import { Button } from "../../ui/Button.js";
|
|
11
12
|
import { joinPath } from "../../util/joinPath.js";
|
|
12
13
|
import { GraphQLClient } from "./client/GraphQLClient.js";
|
|
@@ -35,7 +36,7 @@ const GetCategoriesQuery = graphql(`
|
|
|
35
36
|
}
|
|
36
37
|
`);
|
|
37
38
|
|
|
38
|
-
type InternalOasPluginConfig = {
|
|
39
|
+
type InternalOasPluginConfig = { schemaImports?: SchemaImports };
|
|
39
40
|
|
|
40
41
|
const MethodColorMap: Record<string, keyof typeof ColorMap> = {
|
|
41
42
|
get: "green",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Command,
|
|
4
|
+
CommandEmpty,
|
|
5
|
+
CommandInput,
|
|
6
|
+
CommandItem,
|
|
7
|
+
CommandList,
|
|
8
|
+
} from "../../../ui/Command.js";
|
|
9
|
+
import {
|
|
10
|
+
Popover,
|
|
11
|
+
PopoverContent,
|
|
12
|
+
PopoverTrigger,
|
|
13
|
+
} from "../../../ui/Popover.js";
|
|
14
|
+
import { cn } from "../../../util/cn.js";
|
|
15
|
+
|
|
16
|
+
interface EnumSelectorProps {
|
|
17
|
+
value: string;
|
|
18
|
+
enumValues: string[];
|
|
19
|
+
onChange: (value: string) => void;
|
|
20
|
+
onValueSelected: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const EnumSelector = ({
|
|
24
|
+
value,
|
|
25
|
+
enumValues,
|
|
26
|
+
onChange,
|
|
27
|
+
onValueSelected,
|
|
28
|
+
}: EnumSelectorProps) => {
|
|
29
|
+
const [searchValue, setSearchValue] = useState("");
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
34
|
+
<PopoverTrigger asChild>
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
role="combobox"
|
|
38
|
+
className={cn(
|
|
39
|
+
"px-3 py-2 w-full border-0 shadow-none text-xs font-mono text-start hover:bg-accent/40 rounded border-transparent hover:bg-accent",
|
|
40
|
+
!value && "text-muted-foreground",
|
|
41
|
+
)}
|
|
42
|
+
>
|
|
43
|
+
{value || "Select value"}
|
|
44
|
+
</button>
|
|
45
|
+
</PopoverTrigger>
|
|
46
|
+
<PopoverContent
|
|
47
|
+
className="p-0 w-[--radix-popover-trigger-width] "
|
|
48
|
+
align="start"
|
|
49
|
+
sideOffset={3}
|
|
50
|
+
alignOffset={-3}
|
|
51
|
+
side="bottom"
|
|
52
|
+
>
|
|
53
|
+
<Command className="max-h-[180px]">
|
|
54
|
+
<CommandInput
|
|
55
|
+
placeholder="Enter value"
|
|
56
|
+
className="h-9 bg-transparent "
|
|
57
|
+
onValueChange={setSearchValue}
|
|
58
|
+
onKeyDown={(e) => {
|
|
59
|
+
if (e.key === "Enter") {
|
|
60
|
+
onChange(searchValue);
|
|
61
|
+
onValueSelected();
|
|
62
|
+
setOpen(false);
|
|
63
|
+
}
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
<CommandList>
|
|
67
|
+
<CommandEmpty>Use "{searchValue}"</CommandEmpty>
|
|
68
|
+
{enumValues.map((enumValue) => (
|
|
69
|
+
<CommandItem
|
|
70
|
+
key={enumValue}
|
|
71
|
+
value={enumValue}
|
|
72
|
+
onSelect={(selected) => {
|
|
73
|
+
onChange(selected);
|
|
74
|
+
onValueSelected();
|
|
75
|
+
setOpen(false);
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
{enumValue}
|
|
79
|
+
</CommandItem>
|
|
80
|
+
))}
|
|
81
|
+
</CommandList>
|
|
82
|
+
</Command>
|
|
83
|
+
</PopoverContent>
|
|
84
|
+
</Popover>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { EraserIcon } from "lucide-react";
|
|
2
2
|
import { Control, Controller, useFieldArray } from "react-hook-form";
|
|
3
|
+
import { Card } from "zudoku/ui/Card.js";
|
|
3
4
|
import { Button } from "../../../ui/Button.js";
|
|
4
5
|
import { Input } from "../../../ui/Input.js";
|
|
5
6
|
import { cn } from "../../../util/cn.js";
|
|
6
|
-
import { ColorizedParam } from "../ColorizedParam.js";
|
|
7
|
+
import { ColorizedParam, useParamColor } from "../ColorizedParam.js";
|
|
7
8
|
import type { PlaygroundForm } from "./Playground.js";
|
|
8
9
|
|
|
10
|
+
const PathParamLabel = ({ name }: { name: string }) => {
|
|
11
|
+
const color = useParamColor(name);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex items-center">
|
|
15
|
+
<div
|
|
16
|
+
className="w-2 h-2 rounded-full"
|
|
17
|
+
style={{ backgroundColor: `hsl(${color})` }}
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<ColorizedParam
|
|
21
|
+
slug={name}
|
|
22
|
+
name={name}
|
|
23
|
+
className="font-mono text-xs m-2 px-1"
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
9
29
|
export const PathParams = ({
|
|
10
30
|
control,
|
|
11
31
|
}: {
|
|
@@ -17,72 +37,60 @@ export const PathParams = ({
|
|
|
17
37
|
});
|
|
18
38
|
|
|
19
39
|
return (
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
control={control}
|
|
27
|
-
name={`pathParams.${i}.value`}
|
|
28
|
-
render={({ field }) => (
|
|
29
|
-
<div>
|
|
30
|
-
<ColorizedParam
|
|
31
|
-
slug={part.name}
|
|
32
|
-
name={part.name}
|
|
33
|
-
backgroundOpacity="25%"
|
|
34
|
-
borderOpacity={field.value ? "100%" : "0"}
|
|
35
|
-
className={cn(
|
|
36
|
-
"font-mono text-xs m-2",
|
|
37
|
-
!field.value && "opacity-60",
|
|
38
|
-
)}
|
|
39
|
-
/>
|
|
40
|
-
*
|
|
41
|
-
</div>
|
|
42
|
-
)}
|
|
43
|
-
/>
|
|
44
|
-
</td>
|
|
45
|
-
<td>
|
|
46
|
-
<div className="flex justify-between items-center">
|
|
47
|
-
<Controller
|
|
48
|
-
control={control}
|
|
49
|
-
name={`pathParams.${i}.value`}
|
|
50
|
-
render={({ field }) => (
|
|
51
|
-
<Input
|
|
52
|
-
{...field}
|
|
53
|
-
required
|
|
54
|
-
placeholder="Enter value"
|
|
55
|
-
className="border-0 shadow-none ring-0 font-mono text-xs"
|
|
56
|
-
/>
|
|
57
|
-
)}
|
|
58
|
-
/>
|
|
40
|
+
<Card className="rounded-lg">
|
|
41
|
+
<table className="w-full">
|
|
42
|
+
<tbody>
|
|
43
|
+
{fields.map((part, i) => (
|
|
44
|
+
<tr key={part.id} className="hover:bg-accent/40">
|
|
45
|
+
<td className="w-5/12">
|
|
59
46
|
<Controller
|
|
60
47
|
control={control}
|
|
61
48
|
name={`pathParams.${i}.value`}
|
|
62
|
-
render={(
|
|
63
|
-
<Button
|
|
64
|
-
size="icon"
|
|
65
|
-
type="button"
|
|
66
|
-
variant="ghost"
|
|
67
|
-
aria-label="Clear value"
|
|
68
|
-
className={cn(
|
|
69
|
-
"ms-2",
|
|
70
|
-
field.value.length === 0
|
|
71
|
-
? "opacity-0 pointer-events-none"
|
|
72
|
-
: "opacity-100",
|
|
73
|
-
)}
|
|
74
|
-
title="Clear value"
|
|
75
|
-
onClick={() => field.onChange("")}
|
|
76
|
-
>
|
|
77
|
-
<EraserIcon size={16} />
|
|
78
|
-
</Button>
|
|
79
|
-
)}
|
|
49
|
+
render={() => <PathParamLabel name={part.name} />}
|
|
80
50
|
/>
|
|
81
|
-
</
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
51
|
+
</td>
|
|
52
|
+
<td className="w-7/12">
|
|
53
|
+
<div className="flex justify-between items-center">
|
|
54
|
+
<Controller
|
|
55
|
+
control={control}
|
|
56
|
+
name={`pathParams.${i}.value`}
|
|
57
|
+
render={({ field }) => (
|
|
58
|
+
<Input
|
|
59
|
+
{...field}
|
|
60
|
+
required
|
|
61
|
+
placeholder="Enter value"
|
|
62
|
+
className="w-full border-0 shadow-none text-xs font-mono hover:bg-accent"
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
/>
|
|
66
|
+
<Controller
|
|
67
|
+
control={control}
|
|
68
|
+
name={`pathParams.${i}.value`}
|
|
69
|
+
render={({ field }) => (
|
|
70
|
+
<Button
|
|
71
|
+
size="icon"
|
|
72
|
+
type="button"
|
|
73
|
+
variant="ghost"
|
|
74
|
+
aria-label="Clear value"
|
|
75
|
+
className={cn(
|
|
76
|
+
"ms-2 mr-1",
|
|
77
|
+
field.value.length === 0
|
|
78
|
+
? "opacity-0 pointer-events-none"
|
|
79
|
+
: "opacity-100",
|
|
80
|
+
)}
|
|
81
|
+
title="Clear value"
|
|
82
|
+
onClick={() => field.onChange("")}
|
|
83
|
+
>
|
|
84
|
+
<EraserIcon size={16} />
|
|
85
|
+
</Button>
|
|
86
|
+
)}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</td>
|
|
90
|
+
</tr>
|
|
91
|
+
))}
|
|
92
|
+
</tbody>
|
|
93
|
+
</table>
|
|
94
|
+
</Card>
|
|
87
95
|
);
|
|
88
96
|
};
|
|
@@ -46,6 +46,8 @@ export type QueryParam = {
|
|
|
46
46
|
defaultValue?: string;
|
|
47
47
|
defaultActive?: boolean;
|
|
48
48
|
isRequired?: boolean;
|
|
49
|
+
enum?: string[];
|
|
50
|
+
type?: string;
|
|
49
51
|
};
|
|
50
52
|
export type PathParam = {
|
|
51
53
|
name: string;
|
|
@@ -55,9 +57,17 @@ export type PathParam = {
|
|
|
55
57
|
|
|
56
58
|
export type PlaygroundForm = {
|
|
57
59
|
body: string;
|
|
58
|
-
queryParams: Array<{
|
|
60
|
+
queryParams: Array<{
|
|
61
|
+
name: string;
|
|
62
|
+
value: string;
|
|
63
|
+
active: boolean;
|
|
64
|
+
enum?: string[];
|
|
65
|
+
}>;
|
|
59
66
|
pathParams: Array<{ name: string; value: string }>;
|
|
60
|
-
headers: Array<{
|
|
67
|
+
headers: Array<{
|
|
68
|
+
name: string;
|
|
69
|
+
value: string;
|
|
70
|
+
}>;
|
|
61
71
|
identity?: string;
|
|
62
72
|
};
|
|
63
73
|
|
|
@@ -92,6 +102,7 @@ export const Playground = ({
|
|
|
92
102
|
name: param.name,
|
|
93
103
|
value: param.defaultValue ?? "",
|
|
94
104
|
active: param.defaultActive ?? false,
|
|
105
|
+
enum: param.enum ?? [],
|
|
95
106
|
})),
|
|
96
107
|
pathParams: pathParams.map((param) => ({
|
|
97
108
|
name: param.name,
|
|
@@ -171,17 +182,19 @@ export const Playground = ({
|
|
|
171
182
|
const replaced = part.replace(/[:{}]/g, "");
|
|
172
183
|
const value = formState.pathParams.find((p) => p.name === replaced)?.value;
|
|
173
184
|
|
|
174
|
-
const pathParamValue =
|
|
175
|
-
<ColorizedParam
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
185
|
+
const pathParamValue = (
|
|
186
|
+
<ColorizedParam
|
|
187
|
+
backgroundOpacity="25%"
|
|
188
|
+
name={part}
|
|
189
|
+
slug={part}
|
|
190
|
+
title={
|
|
191
|
+
!value
|
|
192
|
+
? `Missing value for path parameter \`${replaced}\``
|
|
193
|
+
: undefined
|
|
194
|
+
}
|
|
182
195
|
>
|
|
183
|
-
{part}
|
|
184
|
-
</
|
|
196
|
+
{value ? encodeURIComponent(value) : part}
|
|
197
|
+
</ColorizedParam>
|
|
185
198
|
);
|
|
186
199
|
|
|
187
200
|
return (
|
|
@@ -240,7 +253,7 @@ export const Playground = ({
|
|
|
240
253
|
>
|
|
241
254
|
<form onSubmit={handleSubmit((data) => queryMutation.mutateAsync(data))}>
|
|
242
255
|
<div className="grid grid-cols-[8fr_7fr] text-sm h-full">
|
|
243
|
-
<div className="flex flex-col gap-4 p-
|
|
256
|
+
<div className="flex flex-col gap-4 p-4 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
|
|
244
257
|
<div className="flex gap-2 items-stretch">
|
|
245
258
|
<div className="flex flex-1 items-center w-full border rounded-md">
|
|
246
259
|
<div className="border-r p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono">
|