fumadocs-openapi 9.0.4 → 9.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/playground/auth/oauth-dialog.d.ts +4 -2
- package/dist/playground/auth/oauth-dialog.d.ts.map +1 -1
- package/dist/playground/auth/oauth-dialog.js +113 -26
- package/dist/playground/client.d.ts.map +1 -1
- package/dist/playground/client.js +101 -63
- package/dist/playground/index.js +26 -15
- package/dist/playground/inputs.d.ts +8 -2
- package/dist/playground/inputs.d.ts.map +1 -1
- package/dist/playground/inputs.js +32 -31
- package/dist/render/heading.d.ts +1 -1
- package/dist/render/heading.d.ts.map +1 -1
- package/dist/render/heading.js +3 -3
- package/dist/render/markdown.d.ts +2 -2
- package/dist/render/markdown.d.ts.map +1 -1
- package/dist/render/markdown.js +4 -2
- package/dist/render/operation/index.d.ts.map +1 -1
- package/dist/render/operation/index.js +68 -58
- package/dist/render/renderer.d.ts.map +1 -1
- package/dist/render/renderer.js +3 -3
- package/dist/render/schema.d.ts.map +1 -1
- package/dist/render/schema.js +7 -8
- package/dist/ui/components/accordion.d.ts +8 -0
- package/dist/ui/components/accordion.d.ts.map +1 -0
- package/dist/ui/components/accordion.js +20 -0
- package/dist/ui/components/input.js +1 -1
- package/dist/ui/components/select.js +1 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +4 -2
- package/dist/ui/select-tabs.d.ts +13 -0
- package/dist/ui/select-tabs.d.ts.map +1 -0
- package/dist/ui/select-tabs.js +20 -0
- package/dist/ui/server-select.d.ts.map +1 -1
- package/dist/ui/server-select.js +22 -15
- package/dist/utils/schema-to-string.d.ts.map +1 -1
- package/dist/utils/schema-to-string.js +10 -1
- package/dist/utils/schema.d.ts +0 -1
- package/dist/utils/schema.d.ts.map +1 -1
- package/dist/utils/schema.js +0 -10
- package/package.json +10 -9
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { OpenAPIV3_1 } from 'openapi-types';
|
|
2
2
|
export interface AuthDialogProps {
|
|
3
|
-
flow: keyof OpenAPIV3_1.OAuth2SecurityScheme['flows'];
|
|
4
3
|
scheme: OpenAPIV3_1.OAuth2SecurityScheme;
|
|
4
|
+
scopes: string[];
|
|
5
|
+
open: boolean;
|
|
6
|
+
setOpen: (v: boolean) => void;
|
|
5
7
|
setToken: (token: string) => void;
|
|
6
8
|
children: React.ReactNode;
|
|
7
9
|
}
|
|
8
|
-
export declare function OauthDialog({
|
|
10
|
+
export declare function OauthDialog({ scheme, scopes, setToken, children, open, setOpen, }: AuthDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
9
11
|
export declare const OauthDialogTrigger: import("react").ForwardRefExoticComponent<import("@radix-ui/react-dialog").DialogTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
10
12
|
//# sourceMappingURL=oauth-dialog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-dialog.d.ts","sourceRoot":"","sources":["../../../src/playground/auth/oauth-dialog.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"oauth-dialog.d.ts","sourceRoot":"","sources":["../../../src/playground/auth/oauth-dialog.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAiBjD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAuCD,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,OAAO,GACR,EAAE,eAAe,2CAwSjB;AAED,eAAO,MAAM,kBAAkB,mJAAgB,CAAC"}
|
|
@@ -6,15 +6,38 @@ import { useQuery } from '../../utils/use-query.js';
|
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
7
|
import { cn } from 'fumadocs-ui/utils/cn';
|
|
8
8
|
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../ui/components/select.js';
|
|
10
|
+
const FlowTypes = {
|
|
11
|
+
password: {
|
|
12
|
+
name: 'Resource Owner Password Flow',
|
|
13
|
+
description: 'Authenticate using username and password.',
|
|
14
|
+
},
|
|
15
|
+
clientCredentials: {
|
|
16
|
+
name: 'Client Credentials',
|
|
17
|
+
description: 'Intended for the server-to-server authentication.',
|
|
18
|
+
},
|
|
19
|
+
authorizationCode: {
|
|
20
|
+
name: 'Authorization code',
|
|
21
|
+
description: 'Authenticate with 3rd party services',
|
|
22
|
+
},
|
|
23
|
+
implicit: {
|
|
24
|
+
name: 'Implicit',
|
|
25
|
+
description: 'Retrieve the access token directly.',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export function OauthDialog({ scheme, scopes, setToken, children, open, setOpen, }) {
|
|
29
|
+
const [type, setType] = useState(() => {
|
|
30
|
+
return Object.keys(scheme.flows)[0];
|
|
31
|
+
});
|
|
11
32
|
const form = useForm({
|
|
12
33
|
defaultValues: {
|
|
13
34
|
clientId: '',
|
|
14
35
|
clientSecret: '',
|
|
36
|
+
username: '',
|
|
37
|
+
password: '',
|
|
15
38
|
},
|
|
16
39
|
});
|
|
17
|
-
const
|
|
40
|
+
const authCodeCallback = useQuery(async (code, state) => {
|
|
18
41
|
const value = scheme.flows.authorizationCode;
|
|
19
42
|
const res = await fetch(value.tokenUrl, {
|
|
20
43
|
method: 'POST',
|
|
@@ -32,59 +55,123 @@ export function OauthDialog({ flow, scheme, setToken, children, }) {
|
|
|
32
55
|
});
|
|
33
56
|
if (!res.ok)
|
|
34
57
|
throw new Error(await res.text());
|
|
35
|
-
const { access_token } = (await res.json());
|
|
36
|
-
setToken(access_token);
|
|
58
|
+
const { access_token, token_type = 'Bearer' } = (await res.json());
|
|
59
|
+
setToken(`${token_type} ${access_token}`);
|
|
37
60
|
setOpen(false);
|
|
38
61
|
});
|
|
39
62
|
useEffect(() => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
if (scheme.flows.authorizationCode) {
|
|
64
|
+
const params = new URLSearchParams(window.location.search);
|
|
65
|
+
const state = params.get('state');
|
|
66
|
+
const code = params.get('code');
|
|
67
|
+
if (state && code) {
|
|
68
|
+
const parsedState = JSON.parse(state);
|
|
69
|
+
setOpen(true);
|
|
70
|
+
form.setValue('clientId', parsedState.client_id);
|
|
71
|
+
form.setValue('clientSecret', parsedState.client_secret);
|
|
72
|
+
authCodeCallback.start(code, parsedState);
|
|
73
|
+
window.history.replaceState(null, '', window.location.pathname);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (scheme.flows.implicit && window.location.hash.length > 1) {
|
|
78
|
+
const params = new URLSearchParams(window.location.hash.slice(1));
|
|
79
|
+
const state = params.get('state');
|
|
80
|
+
const token = params.get('access_token');
|
|
81
|
+
const type = params.get('token_type') ?? 'Bearer';
|
|
82
|
+
if (state && token) {
|
|
83
|
+
const parsedState = JSON.parse(state);
|
|
84
|
+
form.setValue('clientId', parsedState.client_id);
|
|
85
|
+
setToken(`${type} ${token}`);
|
|
86
|
+
window.history.replaceState(null, '', window.location.pathname);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
51
89
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- first page load only
|
|
52
90
|
}, []);
|
|
53
91
|
const authorize = useQuery(async (values) => {
|
|
54
|
-
if (
|
|
55
|
-
const value = scheme.flows[
|
|
92
|
+
if (type === 'implicit') {
|
|
93
|
+
const value = scheme.flows[type];
|
|
94
|
+
const params = new URLSearchParams();
|
|
95
|
+
params.set('response_type', 'token');
|
|
96
|
+
params.set('client_id', values.clientId);
|
|
97
|
+
params.set('redirect_uri', window.location.href);
|
|
98
|
+
params.set('scope', scopes.join('+'));
|
|
99
|
+
params.set('state', JSON.stringify({
|
|
100
|
+
client_id: values.clientId,
|
|
101
|
+
redirect_uri: window.location.href,
|
|
102
|
+
}));
|
|
103
|
+
window.location.replace(`${value.authorizationUrl}?${params.toString()}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (type === 'authorizationCode') {
|
|
107
|
+
const value = scheme.flows[type];
|
|
56
108
|
const params = new URLSearchParams();
|
|
57
109
|
params.set('response_type', 'code');
|
|
58
110
|
params.set('client_id', values.clientId);
|
|
59
111
|
params.set('redirect_uri', window.location.href);
|
|
60
|
-
params.set('scope',
|
|
112
|
+
params.set('scope', scopes.join('+'));
|
|
61
113
|
params.set('state', JSON.stringify({
|
|
62
114
|
client_id: values.clientId,
|
|
63
115
|
client_secret: values.clientSecret,
|
|
64
116
|
redirect_uri: window.location.href,
|
|
65
117
|
}));
|
|
66
118
|
window.location.replace(`${value.authorizationUrl}?${params.toString()}`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let res;
|
|
122
|
+
if (type === 'password') {
|
|
123
|
+
const value = scheme.flows[type];
|
|
124
|
+
res = await fetch(value.tokenUrl, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: {
|
|
127
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
128
|
+
},
|
|
129
|
+
body: new URLSearchParams({
|
|
130
|
+
grant_type: 'password',
|
|
131
|
+
username: values.username,
|
|
132
|
+
password: values.password,
|
|
133
|
+
scope: scopes.join('+'),
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
67
136
|
}
|
|
68
|
-
if (
|
|
69
|
-
const value = scheme.flows[
|
|
70
|
-
await fetch(value.tokenUrl, {
|
|
137
|
+
if (type === 'clientCredentials') {
|
|
138
|
+
const value = scheme.flows[type];
|
|
139
|
+
res = await fetch(value.tokenUrl, {
|
|
71
140
|
method: 'POST',
|
|
72
141
|
headers: {
|
|
73
142
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
74
143
|
},
|
|
75
144
|
body: new URLSearchParams({
|
|
76
145
|
grant_type: 'client_credentials',
|
|
77
|
-
|
|
146
|
+
client_id: values.clientId,
|
|
147
|
+
client_secret: values.clientSecret,
|
|
148
|
+
scope: scopes.join('+'),
|
|
78
149
|
}),
|
|
79
150
|
});
|
|
80
151
|
}
|
|
152
|
+
if (res) {
|
|
153
|
+
if (!res.ok)
|
|
154
|
+
throw new Error(await res.text());
|
|
155
|
+
const { access_token, token_type = 'Bearer' } = (await res.json());
|
|
156
|
+
setToken(`${token_type} ${access_token}`);
|
|
157
|
+
setOpen(false);
|
|
158
|
+
}
|
|
81
159
|
});
|
|
82
160
|
const onSubmit = form.handleSubmit((values) => {
|
|
83
161
|
return authorize.start(values);
|
|
84
162
|
});
|
|
85
|
-
const isLoading = authorize.isLoading ||
|
|
86
|
-
|
|
163
|
+
const isLoading = authorize.isLoading || authCodeCallback.isLoading;
|
|
164
|
+
const error = authCodeCallback.error ?? authorize.error;
|
|
165
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [children, _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Authorization" }), _jsx(DialogDescription, { children: "Obtain the access token for API." })] }), _jsxs("form", { className: "flex flex-col gap-6", onSubmit: (e) => {
|
|
166
|
+
void onSubmit(e);
|
|
167
|
+
e.stopPropagation();
|
|
168
|
+
}, children: [_jsxs(Select, { value: type, onValueChange: setType, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.keys(scheme.flows).map((key) => {
|
|
169
|
+
const { name, description } = FlowTypes[key];
|
|
170
|
+
return (_jsxs(SelectItem, { value: key, children: [_jsx("p", { className: "font-medium", children: name }), _jsx("p", { className: "text-fd-muted-foreground", children: description })] }, key));
|
|
171
|
+
}) })] }), (type === 'authorizationCode' ||
|
|
172
|
+
type === 'clientCredentials' ||
|
|
173
|
+
type === 'implicit') && (_jsxs("fieldset", { className: "flex flex-col gap-1.5", children: [_jsx("label", { htmlFor: "client_id", className: cn(labelVariants()), children: "Client ID" }), _jsx("p", { className: "text-fd-muted-foreground text-sm", children: "The client ID of your OAuth application." }), _jsx(Input, { id: "client_id", placeholder: "Enter value", type: "text", autoComplete: "off", disabled: isLoading, ...form.register('clientId', { required: true }) })] })), (type === 'authorizationCode' || type === 'clientCredentials') && (_jsxs("fieldset", { className: "flex flex-col gap-1.5", children: [_jsx("label", { htmlFor: "client_secret", className: cn(labelVariants()), children: "Client Secret" }), _jsx("p", { className: "text-fd-muted-foreground text-sm", children: "The client secret of your OAuth application." }), _jsx(Input, { id: "client_secret", placeholder: "Enter value", type: "password", autoComplete: "off", disabled: isLoading, ...form.register('clientSecret', { required: true }) })] })), type === 'password' && (_jsxs(_Fragment, { children: [_jsxs("fieldset", { className: "flex flex-col gap-1.5", children: [_jsx("label", { htmlFor: "username", className: cn(labelVariants()), children: "Username" }), _jsx(Input, { id: "username", placeholder: "Enter value", type: "text", disabled: isLoading, autoComplete: "off", ...form.register('username', { required: true }) })] }), _jsxs("fieldset", { className: "flex flex-col gap-1.5", children: [_jsx("label", { htmlFor: "password", className: cn(labelVariants()), children: "Client Secret" }), _jsx(Input, { id: "password", placeholder: "Enter value", type: "password", autoComplete: "off", disabled: isLoading, ...form.register('password', { required: true }) })] })] })), error ? (_jsx("p", { className: "text-red-400 font-medium text-sm", children: String(error) })) : null, _jsx("button", { type: "submit", disabled: isLoading, className: cn(buttonVariants({
|
|
87
174
|
color: 'primary',
|
|
88
|
-
})), children:
|
|
175
|
+
})), children: authCodeCallback.isLoading ? 'Fetching token...' : 'Submit' })] })] })] }));
|
|
89
176
|
}
|
|
90
177
|
export const OauthDialogTrigger = DialogTrigger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/playground/client.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,EAAE,EAEP,KAAK,cAAc,EAEnB,KAAK,YAAY,EAKlB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACrB,SAAS,EACT,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/playground/client.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,EAAE,EAEP,KAAK,cAAc,EAEnB,KAAK,YAAY,EAKlB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACrB,SAAS,EACT,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,MAAM,oBAAoB,CAAC;AAmC5B,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,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;AAED,MAAM,WAAW,WAAY,SAAQ,cAAc,CAAC,eAAe,CAAC;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,UAAU,EAAE,aAAa,EAAE,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE;QACL,MAAM,EAAE,aAAa,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,WAAW,CACrB,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,EACnC,cAAc,CACf,CAAC;QACF,IAAI,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC,CAAC;QACzD,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;AAgCD,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAC7B,KAAK,EACL,MAAc,EACd,UAAU,EACV,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,EACV,QAAQ,EACR,UAAU,EAAE,EAAE,aAAoC,EAAO,EACzD,GAAG,IAAI,EACR,EAAE,WAAW,2CAqKb"}
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { Fragment, lazy, useEffect, useMemo, useState, } from 'react';
|
|
4
4
|
import { Controller, FormProvider, useForm, useFormContext, } from 'react-hook-form';
|
|
5
5
|
import { useApiContext, useServerSelectContext } from '../ui/contexts/api.js';
|
|
6
|
-
import { FieldSet, JsonInput } from './inputs.js';
|
|
6
|
+
import { FieldInput, FieldSet, JsonInput, ObjectInput } from './inputs.js';
|
|
7
7
|
import { getStatusInfo } from './status-info.js';
|
|
8
8
|
import { getUrl } from '../utils/server-url.js';
|
|
9
9
|
import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
|
|
@@ -17,6 +17,8 @@ import { SchemaProvider, useResolvedSchema, } from '../playground/schema.js';
|
|
|
17
17
|
import { useRequestDataUpdater, useRequestInitialData, } from '../ui/contexts/code-example.js';
|
|
18
18
|
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
|
|
19
19
|
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
|
|
20
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../ui/components/select.js';
|
|
21
|
+
import { labelVariants } from '../ui/components/input.js';
|
|
20
22
|
const AuthPrefix = '__fumadocs_auth';
|
|
21
23
|
function toRequestData(method, mediaType, value) {
|
|
22
24
|
return {
|
|
@@ -30,6 +32,12 @@ function toRequestData(method, mediaType, value) {
|
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
const ServerSelect = lazy(() => import('../ui/server-select.js'));
|
|
35
|
+
const OauthDialog = lazy(() => import('./auth/oauth-dialog.js').then((mod) => ({
|
|
36
|
+
default: mod.OauthDialog,
|
|
37
|
+
})));
|
|
38
|
+
const OauthDialogTrigger = lazy(() => import('./auth/oauth-dialog.js').then((mod) => ({
|
|
39
|
+
default: mod.OauthDialogTrigger,
|
|
40
|
+
})));
|
|
33
41
|
export default function Client({ route, method = 'GET', securities, parameters, body, fields, references, proxyUrl, components: { ResultDisplay = DefaultResultDisplay } = {}, ...rest }) {
|
|
34
42
|
const { server } = useServerSelectContext();
|
|
35
43
|
const requestData = useRequestInitialData();
|
|
@@ -58,29 +66,45 @@ export default function Client({ route, method = 'GET', securities, parameters,
|
|
|
58
66
|
...toRequestData(method, body?.mediaType, input),
|
|
59
67
|
});
|
|
60
68
|
});
|
|
61
|
-
function initAuthValues() {
|
|
69
|
+
function initAuthValues(values, inputs) {
|
|
62
70
|
for (const item of inputs) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
manipulateValues(values, item.fieldName, () => {
|
|
72
|
+
const stored = localStorage.getItem(AuthPrefix + item.original.id);
|
|
73
|
+
if (stored) {
|
|
74
|
+
const parsed = JSON.parse(stored);
|
|
75
|
+
if (typeof parsed === typeof item.defaultValue)
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
return item.defaultValue;
|
|
79
|
+
});
|
|
72
80
|
}
|
|
81
|
+
return values;
|
|
73
82
|
}
|
|
74
83
|
useOnChange(defaultValues, () => {
|
|
75
84
|
fieldInfoMap.clear();
|
|
76
|
-
form.reset(defaultValues);
|
|
77
|
-
|
|
85
|
+
form.reset(initAuthValues(defaultValues, inputs));
|
|
86
|
+
});
|
|
87
|
+
useOnChange(inputs, (current, previous) => {
|
|
88
|
+
form.reset((values) => {
|
|
89
|
+
for (const item of previous) {
|
|
90
|
+
if (current.some(({ original }) => original.id === item.original.id)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
manipulateValues(values, item.fieldName, () => undefined);
|
|
94
|
+
}
|
|
95
|
+
return initAuthValues(values, current);
|
|
96
|
+
});
|
|
78
97
|
});
|
|
79
|
-
|
|
80
|
-
for (const item of
|
|
81
|
-
|
|
98
|
+
const onUpdateDebounced = useEffectEvent((values) => {
|
|
99
|
+
for (const item of inputs) {
|
|
100
|
+
const value = item.fieldName
|
|
101
|
+
.split('.')
|
|
102
|
+
.reduce((v, seg) => v[seg], values);
|
|
103
|
+
if (value) {
|
|
104
|
+
localStorage.setItem(AuthPrefix + item.original.id, JSON.stringify(value));
|
|
105
|
+
}
|
|
82
106
|
}
|
|
83
|
-
|
|
107
|
+
updater.setData(toRequestData(method, body?.mediaType, mapInputs(values)));
|
|
84
108
|
});
|
|
85
109
|
useEffect(() => {
|
|
86
110
|
let timer = null;
|
|
@@ -91,27 +115,36 @@ export default function Client({ route, method = 'GET', securities, parameters,
|
|
|
91
115
|
callback({ values }) {
|
|
92
116
|
if (timer)
|
|
93
117
|
window.clearTimeout(timer);
|
|
94
|
-
timer = window.setTimeout(() =>
|
|
95
|
-
for (const item of inputs) {
|
|
96
|
-
const value = item.fieldName
|
|
97
|
-
.split('.')
|
|
98
|
-
.reduce((v, seg) => v[seg], values);
|
|
99
|
-
if (value) {
|
|
100
|
-
localStorage.setItem(AuthPrefix + item.original.id, JSON.stringify(value));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
updater.setData(toRequestData(method, body?.mediaType, mapInputs(values)));
|
|
104
|
-
}, timer ? 400 : 0);
|
|
118
|
+
timer = window.setTimeout(() => onUpdateDebounced(values), timer ? 400 : 0);
|
|
105
119
|
},
|
|
106
120
|
});
|
|
107
|
-
initAuthValues();
|
|
121
|
+
form.reset((values) => initAuthValues(values, inputs));
|
|
108
122
|
return () => subscription();
|
|
109
123
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- mounted once only
|
|
110
124
|
}, []);
|
|
111
125
|
const onSubmit = form.handleSubmit((value) => {
|
|
112
126
|
testQuery.start(mapInputs(value));
|
|
113
127
|
});
|
|
114
|
-
return (_jsx(FormProvider, { ...form, children: _jsx(SchemaProvider, { fieldInfoMap: fieldInfoMap, references: references, children: _jsxs("form", { ...rest, className: cn('not-prose flex flex-col rounded-xl border shadow-md overflow-hidden bg-fd-card text-fd-card-foreground', rest.className), onSubmit: onSubmit, children: [_jsx(ServerSelect, {}), _jsxs("div", { className: "flex flex-row items-center gap-2 text-sm p-3 pb-0", 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: testQuery.isLoading, children: testQuery.isLoading ? (_jsx(LoaderCircle, { className: "size-4 animate-spin" })) : ('Send') })] }), securities.length > 0 && (
|
|
128
|
+
return (_jsx(FormProvider, { ...form, children: _jsx(SchemaProvider, { fieldInfoMap: fieldInfoMap, references: references, children: _jsxs("form", { ...rest, className: cn('not-prose flex flex-col rounded-xl border shadow-md overflow-hidden bg-fd-card text-fd-card-foreground', rest.className), onSubmit: onSubmit, children: [_jsx(ServerSelect, {}), _jsxs("div", { className: "flex flex-row items-center gap-2 text-sm p-3 pb-0", 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: testQuery.isLoading, children: testQuery.isLoading ? (_jsx(LoaderCircle, { className: "size-4 animate-spin" })) : ('Send') })] }), securities.length > 0 && (_jsx(SecurityTabs, { securities: securities, securityId: securityId, setSecurityId: setSecurityId, children: inputs.map((input) => (_jsx(Fragment, { children: input.children }, input.fieldName))) })), _jsx(FormBody, { body: body, fields: fields, parameters: parameters }), testQuery.data ? _jsx(ResultDisplay, { data: testQuery.data }) : null] }) }) }));
|
|
129
|
+
}
|
|
130
|
+
function SecurityTabs({ securities, setSecurityId, securityId, children, }) {
|
|
131
|
+
const [open, setOpen] = useState(false);
|
|
132
|
+
const form = useFormContext();
|
|
133
|
+
const result = (_jsxs(CollapsiblePanel, { title: "Authorization", children: [_jsxs(Select, { value: securityId.toString(), onValueChange: (v) => setSecurityId(Number(v)), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: securities.map((security, i) => (_jsx(SelectItem, { value: i.toString(), children: security.map((item) => item.id).join(' & ') }, i))) })] }), children] }));
|
|
134
|
+
for (let i = 0; i < securities.length; i++) {
|
|
135
|
+
const security = securities[i];
|
|
136
|
+
for (const item of security) {
|
|
137
|
+
if (item.type === 'oauth2') {
|
|
138
|
+
return (_jsx(OauthDialog, { scheme: item, scopes: item.scopes, open: open, setOpen: (v) => {
|
|
139
|
+
setOpen(v);
|
|
140
|
+
if (v) {
|
|
141
|
+
setSecurityId(i);
|
|
142
|
+
}
|
|
143
|
+
}, setToken: (token) => form.setValue('header.Authorization', token), children: result }));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
115
148
|
}
|
|
116
149
|
const paramNames = ['Headers', 'Cookies', 'Query', 'Path'];
|
|
117
150
|
const paramTypes = ['header', 'cookie', 'query', 'path'];
|
|
@@ -142,7 +175,7 @@ function BodyInput({ field: _field }) {
|
|
|
142
175
|
color: 'ghost',
|
|
143
176
|
size: 'sm',
|
|
144
177
|
className: 'p-2',
|
|
145
|
-
})), onClick: () => setIsJson(false), type: "button", children: "Close JSON Editor" }) })) : (_jsx(FieldSet, { field: field, fieldName: "body", name: _jsx("button", { className: cn(buttonVariants({
|
|
178
|
+
})), onClick: () => setIsJson(false), type: "button", children: "Close JSON Editor" }) })) : (_jsx(FieldSet, { field: field, fieldName: "body", collapsible: false, name: _jsx("button", { className: cn(buttonVariants({
|
|
146
179
|
color: 'secondary',
|
|
147
180
|
size: 'sm',
|
|
148
181
|
className: 'p-2',
|
|
@@ -153,15 +186,16 @@ function BodyInput({ field: _field }) {
|
|
|
153
186
|
*
|
|
154
187
|
* @returns a new manipulated object
|
|
155
188
|
*/
|
|
156
|
-
function manipulateValues(values, fieldName, update) {
|
|
157
|
-
const root = { ...values };
|
|
189
|
+
function manipulateValues(values, fieldName, update, clone = false) {
|
|
190
|
+
const root = clone ? { ...values } : values;
|
|
158
191
|
let current = root;
|
|
159
192
|
const segments = fieldName.split('.');
|
|
160
193
|
for (let i = 0; i < segments.length; i++) {
|
|
161
194
|
const segment = segments[i];
|
|
162
195
|
if (i !== segments.length - 1) {
|
|
163
196
|
let v = current[segment];
|
|
164
|
-
|
|
197
|
+
if (clone)
|
|
198
|
+
v = { ...v };
|
|
165
199
|
current[segment] = v;
|
|
166
200
|
current = v;
|
|
167
201
|
continue;
|
|
@@ -197,10 +231,8 @@ function useAuthInputs(securities) {
|
|
|
197
231
|
}
|
|
198
232
|
return out;
|
|
199
233
|
},
|
|
200
|
-
children: (_jsx(
|
|
234
|
+
children: (_jsx(ObjectInput, { field: {
|
|
201
235
|
type: 'object',
|
|
202
|
-
required: ['username', 'password'],
|
|
203
|
-
description: security.description,
|
|
204
236
|
properties: {
|
|
205
237
|
username: {
|
|
206
238
|
type: 'string',
|
|
@@ -212,16 +244,29 @@ function useAuthInputs(securities) {
|
|
|
212
244
|
}, fieldName: fieldName })),
|
|
213
245
|
});
|
|
214
246
|
}
|
|
215
|
-
else if (security.type === '
|
|
247
|
+
else if (security.type === 'oauth2') {
|
|
216
248
|
const fieldName = 'header.Authorization';
|
|
217
249
|
result.push({
|
|
218
250
|
fieldName: fieldName,
|
|
219
251
|
original: security,
|
|
220
252
|
defaultValue: 'Bearer ',
|
|
221
|
-
children: (_jsxs(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
253
|
+
children: (_jsxs("fieldset", { className: "flex flex-col gap-2", children: [_jsx("label", { htmlFor: fieldName, className: cn(labelVariants()), children: "Access Token" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(FieldInput, { fieldName: fieldName, isRequired: true, field: {
|
|
254
|
+
type: 'string',
|
|
255
|
+
}, className: "flex-1" }), _jsx(OauthDialogTrigger, { type: "button", className: cn(buttonVariants({
|
|
256
|
+
size: 'sm',
|
|
257
|
+
color: 'secondary',
|
|
258
|
+
})), children: "Authorize" })] })] })),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
else if (security.type === 'http') {
|
|
262
|
+
const fieldName = 'header.Authorization';
|
|
263
|
+
result.push({
|
|
264
|
+
fieldName: fieldName,
|
|
265
|
+
original: security,
|
|
266
|
+
defaultValue: 'Bearer ',
|
|
267
|
+
children: (_jsx(FieldSet, { name: "Authorization (header)", fieldName: fieldName, isRequired: true, field: {
|
|
268
|
+
type: 'string',
|
|
269
|
+
} })),
|
|
225
270
|
});
|
|
226
271
|
}
|
|
227
272
|
else if (security.type === 'apiKey') {
|
|
@@ -230,13 +275,22 @@ function useAuthInputs(securities) {
|
|
|
230
275
|
fieldName,
|
|
231
276
|
defaultValue: '',
|
|
232
277
|
original: security,
|
|
233
|
-
children: (_jsx(FieldSet, { fieldName: fieldName, name: security.name, field: {
|
|
278
|
+
children: (_jsx(FieldSet, { fieldName: fieldName, name: `${security.name} (${security.in})`, isRequired: true, field: {
|
|
234
279
|
type: 'string',
|
|
235
|
-
description: security.description ?? `The API key in ${security.in}.`,
|
|
236
280
|
} })),
|
|
237
281
|
});
|
|
238
282
|
}
|
|
239
|
-
|
|
283
|
+
else {
|
|
284
|
+
const fieldName = 'header.Authorization';
|
|
285
|
+
result.push({
|
|
286
|
+
fieldName,
|
|
287
|
+
defaultValue: '',
|
|
288
|
+
original: security,
|
|
289
|
+
children: (_jsxs(_Fragment, { children: [_jsx(FieldSet, { name: "Authorization (header)", isRequired: true, fieldName: fieldName, field: {
|
|
290
|
+
type: 'string',
|
|
291
|
+
} }), _jsx("p", { className: "text-fd-muted-foreground text-xs", children: "OpenID Connect is not supported at the moment, you can still set an access token here." })] })),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
240
294
|
}
|
|
241
295
|
return result;
|
|
242
296
|
}, [securities]);
|
|
@@ -244,7 +298,7 @@ function useAuthInputs(securities) {
|
|
|
244
298
|
for (const item of inputs) {
|
|
245
299
|
if (!item.mapOutput)
|
|
246
300
|
continue;
|
|
247
|
-
values = manipulateValues(values, item.fieldName, item.mapOutput);
|
|
301
|
+
values = manipulateValues(values, item.fieldName, item.mapOutput, true);
|
|
248
302
|
}
|
|
249
303
|
return values;
|
|
250
304
|
});
|
|
@@ -255,22 +309,6 @@ function renderCustomField(fieldName, info, field, key) {
|
|
|
255
309
|
// @ts-expect-error we use string here
|
|
256
310
|
render: (props) => field.render({ ...props, info }), name: fieldName }, key));
|
|
257
311
|
}
|
|
258
|
-
const OauthDialog = lazy(() => import('./auth/oauth-dialog.js').then((mod) => ({
|
|
259
|
-
default: mod.OauthDialog,
|
|
260
|
-
})));
|
|
261
|
-
const OauthDialogTrigger = lazy(() => import('./auth/oauth-dialog.js').then((mod) => ({
|
|
262
|
-
default: mod.OauthDialogTrigger,
|
|
263
|
-
})));
|
|
264
|
-
function OAuth({ authorization }) {
|
|
265
|
-
const form = useFormContext();
|
|
266
|
-
if (authorization.type !== 'oauth2')
|
|
267
|
-
return;
|
|
268
|
-
// only the first one, so it looks simpler :)
|
|
269
|
-
const flow = Object.keys(authorization.flows)[0];
|
|
270
|
-
return (_jsx(OauthDialog, { flow: flow, scheme: authorization, setToken: (token) => form.setValue('authorization', `Bearer ${token}`), children: _jsx(OauthDialogTrigger, { type: "button", className: cn(buttonVariants({
|
|
271
|
-
color: 'secondary',
|
|
272
|
-
})), children: "Auth Settings" }) }));
|
|
273
|
-
}
|
|
274
312
|
function Route({ route, ...props }) {
|
|
275
313
|
const segments = route.split('/').filter((part) => part.length > 0);
|
|
276
314
|
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))) }));
|
|
@@ -278,7 +316,7 @@ function Route({ route, ...props }) {
|
|
|
278
316
|
function DefaultResultDisplay({ data }) {
|
|
279
317
|
const statusInfo = useMemo(() => getStatusInfo(data.status), [data.status]);
|
|
280
318
|
const { shikiOptions } = useApiContext();
|
|
281
|
-
return (_jsxs("div", { className: "flex flex-col gap-3
|
|
319
|
+
return (_jsxs("div", { className: "flex flex-col gap-3 p-3", 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
|
|
282
320
|
? 'text'
|
|
283
321
|
: data.type, code: typeof data.data === 'string'
|
|
284
322
|
? data.data
|
package/dist/playground/index.js
CHANGED
|
@@ -47,22 +47,32 @@ function writeReferences(schema, ctx, stack = new WeakMap()) {
|
|
|
47
47
|
}
|
|
48
48
|
const output = { ...schema };
|
|
49
49
|
stack.set(schema, output);
|
|
50
|
-
for (const
|
|
50
|
+
for (const _n in output) {
|
|
51
|
+
const name = _n;
|
|
51
52
|
if (!output[name])
|
|
52
53
|
continue;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
54
|
+
switch (name) {
|
|
55
|
+
case 'oneOf':
|
|
56
|
+
case 'allOf':
|
|
57
|
+
case 'anyOf':
|
|
58
|
+
output[name] = output[name].map((item) => writeReferences(item, ctx, stack));
|
|
59
|
+
continue;
|
|
60
|
+
case 'items':
|
|
61
|
+
case 'additionalProperties':
|
|
62
|
+
case 'not':
|
|
63
|
+
output[name] = writeReferences(output[name], ctx, stack);
|
|
64
|
+
continue;
|
|
65
|
+
case 'properties':
|
|
66
|
+
case 'patternProperties':
|
|
67
|
+
output[name] = { ...output[name] };
|
|
68
|
+
for (const key in output[name]) {
|
|
69
|
+
output[name][key] = writeReferences(output[name][key], ctx, stack);
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
default:
|
|
73
|
+
if (typeof output[name] === 'object' && !Array.isArray(output[name])) {
|
|
74
|
+
output[name] = { ...output[name] };
|
|
75
|
+
}
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
return output;
|
|
@@ -84,7 +94,8 @@ function parseSecurities(method, { schema: { document } }) {
|
|
|
84
94
|
id: key,
|
|
85
95
|
});
|
|
86
96
|
}
|
|
87
|
-
|
|
97
|
+
if (list.length > 0)
|
|
98
|
+
result.push(list);
|
|
88
99
|
}
|
|
89
100
|
return result;
|
|
90
101
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ComponentProps, type HTMLAttributes, type ReactNode } from 'react';
|
|
1
|
+
import { type ComponentProps, type HTMLAttributes, type ReactNode } from 'react';
|
|
2
2
|
import type { RequestSchema } from '../playground/index.js';
|
|
3
3
|
export declare function ObjectInput({ field: _field, fieldName, ...props }: {
|
|
4
4
|
field: Exclude<RequestSchema, boolean>;
|
|
@@ -8,7 +8,12 @@ export declare function JsonInput({ fieldName, children, }: {
|
|
|
8
8
|
fieldName: string;
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function FieldInput({ field, fieldName, isRequired, ...props }: HTMLAttributes<HTMLElement> & {
|
|
12
|
+
field: Exclude<RequestSchema, boolean>;
|
|
13
|
+
isRequired?: boolean;
|
|
14
|
+
fieldName: string;
|
|
15
|
+
}): import("react/jsx-runtime").JSX.Element | undefined;
|
|
16
|
+
export declare function FieldSet({ field: _field, fieldName, toolbar, name, isRequired, depth, slotType, collapsible, ...props }: HTMLAttributes<HTMLElement> & {
|
|
12
17
|
isRequired?: boolean;
|
|
13
18
|
name?: ReactNode;
|
|
14
19
|
field: RequestSchema;
|
|
@@ -16,5 +21,6 @@ export declare function FieldSet({ field: _field, fieldName, toolbar, name, isRe
|
|
|
16
21
|
depth?: number;
|
|
17
22
|
slotType?: ReactNode;
|
|
18
23
|
toolbar?: ReactNode;
|
|
24
|
+
collapsible?: boolean;
|
|
19
25
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
20
26
|
//# sourceMappingURL=inputs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/playground/inputs.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,
|
|
1
|
+
{"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/playground/inputs.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAef,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAkDxD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EAAE,MAAM,EACb,SAAS,EACT,GAAG,KAAK,EACT,EAAE;IACD,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,cAAc,CAAC,KAAK,CAAC,uDAwCxB;AAED,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CA0BA;AA6FD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,UAAU,EACV,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,WAAW,CAAC,GAAG;IAC/B,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,uDAmFA;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EAAE,MAAM,EACb,SAAS,EACT,OAAO,EACP,IAAI,EACJ,UAAU,EACV,KAAS,EACT,QAAQ,EACR,WAAkB,EAClB,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,WAAW,CAAC,GAAG;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,kDA2KA"}
|