zudoku 0.3.0-dev.77 → 0.3.0-dev.79

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.
Files changed (109) hide show
  1. package/dist/config/validators/validate.d.ts +130 -130
  2. package/dist/lib/authentication/providers/openid.d.ts +1 -1
  3. package/dist/lib/authentication/providers/openid.js +1 -1
  4. package/dist/lib/authentication/providers/openid.js.map +1 -1
  5. package/dist/lib/components/DevPortal.js +11 -2
  6. package/dist/lib/components/DevPortal.js.map +1 -1
  7. package/dist/lib/components/Heading.d.ts +1 -1
  8. package/dist/lib/core/plugins.d.ts +3 -0
  9. package/dist/lib/core/plugins.js +1 -0
  10. package/dist/lib/core/plugins.js.map +1 -1
  11. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.d.ts +2 -1
  12. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +8 -16
  13. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
  14. package/dist/lib/plugins/openapi/Sidecar.js +18 -1
  15. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  16. package/dist/lib/plugins/openapi/graphql/gql.d.ts +6 -6
  17. package/dist/lib/plugins/openapi/graphql/gql.js +1 -1
  18. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  19. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +15 -15
  20. package/dist/lib/plugins/openapi/graphql/graphql.js +67 -67
  21. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  22. package/dist/lib/plugins/openapi/index.js +7 -1
  23. package/dist/lib/plugins/openapi/index.js.map +1 -1
  24. package/dist/lib/plugins/openapi/playground/PathParams.d.ts +2 -3
  25. package/dist/lib/plugins/openapi/playground/PathParams.js +3 -4
  26. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  27. package/dist/lib/plugins/openapi/playground/Playground.d.ts +22 -10
  28. package/dist/lib/plugins/openapi/playground/Playground.js +40 -28
  29. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  30. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.d.ts +3 -1
  31. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +1 -1
  32. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -1
  33. package/dist/lib/plugins/openapi/playground/QueryParams.d.ts +3 -2
  34. package/dist/lib/plugins/openapi/playground/QueryParams.js +10 -5
  35. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  36. package/dist/lib/plugins/openapi/playground/ResponseTab.d.ts +4 -0
  37. package/dist/lib/plugins/openapi/playground/ResponseTab.js +40 -0
  38. package/dist/lib/plugins/openapi/playground/ResponseTab.js.map +1 -0
  39. package/dist/lib/ui/Button.d.ts +5 -1
  40. package/dist/lib/ui/Button.js +24 -1
  41. package/dist/lib/ui/Button.js.map +1 -1
  42. package/dist/lib/util/fetchTimeout.d.ts +1 -0
  43. package/dist/lib/util/fetchTimeout.js +14 -0
  44. package/dist/lib/util/fetchTimeout.js.map +1 -0
  45. package/lib/{CategoryHeading-D0V23fMT.js → CategoryHeading-DCmchnA1.js} +2 -2
  46. package/lib/{CategoryHeading-D0V23fMT.js.map → CategoryHeading-DCmchnA1.js.map} +1 -1
  47. package/lib/{Combination-CgxP9BB4.js → Combination-CS4rK8IJ.js} +33 -39
  48. package/lib/Combination-CS4rK8IJ.js.map +1 -0
  49. package/lib/DeveloperHint-DQVwIery.js +10 -0
  50. package/lib/DeveloperHint-DQVwIery.js.map +1 -0
  51. package/lib/{Input-BcZoDid4.js → Input-GFpPXs5b.js} +3 -3
  52. package/lib/{Input-BcZoDid4.js.map → Input-GFpPXs5b.js.map} +1 -1
  53. package/lib/{Markdown-IsabnbGN.js → Markdown-QsZ-PHET.js} +3 -3
  54. package/lib/{Markdown-IsabnbGN.js.map → Markdown-QsZ-PHET.js.map} +1 -1
  55. package/lib/{MdxPage-B1B2Inj5.js → MdxPage-V4FCB0C_.js} +3 -3
  56. package/lib/{MdxPage-B1B2Inj5.js.map → MdxPage-V4FCB0C_.js.map} +1 -1
  57. package/lib/OperationList-C-M33Hxu.js +4368 -0
  58. package/lib/OperationList-C-M33Hxu.js.map +1 -0
  59. package/lib/{Route-D0Ub80Oa.js → Route-CogU1ofM.js} +2 -2
  60. package/lib/{Route-D0Ub80Oa.js.map → Route-CogU1ofM.js.map} +1 -1
  61. package/lib/{SlotletProvider-CJXWb2gw.js → SlotletProvider-D_Vz-7c_.js} +4 -4
  62. package/lib/{SlotletProvider-CJXWb2gw.js.map → SlotletProvider-D_Vz-7c_.js.map} +1 -1
  63. package/lib/Spinner-oNQQyp-I.js +244 -0
  64. package/lib/Spinner-oNQQyp-I.js.map +1 -0
  65. package/lib/index-D4bOMg7f.js +124 -0
  66. package/lib/index-D4bOMg7f.js.map +1 -0
  67. package/lib/index-Uqja2h2M.js +1960 -0
  68. package/lib/index-Uqja2h2M.js.map +1 -0
  69. package/lib/{urql-DMlBWUKL.js → urql-DrBfkb92.js} +2 -3
  70. package/lib/{urql-DMlBWUKL.js.map → urql-DrBfkb92.js.map} +1 -1
  71. package/lib/zudoku.auth-openid.js +1 -1
  72. package/lib/zudoku.auth-openid.js.map +1 -1
  73. package/lib/zudoku.components.js +352 -348
  74. package/lib/zudoku.components.js.map +1 -1
  75. package/lib/zudoku.openapi-worker.js +1 -1
  76. package/lib/zudoku.plugin-api-keys.js +7 -6
  77. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  78. package/lib/zudoku.plugin-custom-page.js +1 -1
  79. package/lib/zudoku.plugin-markdown.js +1 -1
  80. package/lib/zudoku.plugin-openapi.js +5 -4
  81. package/lib/zudoku.plugin-openapi.js.map +1 -1
  82. package/package.json +1 -1
  83. package/src/app/main.css +1 -1
  84. package/src/lib/authentication/providers/openid.tsx +1 -1
  85. package/src/lib/components/DevPortal.tsx +14 -5
  86. package/src/lib/core/plugins.ts +7 -0
  87. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +9 -17
  88. package/src/lib/plugins/openapi/Sidecar.tsx +23 -1
  89. package/src/lib/plugins/openapi/graphql/gql.ts +8 -8
  90. package/src/lib/plugins/openapi/graphql/graphql.ts +80 -80
  91. package/src/lib/plugins/openapi/index.tsx +21 -0
  92. package/src/lib/plugins/openapi/playground/PathParams.tsx +14 -20
  93. package/src/lib/plugins/openapi/playground/Playground.tsx +76 -56
  94. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +10 -6
  95. package/src/lib/plugins/openapi/playground/QueryParams.tsx +86 -72
  96. package/src/lib/plugins/openapi/playground/ResponseTab.tsx +76 -0
  97. package/src/lib/ui/Button.tsx +32 -2
  98. package/src/lib/util/fetchTimeout.tsx +21 -0
  99. package/dist/lib/ui/button-variants.d.ts +0 -4
  100. package/dist/lib/ui/button-variants.js +0 -25
  101. package/dist/lib/ui/button-variants.js.map +0 -1
  102. package/lib/Combination-CgxP9BB4.js.map +0 -1
  103. package/lib/OperationList-B2nsuf1v.js +0 -5471
  104. package/lib/OperationList-B2nsuf1v.js.map +0 -1
  105. package/lib/Spinner-BhtSoFka.js +0 -359
  106. package/lib/Spinner-BhtSoFka.js.map +0 -1
  107. package/lib/index-BC2Ob2BR.js +0 -727
  108. package/lib/index-BC2Ob2BR.js.map +0 -1
  109. package/src/lib/ui/button-variants.ts +0 -32
@@ -57,7 +57,7 @@ export type MediaTypeObject = {
57
57
  encoding?: Maybe<Array<EncodingItem>>;
58
58
  examples?: Maybe<Array<ExampleItem>>;
59
59
  mediaType: Scalars["String"]["output"];
60
- schema: Scalars["JSON"]["output"];
60
+ schema?: Maybe<Scalars["JSON"]["output"]>;
61
61
  };
62
62
 
63
63
  export type OperationItem = {
@@ -163,16 +163,6 @@ export type TagItem = {
163
163
  name: Scalars["String"]["output"];
164
164
  };
165
165
 
166
- export type GetServerQueryQueryVariables = Exact<{
167
- input: Scalars["JSON"]["input"];
168
- type: SchemaType;
169
- }>;
170
-
171
- export type GetServerQueryQuery = {
172
- __typename?: "Query";
173
- schema: { __typename?: "Schema"; url: string };
174
- };
175
-
176
166
  export type OperationsFragmentFragment = {
177
167
  __typename?: "OperationItem";
178
168
  slug: string;
@@ -206,7 +196,7 @@ export type OperationsFragmentFragment = {
206
196
  content?: Array<{
207
197
  __typename?: "MediaTypeObject";
208
198
  mediaType: string;
209
- schema: any;
199
+ schema?: any | null;
210
200
  encoding?: Array<{ __typename?: "EncodingItem"; name: string }> | null;
211
201
  }> | null;
212
202
  } | null;
@@ -218,7 +208,7 @@ export type OperationsFragmentFragment = {
218
208
  content?: Array<{
219
209
  __typename?: "MediaTypeObject";
220
210
  mediaType: string;
221
- schema: any;
211
+ schema?: any | null;
222
212
  encoding?: Array<{ __typename?: "EncodingItem"; name: string }> | null;
223
213
  }> | null;
224
214
  }>;
@@ -252,6 +242,16 @@ export type AllOperationsQuery = {
252
242
  };
253
243
  };
254
244
 
245
+ export type GetServerQueryQueryVariables = Exact<{
246
+ input: Scalars["JSON"]["input"];
247
+ type: SchemaType;
248
+ }>;
249
+
250
+ export type GetServerQueryQuery = {
251
+ __typename?: "Query";
252
+ schema: { __typename?: "Schema"; url: string };
253
+ };
254
+
255
255
  export type GetCategoriesQueryVariables = Exact<{
256
256
  input: Scalars["JSON"]["input"];
257
257
  type: SchemaType;
@@ -423,73 +423,6 @@ export const OperationsFragmentFragmentDoc = {
423
423
  },
424
424
  ],
425
425
  } as unknown as DocumentNode<OperationsFragmentFragment, unknown>;
426
- export const GetServerQueryDocument = {
427
- kind: "Document",
428
- definitions: [
429
- {
430
- kind: "OperationDefinition",
431
- operation: "query",
432
- name: { kind: "Name", value: "getServerQuery" },
433
- variableDefinitions: [
434
- {
435
- kind: "VariableDefinition",
436
- variable: {
437
- kind: "Variable",
438
- name: { kind: "Name", value: "input" },
439
- },
440
- type: {
441
- kind: "NonNullType",
442
- type: { kind: "NamedType", name: { kind: "Name", value: "JSON" } },
443
- },
444
- },
445
- {
446
- kind: "VariableDefinition",
447
- variable: { kind: "Variable", name: { kind: "Name", value: "type" } },
448
- type: {
449
- kind: "NonNullType",
450
- type: {
451
- kind: "NamedType",
452
- name: { kind: "Name", value: "SchemaType" },
453
- },
454
- },
455
- },
456
- ],
457
- selectionSet: {
458
- kind: "SelectionSet",
459
- selections: [
460
- {
461
- kind: "Field",
462
- name: { kind: "Name", value: "schema" },
463
- arguments: [
464
- {
465
- kind: "Argument",
466
- name: { kind: "Name", value: "input" },
467
- value: {
468
- kind: "Variable",
469
- name: { kind: "Name", value: "input" },
470
- },
471
- },
472
- {
473
- kind: "Argument",
474
- name: { kind: "Name", value: "type" },
475
- value: {
476
- kind: "Variable",
477
- name: { kind: "Name", value: "type" },
478
- },
479
- },
480
- ],
481
- selectionSet: {
482
- kind: "SelectionSet",
483
- selections: [
484
- { kind: "Field", name: { kind: "Name", value: "url" } },
485
- ],
486
- },
487
- },
488
- ],
489
- },
490
- },
491
- ],
492
- } as unknown as DocumentNode<GetServerQueryQuery, GetServerQueryQueryVariables>;
493
426
  export const AllOperationsDocument = {
494
427
  kind: "Document",
495
428
  definitions: [
@@ -735,6 +668,73 @@ export const AllOperationsDocument = {
735
668
  },
736
669
  ],
737
670
  } as unknown as DocumentNode<AllOperationsQuery, AllOperationsQueryVariables>;
671
+ export const GetServerQueryDocument = {
672
+ kind: "Document",
673
+ definitions: [
674
+ {
675
+ kind: "OperationDefinition",
676
+ operation: "query",
677
+ name: { kind: "Name", value: "getServerQuery" },
678
+ variableDefinitions: [
679
+ {
680
+ kind: "VariableDefinition",
681
+ variable: {
682
+ kind: "Variable",
683
+ name: { kind: "Name", value: "input" },
684
+ },
685
+ type: {
686
+ kind: "NonNullType",
687
+ type: { kind: "NamedType", name: { kind: "Name", value: "JSON" } },
688
+ },
689
+ },
690
+ {
691
+ kind: "VariableDefinition",
692
+ variable: { kind: "Variable", name: { kind: "Name", value: "type" } },
693
+ type: {
694
+ kind: "NonNullType",
695
+ type: {
696
+ kind: "NamedType",
697
+ name: { kind: "Name", value: "SchemaType" },
698
+ },
699
+ },
700
+ },
701
+ ],
702
+ selectionSet: {
703
+ kind: "SelectionSet",
704
+ selections: [
705
+ {
706
+ kind: "Field",
707
+ name: { kind: "Name", value: "schema" },
708
+ arguments: [
709
+ {
710
+ kind: "Argument",
711
+ name: { kind: "Name", value: "input" },
712
+ value: {
713
+ kind: "Variable",
714
+ name: { kind: "Name", value: "input" },
715
+ },
716
+ },
717
+ {
718
+ kind: "Argument",
719
+ name: { kind: "Name", value: "type" },
720
+ value: {
721
+ kind: "Variable",
722
+ name: { kind: "Name", value: "type" },
723
+ },
724
+ },
725
+ ],
726
+ selectionSet: {
727
+ kind: "SelectionSet",
728
+ selections: [
729
+ { kind: "Field", name: { kind: "Name", value: "url" } },
730
+ ],
731
+ },
732
+ },
733
+ ],
734
+ },
735
+ },
736
+ ],
737
+ } as unknown as DocumentNode<GetServerQueryQuery, GetServerQueryQueryVariables>;
738
738
  export const GetCategoriesDocument = {
739
739
  kind: "Document",
740
740
  definitions: [
@@ -11,10 +11,16 @@ import {
11
11
  fetchExchange,
12
12
  } from "./util/urql.js";
13
13
 
14
+ import { CirclePlayIcon } from "lucide-react";
14
15
  import { createClient } from "zudoku/openapi-worker";
15
16
  import { ErrorPage } from "../../components/ErrorPage.js";
16
17
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
18
+ import { Button } from "../../ui/Button.js";
17
19
  import { OasPluginConfig } from "./interfaces.js";
20
+ import {
21
+ PlaygroundDialog,
22
+ type PlaygroundDialogProps,
23
+ } from "./playground/PlaygroundDialog.js";
18
24
 
19
25
  const GetCategoriesQuery = graphql(`
20
26
  query GetCategories($input: JSON!, $type: SchemaType!) {
@@ -81,6 +87,21 @@ export const openApiPlugin = (
81
87
  return <link rel="preconnect" href={config.server} />;
82
88
  }
83
89
  },
90
+ getMdxComponents: () => ({
91
+ OpenPlaygroundButton: (props: PlaygroundDialogProps) => (
92
+ <div>
93
+ <PlaygroundDialog {...props}>
94
+ <Button className="gap-2 items-center" variant="outline">
95
+ {props.children ?? (
96
+ <>
97
+ Open in Playground <CirclePlayIcon size={16} />
98
+ </>
99
+ )}
100
+ </Button>
101
+ </PlaygroundDialog>
102
+ </div>
103
+ ),
104
+ }),
84
105
  getNavigation: async (path: string) => {
85
106
  if (!matchPath({ path: basePath, end: false }, path)) {
86
107
  return [];
@@ -1,11 +1,5 @@
1
1
  import { EraserIcon } from "lucide-react";
2
- import {
3
- Control,
4
- Controller,
5
- useFieldArray,
6
- useFormContext,
7
- UseFormRegister,
8
- } from "react-hook-form";
2
+ import { Control, Controller, useFieldArray } from "react-hook-form";
9
3
  import { Button } from "../../../ui/Button.js";
10
4
  import { Input } from "../../../ui/Input.js";
11
5
  import { cn } from "../../../util/cn.js";
@@ -14,12 +8,9 @@ import type { PlaygroundForm } from "./Playground.js";
14
8
 
15
9
  export const PathParams = ({
16
10
  control,
17
- register,
18
11
  }: {
19
- register: UseFormRegister<PlaygroundForm>;
20
12
  control: Control<PlaygroundForm>;
21
13
  }) => {
22
- const form = useFormContext<PlaygroundForm>();
23
14
  const { fields } = useFieldArray<PlaygroundForm>({
24
15
  control,
25
16
  name: "pathParams",
@@ -35,16 +26,19 @@ export const PathParams = ({
35
26
  control={control}
36
27
  name={`pathParams.${i}.value`}
37
28
  render={({ field }) => (
38
- <ColorizedParam
39
- slug={part.name}
40
- name={part.name}
41
- backgroundOpacity="25%"
42
- borderOpacity={field.value ? "100%" : "0"}
43
- className={cn(
44
- "font-mono text-xs m-2",
45
- !field.value && "opacity-60",
46
- )}
47
- />
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>
48
42
  )}
49
43
  />
50
44
  </td>
@@ -1,7 +1,6 @@
1
1
  import { useMutation } from "@tanstack/react-query";
2
- import { Fragment } from "react";
2
+ import { Fragment, useEffect, useRef } from "react";
3
3
  import { FormProvider, useForm } from "react-hook-form";
4
- import { useQuery } from "urql";
5
4
  import { useApiIdentities } from "../../../components/context/DevPortalProvider.js";
6
5
  import {
7
6
  Select,
@@ -11,28 +10,19 @@ import {
11
10
  SelectValue,
12
11
  } from "../../../components/Select.js";
13
12
  import { Spinner } from "../../../components/Spinner.js";
14
- import { SyntaxHighlight } from "../../../components/SyntaxHighlight.js";
15
13
  import { Button } from "../../../ui/Button.js";
16
14
  import { Card } from "../../../ui/Card.js";
17
15
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
16
+ import { fetchTimeout } from "../../../util/fetchTimeout.js";
18
17
  import { ColorizedParam } from "../ColorizedParam.js";
19
- import { useOasConfig } from "../context.js";
20
- import { graphql } from "../graphql/index.js";
21
18
  import { createUrl } from "./createUrl.js";
22
19
  import { Headers } from "./Headers.js";
23
20
  import { PathParams } from "./PathParams.js";
24
21
  import { QueryParams } from "./QueryParams.js";
22
+ import { ResponseTab } from "./ResponseTab.js";
25
23
 
26
24
  export const NO_IDENTITY = "__none";
27
25
 
28
- const GetServerQuery = graphql(/* GraphQL */ `
29
- query getServerQuery($input: JSON!, $type: SchemaType!) {
30
- schema(input: $input, type: $type) {
31
- url
32
- }
33
- }
34
- `);
35
-
36
26
  function mimeTypeToLanguage(mimeType: string) {
37
27
  const mimeTypeMapping = {
38
28
  "application/json": "json",
@@ -47,7 +37,7 @@ function mimeTypeToLanguage(mimeType: string) {
47
37
 
48
38
  return Object.entries(mimeTypeMapping).find(([mime]) =>
49
39
  mimeType.includes(mime),
50
- )?.[0][1];
40
+ )?.[1];
51
41
  }
52
42
 
53
43
  const statusCodeMap: Record<number, string> = {
@@ -65,63 +55,83 @@ const statusCodeMap: Record<number, string> = {
65
55
 
66
56
  export type Header = {
67
57
  name: string;
68
- value: string;
58
+ defaultValue?: string;
69
59
  };
70
60
  export type QueryParam = {
71
61
  name: string;
72
- value: string;
73
- active: boolean;
74
- isRequired: boolean;
62
+ defaultValue?: string;
63
+ defaultActive?: boolean;
64
+ isRequired?: boolean;
75
65
  };
76
66
  export type PathParam = {
77
67
  name: string;
78
- value: string;
68
+ defaultValue?: string;
69
+ isRequired?: boolean;
79
70
  };
80
71
 
81
72
  export type PlaygroundForm = {
82
73
  body: string;
83
- queryParams: QueryParam[];
84
- pathParams: PathParam[];
85
- headers: Header[];
74
+ queryParams: Array<{ name: string; value: string; active: boolean }>;
75
+ pathParams: Array<{ name: string; value: string }>;
76
+ headers: Array<{ name: string; value: string }>;
86
77
  identity?: string;
87
78
  };
88
79
 
89
80
  export type PlaygroundContentProps = {
81
+ server: string;
90
82
  url: string;
91
83
  method: string;
92
84
  headers?: Header[];
93
85
  queryParams?: QueryParam[];
94
86
  pathParams?: PathParam[];
95
- hasParams: boolean;
87
+ defaultBody?: string;
96
88
  };
97
89
 
98
90
  export const Playground = ({
91
+ server,
99
92
  url,
100
93
  method,
101
- headers = [{ name: "", value: "" }],
94
+ headers = [],
102
95
  queryParams = [],
103
96
  pathParams = [],
104
- hasParams,
97
+ defaultBody = "",
105
98
  }: PlaygroundContentProps) => {
106
- const variables = useOasConfig();
107
- const [server] = useQuery({ query: GetServerQuery, variables });
108
-
109
- const { register, control, handleSubmit, watch, ...form } =
99
+ const { register, control, handleSubmit, watch, setValue, ...form } =
110
100
  useForm<PlaygroundForm>({
111
101
  defaultValues: {
112
- body: "",
113
- queryParams,
114
- headers,
115
- pathParams,
102
+ body: defaultBody,
103
+ queryParams: queryParams.map((param) => ({
104
+ name: param.name,
105
+ value: param.defaultValue ?? "",
106
+ active: param.defaultActive ?? false,
107
+ })),
108
+ pathParams: pathParams.map((param) => ({
109
+ name: param.name,
110
+ value: param.defaultValue ?? "",
111
+ })),
112
+ headers: headers.map((header) => ({
113
+ name: header.name,
114
+ value: header.defaultValue ?? "",
115
+ })),
116
116
  identity: NO_IDENTITY,
117
117
  },
118
118
  });
119
119
  const formState = watch();
120
120
  const identities = useApiIdentities();
121
121
 
122
+ const setOnce = useRef(false);
123
+ useEffect(() => {
124
+ if (setOnce.current) return;
125
+ const firstIdentity = identities.data?.at(0);
126
+ if (firstIdentity) {
127
+ setValue("identity", firstIdentity.id);
128
+ setOnce.current = true;
129
+ }
130
+ }, [setValue, identities.data]);
131
+
122
132
  const queryMutation = useMutation({
123
133
  mutationFn: async (data: PlaygroundForm) => {
124
- const requestUrl = createUrl(server.data?.schema.url ?? "", url, data);
134
+ const requestUrl = createUrl(server, url, data);
125
135
  const start = performance.now();
126
136
 
127
137
  const request = new Request(requestUrl, {
@@ -138,7 +148,8 @@ export const Playground = ({
138
148
  ?.find((i) => i.id === data.identity)
139
149
  ?.authorizeRequest(request);
140
150
  }
141
- const response = await fetch(request);
151
+ const response = await fetchTimeout(request);
152
+ const time = performance.now() - start;
142
153
 
143
154
  const body = await response.text();
144
155
 
@@ -147,7 +158,7 @@ export const Playground = ({
147
158
  headers: response.headers,
148
159
  size: body.length,
149
160
  body,
150
- time: performance.now() - start,
161
+ time,
151
162
  };
152
163
  },
153
164
  });
@@ -159,7 +170,7 @@ export const Playground = ({
159
170
 
160
171
  const pathParamValue = value ? (
161
172
  <ColorizedParam backgroundOpacity="25%" name={part} slug={part}>
162
- {value}
173
+ {encodeURIComponent(value)}
163
174
  </ColorizedParam>
164
175
  ) : (
165
176
  <span
@@ -195,8 +206,11 @@ export const Playground = ({
195
206
  <wbr />
196
207
  </Fragment>
197
208
  ));
209
+
198
210
  return (
199
- <FormProvider {...{ register, control, handleSubmit, watch, ...form }}>
211
+ <FormProvider
212
+ {...{ register, control, handleSubmit, watch, setValue, ...form }}
213
+ >
200
214
  <form onSubmit={handleSubmit((data) => queryMutation.mutateAsync(data))}>
201
215
  <div className="grid grid-cols-2 text-sm h-full">
202
216
  <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">
@@ -215,10 +229,12 @@ export const Playground = ({
215
229
  Send
216
230
  </Button>
217
231
  </div>
218
- <Tabs defaultValue={hasParams ? "parameters" : "headers"}>
232
+ <Tabs
233
+ defaultValue={pathParams.length > 0 ? "parameters" : "headers"}
234
+ >
219
235
  <div className="flex justify-between">
220
236
  <TabsList>
221
- {hasParams && (
237
+ {pathParams.length > 0 && (
222
238
  <TabsTrigger value="parameters">Parameters</TabsTrigger>
223
239
  )}
224
240
  <TabsTrigger value="headers">
@@ -228,9 +244,11 @@ export const Playground = ({
228
244
  </TabsTrigger>
229
245
  <TabsTrigger
230
246
  value="body"
231
- disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
232
- method.toUpperCase(),
233
- )}
247
+ disabled={
248
+ !["POST", "PUT", "PATCH", "DELETE"].includes(
249
+ method.toUpperCase(),
250
+ )
251
+ }
234
252
  >
235
253
  Body
236
254
  </TabsTrigger>
@@ -238,7 +256,8 @@ export const Playground = ({
238
256
  <div className="flex gap-2 items-center">
239
257
  Auth:
240
258
  <Select
241
- onValueChange={(value) => form.setValue("identity", value)}
259
+ onValueChange={(value) => setValue("identity", value)}
260
+ value={formState.identity}
242
261
  defaultValue={formState.identity}
243
262
  >
244
263
  <SelectTrigger className="w-[180px] flex">
@@ -262,13 +281,13 @@ export const Playground = ({
262
281
  {pathParams.length > 0 && (
263
282
  <div className="flex flex-col gap-4 my-4">
264
283
  <span className="font-semibold">Path Parameters</span>
265
- <PathParams control={control} register={register} />
284
+ <PathParams control={control} />
266
285
  </div>
267
286
  )}
268
287
  {queryParams.length > 0 && (
269
288
  <div className="flex flex-col gap-4 my-4">
270
289
  <span className="font-semibold">Query Parameters</span>
271
- <QueryParams control={control} />
290
+ <QueryParams control={control} queryParams={queryParams} />
272
291
  </div>
273
292
  )}
274
293
  </TabsContent>
@@ -282,7 +301,12 @@ export const Playground = ({
282
301
  </div>
283
302
  <div className="flex flex-col gap-4 p-8 bg-muted/70">
284
303
  {queryMutation.error ? (
285
- <div>{queryMutation.error.message}</div>
304
+ <div>
305
+ Error:{" "}
306
+ {queryMutation.error.message ||
307
+ String(queryMutation.error) ||
308
+ "Unexpected error"}
309
+ </div>
286
310
  ) : queryMutation.data ? (
287
311
  <div className="flex flex-col gap-2">
288
312
  <div className="flex gap-2">
@@ -307,14 +331,10 @@ export const Playground = ({
307
331
  </TabsList>
308
332
 
309
333
  <TabsContent value="response">
310
- <Card className="shadow-none p-4">
311
- <SyntaxHighlight
312
- language={lang ?? "json"}
313
- noBackground
314
- className="overflow-x-auto "
315
- code={queryMutation.data.body ?? JSON.stringify("")}
316
- />
317
- </Card>
334
+ <ResponseTab
335
+ headers={queryMutation.data.headers}
336
+ body={queryMutation.data.body}
337
+ />
318
338
  </TabsContent>
319
339
  <TabsContent value="headers">
320
340
  <Card className="grid grid-cols-2 w-full gap-2.5 font-mono text-xs shadow-none p-4">
@@ -323,7 +343,7 @@ export const Playground = ({
323
343
  {headerEntries.map(([key, value]) => (
324
344
  <Fragment key={key}>
325
345
  <div>{key}</div>
326
- <div>{value}</div>
346
+ <div className="break-words">{value}</div>
327
347
  </Fragment>
328
348
  ))}
329
349
  </Card>
@@ -1,6 +1,6 @@
1
1
  import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
2
2
  import { CirclePlayIcon } from "lucide-react";
3
- import { useState } from "react";
3
+ import { type PropsWithChildren, useState } from "react";
4
4
  import {
5
5
  Dialog,
6
6
  DialogContent,
@@ -9,15 +9,19 @@ import {
9
9
  } from "../../../components/Dialog.js";
10
10
  import { Playground, type PlaygroundContentProps } from "./Playground.js";
11
11
 
12
- const PlaygroundDialog = (props: PlaygroundContentProps) => {
12
+ export type PlaygroundDialogProps = PropsWithChildren<PlaygroundContentProps>;
13
+
14
+ const PlaygroundDialog = (props: PlaygroundDialogProps) => {
13
15
  const [open, setOpen] = useState(false);
14
16
  return (
15
17
  <Dialog onOpenChange={(open) => setOpen(open)}>
16
18
  <DialogTrigger asChild>
17
- <CirclePlayIcon
18
- className="cursor-pointer text-primary hover:text-primary/80"
19
- size={16}
20
- />
19
+ {props.children ?? (
20
+ <CirclePlayIcon
21
+ className="cursor-pointer text-primary hover:text-primary/80"
22
+ size={16}
23
+ />
24
+ )}
21
25
  </DialogTrigger>
22
26
 
23
27
  <DialogContent