fumadocs-openapi 3.3.0 → 4.0.1

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.
@@ -0,0 +1,963 @@
1
+ import {
2
+ getDefaultValue,
3
+ getDefaultValues,
4
+ resolve,
5
+ useApiContext
6
+ } from "./chunk-RSFOKBAM.js";
7
+
8
+ // src/ui/playground.tsx
9
+ import { useMemo, useRef, useState as useState3 } from "react";
10
+ import { useForm } from "react-hook-form";
11
+ import useSWRImmutable from "swr/immutable";
12
+ import { Accordion, Accordions } from "fumadocs-ui/components/accordion";
13
+ import { cn as cn5, buttonVariants as buttonVariants2 } from "fumadocs-ui/components/api";
14
+
15
+ // src/ui/components/form.tsx
16
+ import { Slot } from "@radix-ui/react-slot";
17
+ import {
18
+ Controller,
19
+ FormProvider,
20
+ useFormContext
21
+ } from "react-hook-form";
22
+ import { cva } from "class-variance-authority";
23
+ import { createContext, forwardRef, useContext, useId } from "react";
24
+ import { cn } from "fumadocs-ui/components/api";
25
+ import { jsx } from "react/jsx-runtime";
26
+ var Form = FormProvider;
27
+ var FormFieldContext = createContext({
28
+ name: ""
29
+ });
30
+ var FormItemContext = createContext({ id: "" });
31
+ function FormField({ ...props }) {
32
+ return /* @__PURE__ */ jsx(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsx(Controller, { ...props }) });
33
+ }
34
+ function useFormField() {
35
+ const fieldContext = useContext(FormFieldContext);
36
+ const { id } = useContext(FormItemContext);
37
+ const { getFieldState, formState } = useFormContext();
38
+ const fieldState = getFieldState(fieldContext.name, formState);
39
+ return {
40
+ id,
41
+ name: fieldContext.name,
42
+ formItemId: `${id}-form-item`,
43
+ formDescriptionId: `${id}-form-item-description`,
44
+ isError: Boolean(fieldState.error)
45
+ };
46
+ }
47
+ var FormItem = forwardRef(({ className, ...props }, ref) => {
48
+ const id = useId();
49
+ return /* @__PURE__ */ jsx(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsx(
50
+ "div",
51
+ {
52
+ ref,
53
+ className: cn("flex flex-col gap-1", className),
54
+ ...props
55
+ }
56
+ ) });
57
+ });
58
+ FormItem.displayName = "FormItem";
59
+ var labelVariants = cva(
60
+ "text-xs font-medium text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
61
+ );
62
+ var FormLabel = forwardRef(({ className, ...props }, ref) => {
63
+ const { isError, formItemId } = useFormField();
64
+ return /* @__PURE__ */ jsx(
65
+ "label",
66
+ {
67
+ ref,
68
+ className: cn(labelVariants(), isError && "text-red-500", className),
69
+ htmlFor: formItemId,
70
+ ...props
71
+ }
72
+ );
73
+ });
74
+ FormLabel.displayName = "FormLabel";
75
+ var FormControl = forwardRef(({ ...props }, ref) => {
76
+ const { isError, formItemId, formDescriptionId } = useFormField();
77
+ return /* @__PURE__ */ jsx(
78
+ Slot,
79
+ {
80
+ ref,
81
+ id: formItemId,
82
+ "aria-describedby": formDescriptionId,
83
+ "aria-invalid": isError,
84
+ ...props
85
+ }
86
+ );
87
+ });
88
+ FormControl.displayName = "FormControl";
89
+ var FormDescription = forwardRef(({ className, ...props }, ref) => {
90
+ const { formDescriptionId } = useFormField();
91
+ return /* @__PURE__ */ jsx(
92
+ "p",
93
+ {
94
+ ref,
95
+ id: formDescriptionId,
96
+ className: cn("text-xs text-muted-foreground", className),
97
+ ...props
98
+ }
99
+ );
100
+ });
101
+ FormDescription.displayName = "FormDescription";
102
+
103
+ // src/ui/fetcher.ts
104
+ import { CircleCheckIcon, CircleXIcon } from "lucide-react";
105
+ function createBodyFromValue(value, schema, references, dynamic) {
106
+ return convertValue("body", value, schema, references, dynamic);
107
+ }
108
+ function convertValue(fieldName, value, schema, references, dynamic) {
109
+ const isEmpty = value === "" || value === void 0 || value === null;
110
+ if (isEmpty && schema.isRequired)
111
+ return schema.type === "boolean" ? false : "";
112
+ else if (isEmpty) return void 0;
113
+ if (Array.isArray(value) && schema.type === "array") {
114
+ return value.map(
115
+ (item, index) => convertValue(
116
+ `${fieldName}.${String(index)}`,
117
+ item,
118
+ resolve(schema.items, references),
119
+ references,
120
+ dynamic
121
+ )
122
+ );
123
+ }
124
+ if (schema.type === "switcher") {
125
+ return convertDynamicValue(fieldName, value, references, dynamic);
126
+ }
127
+ if (typeof value === "object" && schema.type === "object") {
128
+ const entries = Object.keys(value).map((key) => {
129
+ const prop = value[key];
130
+ const propFieldName = `${fieldName}.${key}`;
131
+ if (key in schema.properties) {
132
+ return [
133
+ key,
134
+ convertValue(
135
+ propFieldName,
136
+ prop,
137
+ resolve(schema.properties[key], references),
138
+ references,
139
+ dynamic
140
+ )
141
+ ];
142
+ }
143
+ if (schema.additionalProperties) {
144
+ return [
145
+ key,
146
+ convertDynamicValue(propFieldName, prop, references, dynamic)
147
+ ];
148
+ }
149
+ console.warn("Could not resolve field", propFieldName, dynamic);
150
+ return [key, prop];
151
+ });
152
+ return Object.fromEntries(entries);
153
+ }
154
+ switch (schema.type) {
155
+ case "number":
156
+ return Number(value);
157
+ case "boolean":
158
+ return value === "null" ? void 0 : value === "true";
159
+ default:
160
+ return String(value);
161
+ }
162
+ }
163
+ function convertDynamicValue(fieldName, value, references, dynamic) {
164
+ const fieldDynamic = dynamic.get(fieldName);
165
+ return convertValue(
166
+ fieldName,
167
+ value,
168
+ fieldDynamic?.type === "field" ? resolve(fieldDynamic.schema, references) : { type: "null", isRequired: false },
169
+ references,
170
+ dynamic
171
+ );
172
+ }
173
+ var statusMap = {
174
+ 400: { description: "Bad Request", color: "text-red-500", icon: CircleXIcon },
175
+ 401: {
176
+ description: "Unauthorized",
177
+ color: "text-red-500",
178
+ icon: CircleXIcon
179
+ },
180
+ 403: { description: "Forbidden", color: "text-red-500", icon: CircleXIcon },
181
+ 404: {
182
+ description: "Not Found",
183
+ color: "text-muted-foreground",
184
+ icon: CircleXIcon
185
+ },
186
+ 500: {
187
+ description: "Internal Server Error",
188
+ color: "text-red-500",
189
+ icon: CircleXIcon
190
+ }
191
+ };
192
+ function getStatusInfo(status) {
193
+ if (status in statusMap) {
194
+ return statusMap[status];
195
+ }
196
+ if (status >= 200 && status < 300) {
197
+ return {
198
+ description: "Successful",
199
+ color: "text-green-500",
200
+ icon: CircleCheckIcon
201
+ };
202
+ }
203
+ if (status >= 400) {
204
+ return { description: "Error", color: "text-red-500", icon: CircleXIcon };
205
+ }
206
+ return {
207
+ description: "No Description",
208
+ color: "text-muted-foreground",
209
+ icon: CircleXIcon
210
+ };
211
+ }
212
+
213
+ // src/ui/inputs.tsx
214
+ import {
215
+ useCallback,
216
+ useState
217
+ } from "react";
218
+ import { Plus, Trash2 } from "lucide-react";
219
+ import { useFieldArray, useFormContext as useFormContext2 } from "react-hook-form";
220
+ import { cn as cn4, buttonVariants, useOnChange } from "fumadocs-ui/components/api";
221
+
222
+ // src/ui/components/select.tsx
223
+ import * as SelectPrimitive from "@radix-ui/react-select";
224
+ import { Check, ChevronDown, ChevronUp } from "lucide-react";
225
+ import { forwardRef as forwardRef2 } from "react";
226
+ import { cn as cn2 } from "fumadocs-ui/components/api";
227
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
228
+ var Select = SelectPrimitive.Root;
229
+ var SelectValue = SelectPrimitive.Value;
230
+ var SelectTrigger = forwardRef2(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
231
+ SelectPrimitive.Trigger,
232
+ {
233
+ ref,
234
+ className: cn2(
235
+ "flex h-10 items-center justify-between rounded-md border px-3 py-2 text-sm text-foreground hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
236
+ className
237
+ ),
238
+ ...props,
239
+ children: [
240
+ children,
241
+ /* @__PURE__ */ jsx2(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx2(ChevronDown, { className: "size-4 text-muted-foreground" }) })
242
+ ]
243
+ }
244
+ ));
245
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
246
+ var SelectScrollUpButton = forwardRef2(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
247
+ SelectPrimitive.ScrollUpButton,
248
+ {
249
+ ref,
250
+ className: cn2("flex items-center justify-center py-1", className),
251
+ ...props,
252
+ children: /* @__PURE__ */ jsx2(ChevronUp, { className: "size-4" })
253
+ }
254
+ ));
255
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
256
+ var SelectScrollDownButton = forwardRef2(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
257
+ SelectPrimitive.ScrollDownButton,
258
+ {
259
+ ref,
260
+ className: cn2("flex items-center justify-center py-1", className),
261
+ ...props,
262
+ children: /* @__PURE__ */ jsx2(ChevronDown, { className: "size-4" })
263
+ }
264
+ ));
265
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
266
+ var SelectContent = forwardRef2(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx2(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
267
+ SelectPrimitive.Content,
268
+ {
269
+ ref,
270
+ className: cn2(
271
+ "z-50 overflow-hidden rounded-lg border bg-popover text-popover-foreground shadow-md data-[state=closed]:animate-popover-out data-[state=open]:animate-popover-in",
272
+ className
273
+ ),
274
+ position,
275
+ ...props,
276
+ children: [
277
+ /* @__PURE__ */ jsx2(SelectScrollUpButton, {}),
278
+ /* @__PURE__ */ jsx2(
279
+ SelectPrimitive.Viewport,
280
+ {
281
+ className: cn2(
282
+ "p-1",
283
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
284
+ ),
285
+ children
286
+ }
287
+ ),
288
+ /* @__PURE__ */ jsx2(SelectScrollDownButton, {})
289
+ ]
290
+ }
291
+ ) }));
292
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
293
+ var SelectLabel = forwardRef2(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
294
+ SelectPrimitive.Label,
295
+ {
296
+ ref,
297
+ className: cn2("py-1.5 pe-2 ps-6 text-sm font-semibold", className),
298
+ ...props
299
+ }
300
+ ));
301
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
302
+ var SelectItem = forwardRef2(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
303
+ SelectPrimitive.Item,
304
+ {
305
+ ref,
306
+ className: cn2(
307
+ "flex select-none flex-row items-center rounded-md py-1.5 pe-2 ps-6 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
308
+ className
309
+ ),
310
+ ...props,
311
+ children: [
312
+ /* @__PURE__ */ jsx2(SelectPrimitive.ItemIndicator, { className: "absolute start-2", children: /* @__PURE__ */ jsx2(Check, { className: "size-4" }) }),
313
+ /* @__PURE__ */ jsx2(SelectPrimitive.ItemText, { children })
314
+ ]
315
+ }
316
+ ));
317
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
318
+ var SelectSeparator = forwardRef2(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
319
+ SelectPrimitive.Separator,
320
+ {
321
+ ref,
322
+ className: cn2("my-1 h-px bg-muted", className),
323
+ ...props
324
+ }
325
+ ));
326
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
327
+
328
+ // src/ui/components/input.tsx
329
+ import * as React from "react";
330
+ import { cn as cn3 } from "fumadocs-ui/components/api";
331
+ import { jsx as jsx3 } from "react/jsx-runtime";
332
+ var Input = React.forwardRef(
333
+ ({ className, type, ...props }, ref) => {
334
+ return /* @__PURE__ */ jsx3(
335
+ "input",
336
+ {
337
+ type,
338
+ className: cn3(
339
+ "flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm text-foreground transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
340
+ className
341
+ ),
342
+ ref,
343
+ ...props
344
+ }
345
+ );
346
+ }
347
+ );
348
+ Input.displayName = "Input";
349
+
350
+ // src/ui/contexts/schema.tsx
351
+ import { createContext as createContext2, useContext as useContext2 } from "react";
352
+ var SchemaContext = createContext2(
353
+ void 0
354
+ );
355
+ function useSchemaContext() {
356
+ const ctx = useContext2(SchemaContext);
357
+ if (!ctx) throw new Error("Missing provider");
358
+ return ctx;
359
+ }
360
+
361
+ // src/ui/inputs.tsx
362
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
363
+ function renderInner({ field, ...props }) {
364
+ if (field.type === "object")
365
+ return /* @__PURE__ */ jsx4(
366
+ ObjectInput,
367
+ {
368
+ field,
369
+ ...props,
370
+ className: cn4("rounded-lg border bg-accent/20 p-3", props.className)
371
+ }
372
+ );
373
+ if (field.type === "switcher")
374
+ return /* @__PURE__ */ jsx4(Switcher, { inline: true, field, ...props });
375
+ if (field.type === "array")
376
+ return /* @__PURE__ */ jsx4(
377
+ ArrayInput,
378
+ {
379
+ field,
380
+ ...props,
381
+ className: cn4("rounded-lg border bg-background p-3", props.className)
382
+ }
383
+ );
384
+ if (field.type === "null") return null;
385
+ return /* @__PURE__ */ jsx4(NormalInput, { field, ...props });
386
+ }
387
+ function InputContainer({
388
+ toolbar,
389
+ name,
390
+ required,
391
+ type,
392
+ description,
393
+ inline = false,
394
+ ...props
395
+ }) {
396
+ return /* @__PURE__ */ jsxs2("div", { ...props, className: cn4("flex flex-col gap-2", props.className), children: [
397
+ /* @__PURE__ */ jsxs2("div", { className: cn4(labelVariants(), "inline-flex items-center gap-1"), children: [
398
+ name,
399
+ required ? /* @__PURE__ */ jsx4("span", { className: "text-red-500", children: "*" }) : null,
400
+ /* @__PURE__ */ jsx4("div", { className: "flex-1" }),
401
+ type ? /* @__PURE__ */ jsx4("code", { className: "text-xs text-muted-foreground", children: type }) : null,
402
+ toolbar
403
+ ] }),
404
+ !inline ? /* @__PURE__ */ jsx4("p", { className: "text-xs", children: description }) : null,
405
+ props.children
406
+ ] });
407
+ }
408
+ function ObjectInput({
409
+ field,
410
+ fieldName,
411
+ ...props
412
+ }) {
413
+ const { references } = useSchemaContext();
414
+ return /* @__PURE__ */ jsxs2("div", { ...props, className: cn4("flex flex-col gap-4", props.className), children: [
415
+ Object.entries(field.properties).map(([key, child]) => /* @__PURE__ */ jsx4(
416
+ InputField,
417
+ {
418
+ name: key,
419
+ field: resolve(child, references),
420
+ fieldName: `${fieldName}.${key}`
421
+ },
422
+ key
423
+ )),
424
+ field.additionalProperties ? /* @__PURE__ */ jsx4(
425
+ AdditionalProperties,
426
+ {
427
+ fieldName,
428
+ type: field.additionalProperties
429
+ }
430
+ ) : null
431
+ ] });
432
+ }
433
+ function AdditionalProperties({
434
+ fieldName,
435
+ type
436
+ }) {
437
+ const { control, setValue } = useFormContext2();
438
+ const { references, dynamic } = useSchemaContext();
439
+ const [nextName, setNextName] = useState("");
440
+ const [properties, setProperties] = useState(() => {
441
+ const d = dynamic.current.get(`additional_${fieldName}`);
442
+ if (d?.type === "object") return d.properties;
443
+ return [];
444
+ });
445
+ useOnChange(properties, () => {
446
+ dynamic.current.set(`additional_${fieldName}`, {
447
+ type: "object",
448
+ properties
449
+ });
450
+ });
451
+ const onAppend = useCallback(() => {
452
+ const name = nextName.trim();
453
+ if (name.length === 0) return;
454
+ setProperties((p) => {
455
+ if (p.includes(name)) return p;
456
+ setValue(`${fieldName}.${name}`, "");
457
+ setNextName("");
458
+ return [...p, name];
459
+ });
460
+ }, [nextName, setValue, fieldName]);
461
+ const types = typeof type === "string" ? resolveDynamicTypes(references[type], references) : void 0;
462
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
463
+ properties.map((item) => /* @__PURE__ */ jsx4(
464
+ Switcher,
465
+ {
466
+ name: item,
467
+ field: {
468
+ type: "switcher",
469
+ items: types ?? anyFields,
470
+ isRequired: false
471
+ },
472
+ fieldName: `${fieldName}.${item}`,
473
+ toolbar: /* @__PURE__ */ jsx4(
474
+ "button",
475
+ {
476
+ type: "button",
477
+ "aria-label": "Remove Item",
478
+ className: cn4(
479
+ buttonVariants({
480
+ color: "secondary",
481
+ size: "sm"
482
+ })
483
+ ),
484
+ onClick: () => {
485
+ setProperties((p) => p.filter((prop) => prop !== item));
486
+ control.unregister(`${fieldName}.${item}`);
487
+ },
488
+ children: /* @__PURE__ */ jsx4(Trash2, { className: "size-4" })
489
+ }
490
+ )
491
+ },
492
+ item
493
+ )),
494
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-row gap-1", children: [
495
+ /* @__PURE__ */ jsx4(
496
+ Input,
497
+ {
498
+ value: nextName,
499
+ placeholder: "Enter Property Name",
500
+ onChange: useCallback((e) => {
501
+ setNextName(e.target.value);
502
+ }, []),
503
+ onKeyDown: useCallback(
504
+ (e) => {
505
+ if (e.key === "Enter") {
506
+ onAppend();
507
+ e.preventDefault();
508
+ }
509
+ },
510
+ [onAppend]
511
+ )
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsx4(
515
+ "button",
516
+ {
517
+ type: "button",
518
+ className: cn4(buttonVariants({ color: "secondary" })),
519
+ onClick: onAppend,
520
+ children: "New"
521
+ }
522
+ )
523
+ ] })
524
+ ] });
525
+ }
526
+ function resolveDynamicTypes(schema, references) {
527
+ if (schema.type !== "switcher") return { [schema.type]: schema };
528
+ return Object.fromEntries(
529
+ Object.entries(schema.items).map(([key, value]) => [
530
+ key,
531
+ resolve(value, references)
532
+ ])
533
+ );
534
+ }
535
+ var anyFields = {
536
+ string: {
537
+ type: "string",
538
+ isRequired: false,
539
+ defaultValue: ""
540
+ },
541
+ boolean: {
542
+ type: "boolean",
543
+ isRequired: false,
544
+ defaultValue: ""
545
+ },
546
+ number: {
547
+ type: "number",
548
+ isRequired: false,
549
+ defaultValue: ""
550
+ },
551
+ object: {
552
+ type: "object",
553
+ properties: {},
554
+ additionalProperties: true,
555
+ isRequired: false
556
+ }
557
+ };
558
+ anyFields.array = {
559
+ type: "array",
560
+ isRequired: false,
561
+ items: {
562
+ type: "switcher",
563
+ isRequired: false,
564
+ items: anyFields
565
+ }
566
+ };
567
+ function Switcher({
568
+ field,
569
+ fieldName,
570
+ ...props
571
+ }) {
572
+ const { references, dynamic } = useSchemaContext();
573
+ const items = Object.keys(field.items);
574
+ const [value, setValue] = useState(() => {
575
+ const d = dynamic.current.get(fieldName);
576
+ if (d?.type === "field") {
577
+ const cached = items.find((item) => d.schema === field.items[item]);
578
+ if (cached) return cached;
579
+ }
580
+ return items[0];
581
+ });
582
+ useOnChange(value, () => {
583
+ if (!value) return;
584
+ dynamic.current.set(fieldName, {
585
+ type: "field",
586
+ schema: field.items[value]
587
+ });
588
+ });
589
+ return /* @__PURE__ */ jsx4(
590
+ InputContainer,
591
+ {
592
+ required: field.isRequired,
593
+ description: field.description,
594
+ ...props,
595
+ toolbar: /* @__PURE__ */ jsxs2(Fragment, { children: [
596
+ /* @__PURE__ */ jsxs2(Select, { value, onValueChange: setValue, children: [
597
+ /* @__PURE__ */ jsx4(SelectTrigger, { className: "h-auto p-1 text-xs", children: /* @__PURE__ */ jsx4(SelectValue, {}) }),
598
+ /* @__PURE__ */ jsx4(SelectContent, { children: items.map((item) => /* @__PURE__ */ jsx4(SelectItem, { value: item, children: item }, item)) })
599
+ ] }),
600
+ props.toolbar
601
+ ] }),
602
+ children: renderInner({
603
+ field: resolve(field.items[value], references),
604
+ fieldName
605
+ })
606
+ }
607
+ );
608
+ }
609
+ function InputField({
610
+ field,
611
+ fieldName,
612
+ ...props
613
+ }) {
614
+ const { references } = useSchemaContext();
615
+ if (field.type === "null") return null;
616
+ if (field.type === "object") {
617
+ return /* @__PURE__ */ jsx4(
618
+ InputContainer,
619
+ {
620
+ required: field.isRequired,
621
+ type: field.type,
622
+ description: field.description,
623
+ ...props,
624
+ children: /* @__PURE__ */ jsx4(
625
+ ObjectInput,
626
+ {
627
+ field,
628
+ fieldName,
629
+ className: "rounded-lg border bg-accent/20 p-3"
630
+ }
631
+ )
632
+ }
633
+ );
634
+ }
635
+ if (field.type === "array") {
636
+ return /* @__PURE__ */ jsx4(
637
+ InputContainer,
638
+ {
639
+ required: field.isRequired,
640
+ description: field.description ?? resolve(field.items, references).description,
641
+ type: "array",
642
+ ...props,
643
+ children: /* @__PURE__ */ jsx4(
644
+ ArrayInput,
645
+ {
646
+ fieldName,
647
+ field,
648
+ className: "rounded-lg border bg-background p-3"
649
+ }
650
+ )
651
+ }
652
+ );
653
+ }
654
+ if (field.type === "switcher") {
655
+ return /* @__PURE__ */ jsx4(Switcher, { field, fieldName, ...props });
656
+ }
657
+ const { toolbar, inline = false, name, ...rest } = props;
658
+ return /* @__PURE__ */ jsx4(
659
+ NormalInput,
660
+ {
661
+ field,
662
+ fieldName,
663
+ header: /* @__PURE__ */ jsxs2(Fragment, { children: [
664
+ /* @__PURE__ */ jsxs2(FormLabel, { className: "inline-flex items-center gap-1", children: [
665
+ name,
666
+ field.isRequired ? /* @__PURE__ */ jsx4("span", { className: "text-red-500", children: "*" }) : null,
667
+ /* @__PURE__ */ jsx4("code", { className: "ms-auto text-xs text-muted-foreground", children: field.type }),
668
+ toolbar
669
+ ] }),
670
+ !inline ? /* @__PURE__ */ jsx4(FormDescription, { className: "text-xs", children: field.description }) : null
671
+ ] }),
672
+ ...rest
673
+ }
674
+ );
675
+ }
676
+ function NormalInput({
677
+ fieldName,
678
+ header,
679
+ field,
680
+ ...props
681
+ }) {
682
+ const { control } = useFormContext2();
683
+ if (field.type === "boolean") {
684
+ return /* @__PURE__ */ jsx4(
685
+ FormField,
686
+ {
687
+ control,
688
+ name: fieldName,
689
+ render: ({ field: { value, onChange, ...restField } }) => /* @__PURE__ */ jsxs2(FormItem, { ...props, children: [
690
+ header,
691
+ /* @__PURE__ */ jsxs2(
692
+ Select,
693
+ {
694
+ value,
695
+ onValueChange: onChange,
696
+ disabled: restField.disabled,
697
+ children: [
698
+ /* @__PURE__ */ jsx4(FormControl, { children: /* @__PURE__ */ jsx4(SelectTrigger, { ...restField, children: /* @__PURE__ */ jsx4(SelectValue, {}) }) }),
699
+ /* @__PURE__ */ jsxs2(SelectContent, { children: [
700
+ /* @__PURE__ */ jsx4(SelectItem, { value: "true", children: "True" }),
701
+ /* @__PURE__ */ jsx4(SelectItem, { value: "false", children: "False" }),
702
+ field.isRequired ? null : /* @__PURE__ */ jsx4(SelectItem, { value: "null", children: "Null" })
703
+ ] })
704
+ ]
705
+ }
706
+ )
707
+ ] })
708
+ }
709
+ );
710
+ }
711
+ return /* @__PURE__ */ jsx4(
712
+ FormField,
713
+ {
714
+ control,
715
+ name: fieldName,
716
+ render: ({ field: { value, ...restField } }) => /* @__PURE__ */ jsxs2(FormItem, { ...props, children: [
717
+ header,
718
+ /* @__PURE__ */ jsx4(FormControl, { children: /* @__PURE__ */ jsx4(
719
+ Input,
720
+ {
721
+ placeholder: "Enter value",
722
+ type: field.type === "string" ? "text" : "number",
723
+ value,
724
+ ...restField
725
+ }
726
+ ) })
727
+ ] })
728
+ }
729
+ );
730
+ }
731
+ function ArrayInput({
732
+ fieldName,
733
+ field,
734
+ ...props
735
+ }) {
736
+ const { references } = useSchemaContext();
737
+ const items = resolve(field.items, references);
738
+ const { fields, append, remove } = useFieldArray({
739
+ name: fieldName
740
+ });
741
+ const handleAppend = useCallback(() => {
742
+ append(getDefaultValue(items, references));
743
+ }, [append, references, items]);
744
+ return /* @__PURE__ */ jsxs2("div", { ...props, className: cn4("flex flex-col gap-4", props.className), children: [
745
+ fields.map((item, index) => /* @__PURE__ */ jsx4(
746
+ InputField,
747
+ {
748
+ inline: true,
749
+ name: `Item ${String(index + 1)}`,
750
+ field: items,
751
+ fieldName: `${fieldName}.${String(index)}`,
752
+ className: "flex-1",
753
+ toolbar: /* @__PURE__ */ jsx4(
754
+ "button",
755
+ {
756
+ type: "button",
757
+ "aria-label": "Remove Item",
758
+ className: cn4(
759
+ buttonVariants({
760
+ color: "secondary",
761
+ size: "sm"
762
+ })
763
+ ),
764
+ onClick: () => {
765
+ remove(index);
766
+ },
767
+ children: /* @__PURE__ */ jsx4(Trash2, { className: "size-4" })
768
+ }
769
+ )
770
+ },
771
+ item.id
772
+ )),
773
+ /* @__PURE__ */ jsxs2(
774
+ "button",
775
+ {
776
+ type: "button",
777
+ className: cn4(
778
+ buttonVariants({ color: "secondary", className: "gap-1.5" })
779
+ ),
780
+ onClick: handleAppend,
781
+ children: [
782
+ /* @__PURE__ */ jsx4(Plus, { className: "size-4" }),
783
+ "New Item"
784
+ ]
785
+ }
786
+ )
787
+ ] });
788
+ }
789
+
790
+ // src/ui/components/codeblock.tsx
791
+ import { useLayoutEffect, useState as useState2 } from "react";
792
+ import * as Base from "fumadocs-ui/components/codeblock";
793
+ import { jsx as jsx5 } from "react/jsx-runtime";
794
+ function CodeBlock2({
795
+ code,
796
+ lang = "json",
797
+ ...props
798
+ }) {
799
+ const { highlighter } = useApiContext();
800
+ const [html, setHtml] = useState2("");
801
+ useLayoutEffect(() => {
802
+ if (!highlighter) return;
803
+ const themedHtml = highlighter.codeToHtml(code, {
804
+ lang,
805
+ defaultColor: false,
806
+ themes: { light: "github-light", dark: "github-dark" }
807
+ });
808
+ setHtml(themedHtml);
809
+ }, [code, lang, highlighter]);
810
+ return /* @__PURE__ */ jsx5(Base.CodeBlock, { className: "my-0", children: /* @__PURE__ */ jsx5(Base.Pre, { ...props, dangerouslySetInnerHTML: { __html: html } }) });
811
+ }
812
+
813
+ // src/ui/playground.tsx
814
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
815
+ function APIPlayground({
816
+ route,
817
+ method = "GET",
818
+ authorization,
819
+ path = [],
820
+ header = [],
821
+ query = [],
822
+ body,
823
+ schemas
824
+ }) {
825
+ const { baseUrl } = useApiContext();
826
+ const dynamicRef = useRef(/* @__PURE__ */ new Map());
827
+ const [input, setInput] = useState3();
828
+ const form = useForm({
829
+ defaultValues: {
830
+ authorization: authorization?.defaultValue,
831
+ path: getDefaultValues(path, schemas),
832
+ query: getDefaultValues(query, schemas),
833
+ header: getDefaultValues(header, schemas),
834
+ body: body ? getDefaultValue(body, schemas) : void 0
835
+ }
836
+ });
837
+ const testQuery = useSWRImmutable(
838
+ input ? [baseUrl, route, method, input] : null,
839
+ async () => {
840
+ if (!input) return;
841
+ let pathname = route;
842
+ Object.keys(input.path ?? {}).forEach((key) => {
843
+ const paramValue = input.path?.[key];
844
+ if (paramValue) pathname = pathname.replace(`{${key}}`, paramValue);
845
+ });
846
+ const url = new URL(pathname, baseUrl ?? window.location.origin);
847
+ Object.keys(input.query ?? {}).forEach((key) => {
848
+ const paramValue = input.query?.[key];
849
+ if (paramValue) url.searchParams.append(key, paramValue);
850
+ });
851
+ const headers = new Headers({
852
+ "Content-Type": "application/json"
853
+ });
854
+ if (input.authorization) {
855
+ headers.append("Authorization", input.authorization);
856
+ }
857
+ Object.keys(input.header ?? {}).forEach((key) => {
858
+ const paramValue = input.header?.[key];
859
+ if (paramValue) headers.append(key, paramValue);
860
+ });
861
+ const bodyValue = body && input.body && Object.keys(input.body).length > 0 ? createBodyFromValue(input.body, body, schemas, dynamicRef.current) : void 0;
862
+ const response = await fetch(url, {
863
+ method,
864
+ headers,
865
+ body: bodyValue ? JSON.stringify(bodyValue) : void 0
866
+ });
867
+ const data = await response.json().catch(() => void 0);
868
+ return { status: response.status, data };
869
+ },
870
+ {
871
+ shouldRetryOnError: false
872
+ }
873
+ );
874
+ const statusInfo = testQuery.data ? getStatusInfo(testQuery.data.status) : void 0;
875
+ const onSubmit = form.handleSubmit((value) => {
876
+ setInput(value);
877
+ });
878
+ return /* @__PURE__ */ jsx6(Form, { ...form, children: /* @__PURE__ */ jsx6(
879
+ SchemaContext.Provider,
880
+ {
881
+ value: useMemo(
882
+ () => ({ references: schemas, dynamic: dynamicRef }),
883
+ [schemas]
884
+ ),
885
+ children: /* @__PURE__ */ jsxs3(
886
+ "form",
887
+ {
888
+ className: "not-prose flex flex-col gap-4 rounded-lg border bg-card p-4",
889
+ onSubmit,
890
+ children: [
891
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-row gap-2", children: [
892
+ /* @__PURE__ */ jsx6("code", { className: "flex-1 overflow-auto rounded-lg border bg-secondary px-3 py-1.5 text-sm", children: route }),
893
+ /* @__PURE__ */ jsx6(
894
+ "button",
895
+ {
896
+ type: "submit",
897
+ className: cn5(buttonVariants2({ color: "secondary" })),
898
+ disabled: testQuery.isLoading,
899
+ children: "Send"
900
+ }
901
+ )
902
+ ] }),
903
+ /* @__PURE__ */ jsxs3(Accordions, { type: "multiple", className: "-m-4 mt-2 border-0 text-sm", children: [
904
+ authorization ? /* @__PURE__ */ jsx6(Accordion, { title: "Authorization", children: /* @__PURE__ */ jsx6(
905
+ InputField,
906
+ {
907
+ name: "Authorization",
908
+ fieldName: "authorization",
909
+ field: authorization
910
+ }
911
+ ) }) : null,
912
+ path.length > 0 ? /* @__PURE__ */ jsx6(Accordion, { title: "Path", children: path.map((field) => /* @__PURE__ */ jsx6(
913
+ InputField,
914
+ {
915
+ field,
916
+ name: field.name,
917
+ fieldName: `path.${field.name}`
918
+ },
919
+ field.name
920
+ )) }) : null,
921
+ query.length > 0 ? /* @__PURE__ */ jsx6(Accordion, { title: "Query", children: /* @__PURE__ */ jsx6("div", { className: "flex flex-col gap-2", children: query.map((field) => /* @__PURE__ */ jsx6(
922
+ InputField,
923
+ {
924
+ field,
925
+ name: field.name,
926
+ fieldName: `query.${field.name}`
927
+ },
928
+ field.name
929
+ )) }) }) : null,
930
+ header.length > 0 ? /* @__PURE__ */ jsx6(Accordion, { title: "Headers", children: header.map((field) => /* @__PURE__ */ jsx6(
931
+ InputField,
932
+ {
933
+ field,
934
+ name: field.name,
935
+ fieldName: `header.${field.name}`
936
+ },
937
+ field.name
938
+ )) }) : null,
939
+ body ? /* @__PURE__ */ jsx6(Accordion, { title: "Body", children: body.type === "object" ? /* @__PURE__ */ jsx6(ObjectInput, { field: body, fieldName: "body" }) : /* @__PURE__ */ jsx6(InputField, { name: "Body", field: body, fieldName: "body" }) }) : null
940
+ ] }),
941
+ testQuery.data && statusInfo ? /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-3 rounded-lg border bg-card p-4", children: [
942
+ /* @__PURE__ */ jsxs3("div", { className: "inline-flex items-center gap-1.5 text-sm font-medium text-foreground", children: [
943
+ /* @__PURE__ */ jsx6(statusInfo.icon, { className: cn5("size-4", statusInfo.color) }),
944
+ statusInfo.description
945
+ ] }),
946
+ /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: testQuery.data.status }),
947
+ testQuery.data.data ? /* @__PURE__ */ jsx6(
948
+ CodeBlock2,
949
+ {
950
+ code: JSON.stringify(testQuery.data.data, null, 2),
951
+ className: "max-h-[288px]"
952
+ }
953
+ ) : null
954
+ ] }) : null
955
+ ]
956
+ }
957
+ )
958
+ }
959
+ ) });
960
+ }
961
+ export {
962
+ APIPlayground
963
+ };