fumadocs-openapi 6.0.1 → 6.0.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.
Files changed (155) hide show
  1. package/dist/build-routes.d.ts +15 -0
  2. package/dist/build-routes.d.ts.map +1 -0
  3. package/dist/build-routes.js +38 -0
  4. package/dist/generate-file.d.ts +38 -0
  5. package/dist/generate-file.d.ts.map +1 -0
  6. package/dist/generate-file.js +118 -0
  7. package/dist/generate.d.ts +48 -0
  8. package/dist/generate.d.ts.map +1 -0
  9. package/dist/generate.js +129 -0
  10. package/dist/index.d.ts +4 -274
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +3 -389
  13. package/dist/render/codeblock.d.ts +9 -0
  14. package/dist/render/codeblock.d.ts.map +1 -0
  15. package/dist/render/codeblock.js +13 -0
  16. package/dist/render/heading.d.ts +4 -0
  17. package/dist/render/heading.d.ts.map +1 -0
  18. package/dist/render/heading.js +6 -0
  19. package/dist/render/markdown.d.ts +5 -0
  20. package/dist/render/markdown.d.ts.map +1 -0
  21. package/dist/render/markdown.js +25 -0
  22. package/dist/render/operation.d.ts +17 -0
  23. package/dist/render/operation.d.ts.map +1 -0
  24. package/dist/render/operation.js +230 -0
  25. package/dist/render/playground.d.ts +66 -0
  26. package/dist/render/playground.d.ts.map +1 -0
  27. package/dist/render/playground.js +181 -0
  28. package/dist/render/renderer.d.ts +76 -0
  29. package/dist/render/renderer.d.ts.map +1 -0
  30. package/dist/render/renderer.js +23 -0
  31. package/dist/render/schema.d.ts +29 -0
  32. package/dist/render/schema.d.ts.map +1 -0
  33. package/dist/render/schema.js +151 -0
  34. package/dist/requests/curl.d.ts +3 -0
  35. package/dist/requests/curl.d.ts.map +1 -0
  36. package/dist/requests/curl.js +33 -0
  37. package/dist/requests/go.d.ts +3 -0
  38. package/dist/requests/go.d.ts.map +1 -0
  39. package/dist/requests/go.js +54 -0
  40. package/dist/requests/javascript.d.ts +3 -0
  41. package/dist/requests/javascript.d.ts.map +1 -0
  42. package/dist/requests/javascript.js +49 -0
  43. package/dist/requests/python.d.ts +3 -0
  44. package/dist/requests/python.d.ts.map +1 -0
  45. package/dist/requests/python.js +45 -0
  46. package/dist/scalar/client.d.ts +6 -0
  47. package/dist/scalar/client.d.ts.map +1 -0
  48. package/dist/scalar/client.js +25 -0
  49. package/dist/scalar/index.d.ts +4 -173
  50. package/dist/scalar/index.d.ts.map +1 -0
  51. package/dist/scalar/index.js +4 -11
  52. package/dist/server/api-page.d.ts +33 -0
  53. package/dist/server/api-page.d.ts.map +1 -0
  54. package/dist/server/api-page.js +59 -0
  55. package/dist/server/create-method.d.ts +7 -0
  56. package/dist/server/create-method.d.ts.map +1 -0
  57. package/dist/server/create-method.js +12 -0
  58. package/dist/server/create.d.ts +16 -0
  59. package/dist/server/create.d.ts.map +1 -0
  60. package/dist/server/create.js +11 -0
  61. package/dist/server/index.d.ts +3 -221
  62. package/dist/server/index.d.ts.map +1 -0
  63. package/dist/server/index.js +2 -1493
  64. package/dist/server/proxy.d.ts +8 -0
  65. package/dist/server/proxy.d.ts.map +1 -0
  66. package/dist/server/proxy.js +53 -0
  67. package/dist/server/source-api.d.ts +8 -0
  68. package/dist/server/source-api.d.ts.map +1 -0
  69. package/dist/server/source-api.js +34 -0
  70. package/dist/types.d.ts +60 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +1 -0
  73. package/dist/ui/client.d.ts +22 -0
  74. package/dist/ui/client.d.ts.map +1 -0
  75. package/dist/ui/client.js +10 -0
  76. package/dist/ui/components/input.d.ts +6 -0
  77. package/dist/ui/components/input.d.ts.map +1 -0
  78. package/dist/ui/components/input.js +10 -0
  79. package/dist/ui/components/method-label.d.ts +5 -0
  80. package/dist/ui/components/method-label.d.ts.map +1 -0
  81. package/dist/ui/components/method-label.js +33 -0
  82. package/dist/ui/components/select.d.ts +14 -0
  83. package/dist/ui/components/select.d.ts.map +1 -0
  84. package/dist/ui/components/select.js +24 -0
  85. package/dist/ui/components/variants.d.ts +6 -0
  86. package/dist/ui/components/variants.d.ts.map +1 -0
  87. package/dist/ui/components/variants.js +26 -0
  88. package/dist/ui/contexts/api.d.ts +28 -0
  89. package/dist/ui/contexts/api.d.ts.map +1 -0
  90. package/dist/ui/contexts/api.js +74 -0
  91. package/dist/ui/contexts/schema.d.ts +17 -0
  92. package/dist/ui/contexts/schema.d.ts.map +1 -0
  93. package/dist/ui/contexts/schema.js +8 -0
  94. package/dist/ui/index.d.ts +12 -302
  95. package/dist/ui/index.d.ts.map +1 -0
  96. package/dist/ui/index.js +18 -98
  97. package/dist/ui/playground/fetcher.d.ts +34 -0
  98. package/dist/ui/playground/fetcher.d.ts.map +1 -0
  99. package/dist/ui/{fetcher-CsHhplmD.js → playground/fetcher.js} +45 -44
  100. package/dist/ui/playground/get-default-values.d.ts +4 -0
  101. package/dist/ui/playground/get-default-values.d.ts.map +1 -0
  102. package/dist/ui/playground/get-default-values.js +24 -0
  103. package/dist/ui/playground/index.d.ts +44 -0
  104. package/dist/ui/playground/index.d.ts.map +1 -0
  105. package/dist/ui/playground/index.js +187 -0
  106. package/dist/ui/playground/inputs.d.ts +23 -0
  107. package/dist/ui/playground/inputs.d.ts.map +1 -0
  108. package/dist/ui/playground/inputs.js +172 -0
  109. package/dist/ui/playground/resolve.d.ts +6 -0
  110. package/dist/ui/playground/resolve.d.ts.map +1 -0
  111. package/dist/ui/playground/resolve.js +14 -0
  112. package/dist/ui/playground/status-info.d.ts +8 -0
  113. package/dist/ui/playground/status-info.d.ts.map +1 -0
  114. package/dist/ui/playground/status-info.js +40 -0
  115. package/dist/ui/server-select.d.ts +3 -0
  116. package/dist/ui/server-select.d.ts.map +1 -0
  117. package/dist/ui/server-select.js +27 -0
  118. package/dist/utils/combine-schema.d.ts +6 -0
  119. package/dist/utils/combine-schema.d.ts.map +1 -0
  120. package/dist/utils/combine-schema.js +46 -0
  121. package/dist/utils/generate-document.d.ts +20 -0
  122. package/dist/utils/generate-document.d.ts.map +1 -0
  123. package/dist/utils/generate-document.js +82 -0
  124. package/dist/utils/generate-sample.d.ts +33 -0
  125. package/dist/utils/generate-sample.d.ts.map +1 -0
  126. package/dist/utils/generate-sample.js +97 -0
  127. package/dist/utils/get-security.d.ts +8 -0
  128. package/dist/utils/get-security.d.ts.map +1 -0
  129. package/dist/utils/get-security.js +23 -0
  130. package/dist/utils/get-typescript-schema.d.ts +4 -0
  131. package/dist/utils/get-typescript-schema.d.ts.map +1 -0
  132. package/dist/utils/get-typescript-schema.js +18 -0
  133. package/dist/utils/id-to-title.d.ts +2 -0
  134. package/dist/utils/id-to-title.d.ts.map +1 -0
  135. package/dist/utils/id-to-title.js +17 -0
  136. package/dist/utils/input-to-string.d.ts +5 -0
  137. package/dist/utils/input-to-string.d.ts.map +1 -0
  138. package/dist/utils/input-to-string.js +21 -0
  139. package/dist/utils/process-document.d.ts +14 -0
  140. package/dist/utils/process-document.d.ts.map +1 -0
  141. package/dist/utils/process-document.js +32 -0
  142. package/dist/utils/schema.d.ts +9 -0
  143. package/dist/utils/schema.d.ts.map +1 -0
  144. package/dist/utils/schema.js +16 -0
  145. package/dist/utils/server-url.d.ts +2 -0
  146. package/dist/utils/server-url.d.ts.map +1 -0
  147. package/dist/utils/server-url.js +7 -0
  148. package/dist/utils/use-query.d.ts +6 -0
  149. package/dist/utils/use-query.d.ts.map +1 -0
  150. package/dist/utils/use-query.js +22 -0
  151. package/package.json +8 -8
  152. package/dist/scalar/client-client-BXAjVueF.js +0 -93
  153. package/dist/ui/client-client-CYO00OiB.js +0 -107
  154. package/dist/ui/index-client-BUeWwFWK.js +0 -1116
  155. package/dist/ui/server-select-client-Ct_HJ46K.js +0 -86
@@ -0,0 +1,24 @@
1
+ import { resolve } from '../../ui/playground/resolve.js';
2
+ export function getDefaultValue(item, references) {
3
+ if (item.type === 'object')
4
+ return Object.fromEntries(Object.entries(item.properties).map(([key, prop]) => [
5
+ key,
6
+ getDefaultValue(prop.type === 'ref' ? references[prop.schema] : prop, references),
7
+ ]));
8
+ if (item.type === 'array')
9
+ return [];
10
+ if (item.type === 'null')
11
+ return null;
12
+ if (item.type === 'switcher') {
13
+ const first = Object.values(item.items).at(0);
14
+ if (!first)
15
+ return '';
16
+ return getDefaultValue(resolve(first, references), references);
17
+ }
18
+ if (item.type === 'file')
19
+ return undefined;
20
+ return String(item.defaultValue);
21
+ }
22
+ export function getDefaultValues(field, context) {
23
+ return Object.fromEntries(field.map((p) => [p.name, getDefaultValue(p, context)]));
24
+ }
@@ -0,0 +1,44 @@
1
+ import { type ReactElement, type HTMLAttributes, type FC, type ReactNode } from 'react';
2
+ import type { FieldPath, UseFormStateReturn, ControllerFieldState, ControllerRenderProps } from 'react-hook-form';
3
+ import type { FetchResult } from '../../ui/playground/fetcher.js';
4
+ import type { APIPlaygroundProps, PrimitiveRequestField, RequestSchema } from '../../render/playground.js';
5
+ interface FormValues {
6
+ authorization: string | {
7
+ username: string;
8
+ password: string;
9
+ };
10
+ path: Record<string, unknown>;
11
+ query: Record<string, unknown>;
12
+ header: Record<string, unknown>;
13
+ body: unknown;
14
+ }
15
+ export interface CustomField<TName extends FieldPath<FormValues>, Info> {
16
+ render: (props: {
17
+ /**
18
+ * Field Info
19
+ */
20
+ info: Info;
21
+ field: ControllerRenderProps<FormValues, TName>;
22
+ fieldState: ControllerFieldState;
23
+ formState: UseFormStateReturn<FormValues>;
24
+ }) => ReactElement;
25
+ }
26
+ export declare function APIPlayground({ route, method, authorization, path, header, query, body, fields, schemas, proxyUrl, components: { ResultDisplay }, ...props }: APIPlaygroundProps & {
27
+ fields?: {
28
+ auth?: CustomField<'authorization', RequestSchema>;
29
+ path?: CustomField<`path.${string}`, PrimitiveRequestField>;
30
+ query?: CustomField<`query.${string}`, PrimitiveRequestField>;
31
+ header?: CustomField<`header.${string}`, PrimitiveRequestField>;
32
+ body?: CustomField<'body', RequestSchema>;
33
+ };
34
+ components?: Partial<{
35
+ ResultDisplay: FC<{
36
+ data: FetchResult;
37
+ }>;
38
+ }>;
39
+ } & HTMLAttributes<HTMLFormElement>): import("react/jsx-runtime").JSX.Element;
40
+ export declare function CollapsiblePanel({ title, children, ...props }: HTMLAttributes<HTMLDivElement> & {
41
+ title: ReactNode;
42
+ }): import("react/jsx-runtime").JSX.Element;
43
+ export {};
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/playground/index.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EAInB,KAAK,EAAE,EAEP,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAM3D,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,EACd,MAAM,qBAAqB,CAAC;AAe7B,UAAU,UAAU;IAClB,aAAa,EACT,MAAM,GACN;QACE,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACN,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,WAAW,CAAC,KAAK,SAAS,SAAS,CAAC,UAAU,CAAC,EAAE,IAAI;IACpE,MAAM,EAAE,CAAC,KAAK,EAAE;QACd;;WAEG;QACH,IAAI,EAAE,IAAI,CAAC;QACX,KAAK,EAAE,qBAAqB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAChD,UAAU,EAAE,oBAAoB,CAAC;QACjC,SAAS,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC;KAC3C,KAAK,YAAY,CAAC;CACpB;AAcD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,MAAc,EACd,aAAa,EACb,IAAS,EACT,MAAW,EACX,KAAU,EACV,IAAI,EACJ,MAAW,EACX,OAAO,EACP,QAAQ,EACR,UAAU,EAAE,EAAE,aAAoC,EAAO,EACzD,GAAG,KAAK,EACT,EAAE,kBAAkB,GAAG;IACtB,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,WAAW,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;QACnD,IAAI,CAAC,EAAE,WAAW,CAAC,QAAQ,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAC5D,KAAK,CAAC,EAAE,WAAW,CAAC,SAAS,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAC9D,MAAM,CAAC,EAAE,WAAW,CAAC,UAAU,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAChE,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;KAC3C,CAAC;IAEF,UAAU,CAAC,EAAE,OAAO,CAAC;QACnB,aAAa,EAAE,EAAE,CAAC;YAAE,IAAI,EAAE,WAAW,CAAA;SAAE,CAAC,CAAC;KAC1C,CAAC,CAAC;CACJ,GAAG,cAAc,CAAC,eAAe,CAAC,2CAyOlC;AAiHD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG;IAClC,KAAK,EAAE,SAAS,CAAC;CAClB,2CAeA"}
@@ -0,0 +1,187 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useRef, useEffect, Fragment, } from 'react';
4
+ import { Controller, FormProvider, useForm } from 'react-hook-form';
5
+ import { cn, buttonVariants } from 'fumadocs-ui/components/api';
6
+ import { useApiContext } from '../../ui/contexts/api.js';
7
+ import { getDefaultValue, getDefaultValues, } from '../../ui/playground/get-default-values.js';
8
+ import { FieldSet, ObjectInput } from '../../ui/playground/inputs.js';
9
+ import { SchemaContext } from '../contexts/schema.js';
10
+ import { getStatusInfo } from '../../ui/playground/status-info.js';
11
+ import { getUrl } from '../../utils/server-url.js';
12
+ import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
13
+ import { MethodLabel } from '../../ui/components/method-label.js';
14
+ import { useQuery } from '../../utils/use-query.js';
15
+ import ServerSelect from '../../ui/server-select.js';
16
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from 'fumadocs-ui/components/ui/collapsible';
17
+ import { ChevronDown } from 'lucide-react';
18
+ function defaultAuthValue(auth) {
19
+ if (!auth || auth.type === 'apiKey')
20
+ return '';
21
+ if (auth.type === 'http' && auth.scheme === 'basic') {
22
+ return {
23
+ username: '',
24
+ password: '',
25
+ };
26
+ }
27
+ return 'Bearer';
28
+ }
29
+ export function APIPlayground({ route, method = 'GET', authorization, path = [], header = [], query = [], body, fields = {}, schemas, proxyUrl, components: { ResultDisplay = DefaultResultDisplay } = {}, ...props }) {
30
+ const { serverRef } = useApiContext();
31
+ const dynamicRef = useRef(new Map());
32
+ const form = useForm({
33
+ defaultValues: {
34
+ authorization: defaultAuthValue(authorization),
35
+ path: getDefaultValues(path, schemas),
36
+ query: getDefaultValues(query, schemas),
37
+ header: getDefaultValues(header, schemas),
38
+ body: body ? getDefaultValue(body, schemas) : undefined,
39
+ },
40
+ });
41
+ const testQuery = useQuery(async (input) => {
42
+ const fetcher = await import('./fetcher.js').then((mod) => mod.createBrowserFetcher(body, schemas));
43
+ const query = { ...input.query };
44
+ const header = { ...input.header };
45
+ if (input.authorization && authorization) {
46
+ if (authorization.type === 'apiKey') {
47
+ if (authorization.in === 'header') {
48
+ header[authorization.name] = input.authorization;
49
+ }
50
+ else if (authorization.in === 'query') {
51
+ query[authorization.name] = input.authorization;
52
+ }
53
+ else {
54
+ if ('cookie' in header) {
55
+ header.Cookie = header.cookie;
56
+ delete header.cookie;
57
+ }
58
+ header.Cookie = [
59
+ header.Cookie,
60
+ `${authorization.name}=${input.authorization}`,
61
+ ]
62
+ .filter((s) => s.length > 0)
63
+ .join('; ');
64
+ }
65
+ }
66
+ else if (authorization.type === 'http' &&
67
+ authorization.scheme === 'basic') {
68
+ if (typeof input.authorization === 'object')
69
+ header.Authorization = `Basic ${btoa(`${input.authorization.username}:${input.authorization.password}`)}`;
70
+ }
71
+ else {
72
+ header.Authorization = input.authorization;
73
+ }
74
+ }
75
+ const serverUrl = serverRef.current
76
+ ? getUrl(serverRef.current.url, serverRef.current.variables)
77
+ : window.location.origin;
78
+ let url = `${serverUrl}${createPathnameFromInput(route, input.path, query)}`;
79
+ if (proxyUrl) {
80
+ const updated = new URL(proxyUrl, window.location.origin);
81
+ updated.searchParams.append('url', url);
82
+ url = updated.toString();
83
+ }
84
+ return fetcher.fetch({
85
+ url: url.toString(),
86
+ header,
87
+ body: body
88
+ ? {
89
+ mediaType: body.mediaType,
90
+ value: input.body,
91
+ }
92
+ : undefined,
93
+ dynamicFields: dynamicRef.current,
94
+ method,
95
+ });
96
+ });
97
+ useEffect(() => {
98
+ if (!authorization)
99
+ return;
100
+ const key = `__fumadocs_auth_${JSON.stringify(authorization)}`;
101
+ const cached = localStorage.getItem(key);
102
+ if (cached)
103
+ form.setValue('authorization', JSON.parse(cached));
104
+ const subscription = form.watch((value, { name }) => {
105
+ if (!name || !name.startsWith('authorization') || !value.authorization)
106
+ return;
107
+ localStorage.setItem(key, JSON.stringify(value.authorization));
108
+ });
109
+ return () => {
110
+ subscription.unsubscribe();
111
+ };
112
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- mounted only once
113
+ }, []);
114
+ const onSubmit = form.handleSubmit((value) => {
115
+ testQuery.start(value);
116
+ });
117
+ function renderCustomField(fieldName, info, field, key) {
118
+ if (field) {
119
+ return (_jsx(Controller, { control: form.control, render: (props) => field.render({ ...props, info }), name: fieldName }, key));
120
+ }
121
+ return (_jsx(FieldSet, { name: info.name, fieldName: fieldName, field: info }, key));
122
+ }
123
+ return (_jsx(FormProvider, { ...form, children: _jsx(SchemaContext.Provider, { value: useMemo(() => ({ references: schemas, dynamic: dynamicRef }), [schemas]), children: _jsxs("form", { ...props, className: cn('not-prose flex flex-col gap-2 rounded-xl border p-3 shadow-md', props.className), onSubmit: onSubmit, children: [_jsx(FormHeader, { method: method, route: route, isLoading: testQuery.isLoading }), _jsx(CollapsiblePanel, { title: "Server URL", children: _jsx(ServerSelect, {}) }), header.length > 0 || authorization ? (_jsxs(CollapsiblePanel, { title: "Headers", children: [authorization?.type === 'http' &&
124
+ authorization.scheme === 'basic'
125
+ ? renderCustomField('authorization', {
126
+ name: 'Authorization',
127
+ type: 'object',
128
+ isRequired: true,
129
+ properties: {
130
+ username: {
131
+ type: 'string',
132
+ isRequired: true,
133
+ defaultValue: '',
134
+ },
135
+ password: {
136
+ type: 'string',
137
+ isRequired: true,
138
+ defaultValue: '',
139
+ },
140
+ },
141
+ }, fields.auth)
142
+ : authorization
143
+ ? renderCustomField('authorization', {
144
+ name: 'Authorization',
145
+ type: 'string',
146
+ isRequired: true,
147
+ description: 'The Authorization access token',
148
+ defaultValue: '',
149
+ }, fields.auth)
150
+ : null, header.map((field) => renderCustomField(`header.${field.name}`, field, fields.header, field.name))] })) : null, path.length > 0 ? (_jsx(CollapsiblePanel, { title: "Path", children: path.map((field) => renderCustomField(`path.${field.name}`, field, fields.path, field.name)) })) : null, query.length > 0 ? (_jsx(CollapsiblePanel, { title: "Query", children: query.map((field) => renderCustomField(`query.${field.name}`, field, fields.query, field.name)) })) : null, body ? (_jsx(CollapsiblePanel, { title: "Body", children: body.type === 'object' && !fields.body ? (_jsx(ObjectInput, { field: body, fieldName: "body" })) : (renderCustomField('body', body, fields.body)) })) : null, testQuery.data ? _jsx(ResultDisplay, { data: testQuery.data }) : null] }) }) }));
151
+ }
152
+ function createPathnameFromInput(route, path, query) {
153
+ let pathname = route;
154
+ for (const key of Object.keys(path)) {
155
+ const paramValue = path[key];
156
+ if (typeof paramValue === 'string' && paramValue.length > 0)
157
+ pathname = pathname.replace(`{${key}}`, paramValue);
158
+ }
159
+ const searchParams = new URLSearchParams();
160
+ for (const key of Object.keys(query)) {
161
+ const paramValue = query[key];
162
+ if (typeof paramValue === 'string' && paramValue.length > 0)
163
+ searchParams.append(key, paramValue);
164
+ }
165
+ return searchParams.size > 0
166
+ ? `${pathname}?${searchParams.toString()}`
167
+ : pathname;
168
+ }
169
+ function Route({ route, ...props }) {
170
+ const segments = route.split('/').filter((part) => part.length > 0);
171
+ return (_jsx("div", { ...props, className: cn('flex flex-row items-center gap-0.5 overflow-auto text-nowrap', props.className), children: segments.map((part, index) => (_jsxs(Fragment, { children: [_jsx("span", { className: "text-fd-muted-foreground", children: "/" }), part.startsWith('{') && part.endsWith('}') ? (_jsx("code", { className: "bg-fd-primary/10 text-fd-primary", children: part })) : (_jsx("code", { className: "text-fd-foreground", children: part }))] }, index))) }));
172
+ }
173
+ function FormHeader({ route, method, isLoading, }) {
174
+ return (_jsxs("div", { className: "flex flex-row items-center gap-2 text-sm", children: [_jsx(MethodLabel, { children: method }), _jsx(Route, { route: route, className: "flex-1" }), _jsx("button", { type: "submit", className: cn(buttonVariants({ color: 'primary', size: 'sm' }), 'px-3 py-1.5'), disabled: isLoading, children: "Send" })] }));
175
+ }
176
+ function DefaultResultDisplay({ data }) {
177
+ const statusInfo = useMemo(() => getStatusInfo(data.status), [data.status]);
178
+ const { shikiOptions } = useApiContext();
179
+ return (_jsxs("div", { className: "flex flex-col gap-3 rounded-lg border bg-fd-card p-4", children: [_jsxs("div", { className: "inline-flex items-center gap-1.5 text-sm font-medium text-fd-foreground", children: [_jsx(statusInfo.icon, { className: cn('size-4', statusInfo.color) }), statusInfo.description] }), _jsx("p", { className: "text-sm text-fd-muted-foreground", children: data.status }), data.data ? (_jsx(DynamicCodeBlock, { lang: typeof data.data === 'string' && data.data.length > 50000
180
+ ? 'text'
181
+ : data.type, code: typeof data.data === 'string'
182
+ ? data.data
183
+ : JSON.stringify(data.data, null, 2), ...shikiOptions })) : null] }));
184
+ }
185
+ export function CollapsiblePanel({ title, children, ...props }) {
186
+ return (_jsxs(Collapsible, { ...props, className: "border rounded-xl bg-fd-card text-fd-card-foreground overflow-hidden", children: [_jsxs(CollapsibleTrigger, { className: "group w-full inline-flex items-center gap-2 justify-between p-3 text-sm font-medium hover:bg-fd-accent", children: [title, _jsx(ChevronDown, { className: "size-4 group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { children: _jsx("div", { className: "flex flex-col gap-4 p-3", children: children }) })] }));
187
+ }
@@ -0,0 +1,23 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ import type { RequestSchema } from '../../render/playground.js';
3
+ type FieldOfType<Type> = Extract<RequestSchema, {
4
+ type: Type;
5
+ }>;
6
+ export declare function ObjectInput({ field, fieldName, ...props }: {
7
+ field: FieldOfType<'object'>;
8
+ fieldName: string;
9
+ } & HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
10
+ export declare function FieldInput({ field, fieldName, ...props }: HTMLAttributes<HTMLElement> & {
11
+ field: Exclude<RequestSchema, {
12
+ type: 'switcher';
13
+ }>;
14
+ fieldName: string;
15
+ }): import("react/jsx-runtime").JSX.Element | null;
16
+ export declare function FieldSet({ field, fieldName, toolbar, name, ...props }: HTMLAttributes<HTMLElement> & {
17
+ name?: string;
18
+ field: RequestSchema;
19
+ fieldName: string;
20
+ toolbar?: ReactNode;
21
+ }): import("react/jsx-runtime").JSX.Element | null;
22
+ export {};
23
+ //# sourceMappingURL=inputs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../../src/ui/playground/inputs.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAWf,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD,KAAK,WAAW,CAAC,IAAI,IAAI,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AA6BhE,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EACT,EAAE;IACD,KAAK,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,cAAc,CAAC,cAAc,CAAC,2CAqBjC;AAoJD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,WAAW,CAAC,GAAG;IAC/B,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IACpD,SAAS,EAAE,MAAM,CAAC;CACnB,kDAuFA;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,SAAS,EACT,OAAO,EACP,IAAI,EACJ,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,WAAW,CAAC,GAAG;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,kDA8EA"}
@@ -0,0 +1,172 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, } from 'react';
3
+ import { Plus, Trash2 } from 'lucide-react';
4
+ import { Controller, useFieldArray, useFormContext } from 'react-hook-form';
5
+ import { cn, buttonVariants } from 'fumadocs-ui/components/api';
6
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../ui/components/select.js';
7
+ import { resolve } from '../../ui/playground/resolve.js';
8
+ import { Input, labelVariants } from '../../ui/components/input.js';
9
+ import { getDefaultValue } from '../../ui/playground/get-default-values.js';
10
+ import { useSchemaContext } from '../../ui/contexts/schema.js';
11
+ function FieldHeader({ name, required = false, type, ...props }) {
12
+ return (_jsxs("label", { ...props, className: cn('w-full inline-flex items-center gap-1', props.className), children: [_jsx("span", { className: cn(labelVariants()), children: name }), required ? _jsx("span", { className: "text-red-500", children: "*" }) : null, _jsx("div", { className: "flex-1" }), type ? (_jsx("code", { className: "text-xs text-fd-muted-foreground", children: type })) : null, props.children] }));
13
+ }
14
+ export function ObjectInput({ field, fieldName, ...props }) {
15
+ const { references } = useSchemaContext();
16
+ return (_jsxs("div", { ...props, className: cn('flex flex-col gap-6', props.className), children: [Object.entries(field.properties).map(([key, child]) => (_jsx(FieldSet, { name: key, field: resolve(child, references), fieldName: `${fieldName}.${key}` }, key))), field.additionalProperties ? (_jsx(AdditionalProperties, { fieldName: fieldName, type: field.additionalProperties })) : null] }));
17
+ }
18
+ function AdditionalProperties({ fieldName, type, }) {
19
+ const { control, setValue } = useFormContext();
20
+ const { references, dynamic } = useSchemaContext();
21
+ const [nextName, setNextName] = useState('');
22
+ const [properties, setProperties] = useState(() => {
23
+ const d = dynamic.current.get(`additional_${fieldName}`);
24
+ if (d?.type === 'object')
25
+ return d.properties;
26
+ return [];
27
+ });
28
+ dynamic.current.set(`additional_${fieldName}`, {
29
+ type: 'object',
30
+ properties,
31
+ });
32
+ const onAppend = () => {
33
+ const name = nextName.trim();
34
+ if (name.length === 0)
35
+ return;
36
+ setProperties((p) => {
37
+ if (p.includes(name))
38
+ return p;
39
+ setValue(`${fieldName}.${name}`, '');
40
+ setNextName('');
41
+ return [...p, name];
42
+ });
43
+ };
44
+ const types = typeof type === 'string'
45
+ ? resolveDynamicTypes(references[type], references)
46
+ : undefined;
47
+ return (_jsxs(_Fragment, { children: [properties.map((item) => (_jsx(FieldSet, { name: item, field: {
48
+ type: 'switcher',
49
+ items: types ?? anyFields,
50
+ isRequired: false,
51
+ }, fieldName: `${fieldName}.${item}`, toolbar: _jsx("button", { type: "button", "aria-label": "Remove Item", className: cn(buttonVariants({
52
+ color: 'secondary',
53
+ size: 'sm',
54
+ })), onClick: () => {
55
+ setProperties((p) => p.filter((prop) => prop !== item));
56
+ control.unregister(`${fieldName}.${item}`);
57
+ }, children: _jsx(Trash2, { className: "size-4" }) }) }, item))), _jsxs("div", { className: "flex flex-row gap-1", children: [_jsx(Input, { value: nextName, placeholder: "Enter Property Name", onChange: (e) => {
58
+ setNextName(e.target.value);
59
+ }, onKeyDown: (e) => {
60
+ if (e.key === 'Enter') {
61
+ onAppend();
62
+ e.preventDefault();
63
+ }
64
+ } }), _jsx("button", { type: "button", className: cn(buttonVariants({ color: 'secondary' })), onClick: onAppend, children: "New" })] })] }));
65
+ }
66
+ function resolveDynamicTypes(schema, references) {
67
+ if (schema.type !== 'switcher')
68
+ return { [schema.type]: schema };
69
+ return Object.fromEntries(Object.entries(schema.items).map(([key, value]) => [
70
+ key,
71
+ resolve(value, references),
72
+ ]));
73
+ }
74
+ const anyFields = {
75
+ string: {
76
+ type: 'string',
77
+ isRequired: false,
78
+ defaultValue: '',
79
+ },
80
+ boolean: {
81
+ type: 'boolean',
82
+ isRequired: false,
83
+ defaultValue: '',
84
+ },
85
+ number: {
86
+ type: 'number',
87
+ isRequired: false,
88
+ defaultValue: '',
89
+ },
90
+ object: {
91
+ type: 'object',
92
+ properties: {},
93
+ additionalProperties: true,
94
+ isRequired: false,
95
+ },
96
+ };
97
+ anyFields.array = {
98
+ type: 'array',
99
+ isRequired: false,
100
+ items: {
101
+ type: 'switcher',
102
+ isRequired: false,
103
+ items: anyFields,
104
+ },
105
+ };
106
+ export function FieldInput({ field, fieldName, ...props }) {
107
+ const { control, register } = useFormContext();
108
+ if (field.type === 'null')
109
+ return null;
110
+ if (field.type === 'object') {
111
+ return (_jsx(ObjectInput, { field: field, fieldName: fieldName, ...props, className: cn('rounded-lg border border-fd-primary/20 bg-fd-card p-3 shadow-sm', props.className) }));
112
+ }
113
+ if (field.type === 'array') {
114
+ return (_jsx(ArrayInput, { fieldName: fieldName, field: field, ...props, className: cn('rounded-lg border border-fd-primary/20 bg-fd-background p-3 shadow-sm', props.className) }));
115
+ }
116
+ if (field.type === 'file' || field.type === 'boolean') {
117
+ return (_jsx(Controller, { control: control, name: fieldName, render: ({ field: { value, onChange, ...restField } }) => field.type === 'file' ? (_jsx("input", { id: fieldName, type: "file", multiple: false, onChange: (e) => {
118
+ if (!e.target.files)
119
+ return;
120
+ onChange(e.target.files.item(0));
121
+ }, ...props, ...restField })) : (_jsxs(Select, { value: value, onValueChange: onChange, disabled: restField.disabled, children: [_jsx(SelectTrigger, { id: fieldName, className: props.className, ...restField, children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "true", children: "True" }), _jsx(SelectItem, { value: "false", children: "False" }), field.isRequired ? null : (_jsx(SelectItem, { value: "null", children: "Null" }))] })] })) }));
122
+ }
123
+ return (_jsx(Input, { id: fieldName, placeholder: "Enter value", type: field.type === 'string' ? 'text' : 'number', ...register(fieldName), ...props }));
124
+ }
125
+ export function FieldSet({ field, fieldName, toolbar, name, ...props }) {
126
+ const { references, dynamic } = useSchemaContext();
127
+ const [value, setValue] = useState(() => {
128
+ if (field.type !== 'switcher')
129
+ return '';
130
+ const d = dynamic.current.get(fieldName);
131
+ const items = Object.keys(field.items);
132
+ if (d?.type === 'field') {
133
+ // schemas are passed from server components, they shouldn't be re-constructed
134
+ const cached = items.find((item) => d.schema === field.items[item]);
135
+ if (cached)
136
+ return cached;
137
+ }
138
+ return items[0];
139
+ });
140
+ if (field.type === 'null')
141
+ return null;
142
+ if (value && field.type === 'switcher') {
143
+ dynamic.current.set(fieldName, {
144
+ type: 'field',
145
+ schema: field.items[value],
146
+ });
147
+ }
148
+ if (field.type === 'switcher') {
149
+ const child = resolve(field.items[value], references);
150
+ return (_jsxs("fieldset", { ...props, className: cn('flex flex-col gap-1.5', props.className), children: [_jsxs(FieldHeader, { name: name, htmlFor: fieldName, required: field.isRequired, children: [_jsx("select", { value: value, onChange: (e) => setValue(e.target.value), className: "text-xs", children: Object.keys(field.items).map((item) => (_jsx("option", { value: item, children: item }, item))) }), toolbar] }), _jsx("p", { className: "text-xs text-fd-muted-foreground", children: field.description }), child.type === 'switcher' ? (_jsx(FieldSet, { field: child, fieldName: fieldName })) : (_jsx(FieldInput, { field: child, fieldName: fieldName }))] }));
151
+ }
152
+ return (_jsxs("fieldset", { ...props, className: cn('flex flex-col gap-1.5', props.className), children: [_jsx(FieldHeader, { htmlFor: fieldName, name: name, required: field.isRequired, type: field.type, children: toolbar }), _jsx(FieldInput, { field: field, fieldName: fieldName })] }));
153
+ }
154
+ function ArrayInput({ fieldName, field, ...props }) {
155
+ const { references } = useSchemaContext();
156
+ const items = resolve(field.items, references);
157
+ const { fields, append, remove } = useFieldArray({
158
+ name: fieldName,
159
+ });
160
+ return (_jsxs("div", { ...props, className: cn('flex flex-col gap-2', props.className), children: [fields.map((item, index) => (_jsx(FieldSet, { name: `${fieldName.split('.').at(-1)}[${index}]`, field: items, fieldName: `${fieldName}.${index}`, toolbar: _jsx("button", { type: "button", "aria-label": "Remove Item", className: cn(buttonVariants({
161
+ color: 'secondary',
162
+ size: 'sm',
163
+ })), onClick: () => {
164
+ remove(index);
165
+ }, children: _jsx(Trash2, { className: "size-4" }) }) }, item.id))), _jsxs("button", { type: "button", className: cn(buttonVariants({
166
+ color: 'outline',
167
+ className: 'gap-1.5 py-2',
168
+ size: 'sm',
169
+ })), onClick: () => {
170
+ append(getDefaultValue(items, references));
171
+ }, children: [_jsx(Plus, { className: "size-4" }), "New Item"] })] }));
172
+ }
@@ -0,0 +1,6 @@
1
+ import { type ReferenceSchema, type RequestSchema } from '../../render/playground.js';
2
+ /**
3
+ * Resolve reference
4
+ */
5
+ export declare function resolve(schema: RequestSchema | ReferenceSchema | string, references: Record<string, RequestSchema>): RequestSchema;
6
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/ui/playground/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE/E;;GAEG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,EAChD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GACxC,aAAa,CASf"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve reference
3
+ */
4
+ export function resolve(schema, references) {
5
+ if (typeof schema === 'string')
6
+ return references[schema];
7
+ if (schema.type !== 'ref')
8
+ return schema;
9
+ return {
10
+ ...references[schema.schema],
11
+ description: schema.description,
12
+ isRequired: schema.isRequired,
13
+ };
14
+ }
@@ -0,0 +1,8 @@
1
+ interface StatusInfo {
2
+ description: string;
3
+ color: string;
4
+ icon: React.ElementType;
5
+ }
6
+ export declare function getStatusInfo(status: number): StatusInfo;
7
+ export {};
8
+ //# sourceMappingURL=status-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-info.d.ts","sourceRoot":"","sources":["../../../src/ui/playground/status-info.tsx"],"names":[],"mappings":"AAEA,UAAU,UAAU;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC,WAAW,CAAC;CACzB;AAsBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAsBxD"}
@@ -0,0 +1,40 @@
1
+ import { CircleCheckIcon, CircleXIcon } from 'lucide-react';
2
+ const statusMap = {
3
+ 400: { description: 'Bad Request', color: 'text-red-500', icon: CircleXIcon },
4
+ 401: {
5
+ description: 'Unauthorized',
6
+ color: 'text-red-500',
7
+ icon: CircleXIcon,
8
+ },
9
+ 403: { description: 'Forbidden', color: 'text-red-500', icon: CircleXIcon },
10
+ 404: {
11
+ description: 'Not Found',
12
+ color: 'text-fd-muted-foreground',
13
+ icon: CircleXIcon,
14
+ },
15
+ 500: {
16
+ description: 'Internal Server Error',
17
+ color: 'text-red-500',
18
+ icon: CircleXIcon,
19
+ },
20
+ };
21
+ export function getStatusInfo(status) {
22
+ if (status in statusMap) {
23
+ return statusMap[status];
24
+ }
25
+ if (status >= 200 && status < 300) {
26
+ return {
27
+ description: 'Successful',
28
+ color: 'text-green-500',
29
+ icon: CircleCheckIcon,
30
+ };
31
+ }
32
+ if (status >= 400) {
33
+ return { description: 'Error', color: 'text-red-500', icon: CircleXIcon };
34
+ }
35
+ return {
36
+ description: 'No Description',
37
+ color: 'text-fd-muted-foreground',
38
+ icon: CircleXIcon,
39
+ };
40
+ }
@@ -0,0 +1,3 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ export default function ServerSelect(props: HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element | null;
3
+ //# sourceMappingURL=server-select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-select.d.ts","sourceRoot":"","sources":["../../src/ui/server-select.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAG5C,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,cAAc,CAAC,kDA6EzE"}
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useApiContext, useServerSelectContext } from '../ui/contexts/api.js';
4
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../ui/components/select.js';
5
+ import { Input, labelVariants } from '../ui/components/input.js';
6
+ import { cn } from 'fumadocs-ui/components/api';
7
+ export default function ServerSelect(props) {
8
+ const { servers } = useApiContext();
9
+ const { server, setServer, setServerVariables } = useServerSelectContext();
10
+ if (servers.length <= 1)
11
+ return null;
12
+ const schema = server
13
+ ? servers.find((item) => item.url === server.url)
14
+ : undefined;
15
+ return (_jsxs("div", { ...props, className: cn('flex flex-col gap-4', props.className), children: [_jsxs(Select, { value: server?.url, onValueChange: setServer, children: [_jsx(SelectTrigger, { className: "h-auto break-all", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: servers.map((item) => (_jsxs(SelectItem, { value: item.url, children: [item.url, _jsx("p", { className: "text-start text-fd-muted-foreground", children: item.description })] }, item.url))) })] }), Object.entries(schema?.variables ?? {}).map(([key, variable]) => {
16
+ if (!server)
17
+ return;
18
+ const id = `fd_server_select_${key}`;
19
+ return (_jsxs("fieldset", { className: "flex flex-col gap-1", children: [_jsx("label", { className: cn(labelVariants()), htmlFor: id, children: key }), _jsx("p", { className: "text-xs text-fd-muted-foreground empty:hidden", children: variable.description }), variable.enum ? (_jsxs(Select, { value: server.variables[key], onValueChange: (v) => setServerVariables({
20
+ ...server?.variables,
21
+ [key]: v,
22
+ }), children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: variable.enum.map((value) => (_jsx(SelectItem, { value: value, children: value }, value))) })] })) : (_jsx(Input, { id: id, value: server.variables[key], onChange: (e) => setServerVariables({
23
+ ...server?.variables,
24
+ [key]: e.target.value,
25
+ }) }))] }, key));
26
+ })] }));
27
+ }
@@ -0,0 +1,6 @@
1
+ import { type ParsedSchema } from '../utils/schema.js';
2
+ /**
3
+ * Combine multiple object schemas into one
4
+ */
5
+ export declare function combineSchema(schema: ParsedSchema[]): ParsedSchema;
6
+ //# sourceMappingURL=combine-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combine-schema.d.ts","sourceRoot":"","sources":["../../src/utils/combine-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAoDlE"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Combine multiple object schemas into one
3
+ */
4
+ export function combineSchema(schema) {
5
+ const result = {
6
+ type: undefined,
7
+ };
8
+ function add(s) {
9
+ if (s.type) {
10
+ result.type ?? (result.type = []);
11
+ if (!Array.isArray(result.type)) {
12
+ result.type = [result.type];
13
+ }
14
+ for (const v of Array.isArray(s.type) ? s.type : [s.type]) {
15
+ if (!result.type.includes(v)) {
16
+ result.type.push(v);
17
+ }
18
+ }
19
+ }
20
+ if (s.properties) {
21
+ result.properties ?? (result.properties = {});
22
+ Object.assign(result.properties, s.properties);
23
+ }
24
+ if (s.additionalProperties === true) {
25
+ result.additionalProperties = true;
26
+ }
27
+ else if (s.additionalProperties &&
28
+ typeof result.additionalProperties !== 'boolean') {
29
+ result.additionalProperties ?? (result.additionalProperties = {});
30
+ Object.assign(result.additionalProperties, s.additionalProperties);
31
+ }
32
+ if (s.required) {
33
+ result.required ?? (result.required = []);
34
+ result.required.push(...s.required);
35
+ }
36
+ if (s.enum && s.enum.length > 0) {
37
+ result.enum ?? (result.enum = []);
38
+ result.enum.push(...s.enum);
39
+ }
40
+ if (s.allOf) {
41
+ s.allOf.forEach(add);
42
+ }
43
+ }
44
+ schema.forEach(add);
45
+ return result;
46
+ }