fumadocs-openapi 4.4.1 → 5.0.0

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