fumadocs-openapi 9.0.2 → 9.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.
@@ -1,13 +1,8 @@
1
1
  import { type FC, type HTMLAttributes, type ReactElement } from 'react';
2
2
  import type { ControllerFieldState, ControllerRenderProps, FieldPath, UseFormStateReturn } from 'react-hook-form';
3
3
  import type { FetchResult } from '../playground/fetcher.js';
4
- import type { ParameterField, RequestSchema } from '../playground/index.js';
5
- import type { Security } from '../utils/get-security.js';
4
+ import type { ParameterField, RequestSchema, SecurityEntry } from '../playground/index.js';
6
5
  interface FormValues {
7
- authorization: string | {
8
- username: string;
9
- password: string;
10
- };
11
6
  path: Record<string, string>;
12
7
  query: Record<string, string>;
13
8
  header: Record<string, string>;
@@ -25,13 +20,11 @@ export interface CustomField<TName extends FieldPath<FormValues>, Info> {
25
20
  formState: UseFormStateReturn<FormValues>;
26
21
  }) => ReactElement;
27
22
  }
28
- export type ClientProps = HTMLAttributes<HTMLFormElement> & {
23
+ export interface ClientProps extends HTMLAttributes<HTMLFormElement> {
29
24
  route: string;
30
25
  method: string;
31
- authorization?: Security & {
32
- persistentId: string;
33
- };
34
26
  parameters?: ParameterField[];
27
+ securities: SecurityEntry[][];
35
28
  body?: {
36
29
  schema: RequestSchema;
37
30
  mediaType: string;
@@ -43,7 +36,7 @@ export type ClientProps = HTMLAttributes<HTMLFormElement> & {
43
36
  proxyUrl?: string;
44
37
  fields?: {
45
38
  parameter?: CustomField<`${ParameterField['in']}.${string}`, ParameterField>;
46
- auth?: CustomField<'authorization', RequestSchema>;
39
+ auth?: CustomField<FieldPath<FormValues>, RequestSchema>;
47
40
  body?: CustomField<'body', RequestSchema>;
48
41
  };
49
42
  components?: Partial<{
@@ -51,7 +44,7 @@ export type ClientProps = HTMLAttributes<HTMLFormElement> & {
51
44
  data: FetchResult;
52
45
  }>;
53
46
  }>;
54
- };
55
- export default function Client({ route, method, authorization, parameters, body, fields, references, proxyUrl, components: { ResultDisplay }, ...rest }: ClientProps): import("react/jsx-runtime").JSX.Element;
47
+ }
48
+ export default function Client({ route, method, securities, parameters, body, fields, references, proxyUrl, components: { ResultDisplay }, ...rest }: ClientProps): import("react/jsx-runtime").JSX.Element;
56
49
  export {};
57
50
  //# sourceMappingURL=client.d.ts.map
@@ -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,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAYxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAYrD,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,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,MAAM,WAAW,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,QAAQ,GAAG;QACzB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,UAAU,CAAC,EAAE,cAAc,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,eAAe,EAAE,aAAa,CAAC,CAAC;QACnD,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,CAAC;AAoBF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAC7B,KAAK,EACL,MAAc,EACd,aAAa,EACb,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,EACV,QAAQ,EACR,UAAU,EAAE,EAAE,aAAoC,EAAO,EACzD,GAAG,IAAI,EACR,EAAE,WAAW,2CAkHb"}
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;AA2B5B,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;AAsBD,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,2CAgLb"}
@@ -11,11 +11,13 @@ import { MethodLabel } from '../ui/components/method-label.js';
11
11
  import { useQuery } from '../utils/use-query.js';
12
12
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from 'fumadocs-ui/components/ui/collapsible';
13
13
  import { ChevronDown, LoaderCircle } from '../icons.js';
14
- import { useRequestData } from '../ui/contexts/code-example.js';
15
- import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
16
14
  import { buttonVariants } from 'fumadocs-ui/components/ui/button';
17
15
  import { cn } from 'fumadocs-ui/utils/cn';
18
16
  import { SchemaProvider, useResolvedSchema, } from '../playground/schema.js';
17
+ import { useRequestDataUpdater, useRequestInitialData, } from '../ui/contexts/code-example.js';
18
+ import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
19
+ import { useOnChange } from 'fumadocs-core/utils/use-on-change';
20
+ const AuthPrefix = '__fumadocs_auth';
19
21
  function toRequestData(method, mediaType, value) {
20
22
  return {
21
23
  path: value.path,
@@ -28,20 +30,21 @@ function toRequestData(method, mediaType, value) {
28
30
  };
29
31
  }
30
32
  const ServerSelect = lazy(() => import('../ui/server-select.js'));
31
- export default function Client({ route, method = 'GET', authorization, parameters, body, fields, references, proxyUrl, components: { ResultDisplay = DefaultResultDisplay } = {}, ...rest }) {
33
+ export default function Client({ route, method = 'GET', securities, parameters, body, fields, references, proxyUrl, components: { ResultDisplay = DefaultResultDisplay } = {}, ...rest }) {
32
34
  const { server } = useServerSelectContext();
33
- const requestData = useRequestData();
35
+ const requestData = useRequestInitialData();
36
+ const updater = useRequestDataUpdater();
34
37
  const fieldInfoMap = useMemo(() => new Map(), []);
35
- const authInfo = usePersistentAuthInfo(authorization);
36
38
  const { mediaAdapters } = useApiContext();
39
+ const [securityId, setSecurityId] = useState(0);
40
+ const { inputs, mapInputs } = useAuthInputs(securities[securityId]);
37
41
  const defaultValues = useMemo(() => ({
38
- authorization: authInfo.info,
39
- path: requestData.data.path,
40
- query: requestData.data.query,
41
- header: requestData.data.header,
42
- body: requestData.data.body,
43
- cookie: requestData.data.cookie,
44
- }), [authInfo.info, requestData.data]);
42
+ path: requestData.path,
43
+ query: requestData.query,
44
+ header: requestData.header,
45
+ body: requestData.body,
46
+ cookie: requestData.cookie,
47
+ }), [requestData]);
45
48
  const form = useForm({
46
49
  defaultValues,
47
50
  });
@@ -55,97 +58,79 @@ export default function Client({ route, method = 'GET', authorization, parameter
55
58
  ...toRequestData(method, body?.mediaType, input),
56
59
  });
57
60
  });
58
- useEffect(() => {
61
+ function initAuthValues() {
62
+ for (const item of inputs) {
63
+ let value = item.defaultValue;
64
+ const stored = localStorage.getItem(AuthPrefix + item.original.id);
65
+ if (stored) {
66
+ const parsed = JSON.parse(stored);
67
+ if (typeof parsed === typeof item.defaultValue)
68
+ value = parsed;
69
+ }
70
+ // @ts-expect-error -- safe
71
+ form.setValue(item.fieldName, value);
72
+ }
73
+ }
74
+ useOnChange(defaultValues, () => {
59
75
  fieldInfoMap.clear();
60
76
  form.reset(defaultValues);
61
- // eslint-disable-next-line react-hooks/exhaustive-deps -- update default value
62
- }, [defaultValues]);
77
+ initAuthValues();
78
+ });
79
+ useOnChange(inputs, (_, previous) => {
80
+ for (const item of previous) {
81
+ form.reset((values) => manipulateValues(values, item.fieldName, () => undefined));
82
+ }
83
+ initAuthValues();
84
+ });
63
85
  useEffect(() => {
64
- const subscription = form.watch((_value) => {
65
- const value = _value;
66
- if (authorization && value.authorization) {
67
- authInfo.saveInfo(value.authorization);
68
- writeAuthInfo(authorization, value.authorization, value.header, value.query, value.cookie);
69
- }
70
- requestData.saveData(toRequestData(method, body?.mediaType, value));
86
+ let timer = null;
87
+ const subscription = form.subscribe({
88
+ formState: {
89
+ values: true,
90
+ },
91
+ callback({ values }) {
92
+ if (timer)
93
+ 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);
105
+ },
71
106
  });
72
- return () => {
73
- subscription.unsubscribe();
74
- };
75
- // eslint-disable-next-line react-hooks/exhaustive-deps -- mounted only once
107
+ initAuthValues();
108
+ return () => subscription();
109
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- mounted once only
76
110
  }, []);
77
111
  const onSubmit = form.handleSubmit((value) => {
78
- testQuery.start(value);
112
+ testQuery.start(mapInputs(value));
79
113
  });
80
- 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') })] }), _jsx(FormBody, { body: body, fields: fields, parameters: parameters, authorization: authorization }), testQuery.data ? _jsx(ResultDisplay, { data: testQuery.data }) : null, authorization && _jsx(AuthFooter, { authorization: authorization })] }) }) }));
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 && (_jsxs("div", { className: "m-3 p-3 border bg-fd-muted rounded-lg", children: [_jsxs("div", { className: "flex gap-4 items-center justify-between mb-4", children: [_jsx("p", { className: "text-sm font-medium", children: "Authorization" }), _jsx("select", { value: securityId.toString(), onChange: (e) => setSecurityId(Number(e.target.value)), className: "text-xs font-mono px-2 py-1.5 outline-none", children: securities.map((security, i) => (_jsx("option", { value: i, children: security.map((item) => item.id).join(' & ') }, i))) })] }), _jsx("div", { className: "flex flex-col gap-4", 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] }) }) }));
81
115
  }
82
116
  const paramNames = ['Headers', 'Cookies', 'Query', 'Path'];
83
117
  const paramTypes = ['header', 'cookie', 'query', 'path'];
84
- function FormBody({ authorization, parameters = [], fields = {}, body, }) {
118
+ function FormBody({ parameters = [], fields = {}, body, }) {
85
119
  const params = useMemo(() => {
86
120
  return paramTypes.map((param) => parameters.filter((v) => v.in === param));
87
121
  }, [parameters]);
88
- const auth = useMemo(() => {
89
- if (!authorization)
90
- return;
91
- if (authorization.type === 'http' && authorization.scheme === 'basic') {
92
- return {
93
- in: 'header',
94
- field: {
95
- type: 'object',
96
- required: ['username', 'password'],
97
- description: authorization.description,
98
- properties: {
99
- username: {
100
- type: 'string',
101
- },
102
- password: {
103
- type: 'string',
104
- },
105
- },
106
- },
107
- };
108
- }
109
- else if (authorization.type === 'http' ||
110
- authorization.type === 'oauth2') {
111
- return {
112
- in: 'header',
113
- field: {
114
- type: 'string',
115
- description: authorization.description ?? `The Authorization access token.`,
116
- },
117
- };
118
- }
119
- else if (authorization.type === 'apiKey') {
120
- return {
121
- in: authorization.in,
122
- field: {
123
- type: 'string',
124
- description: authorization.description ?? 'The API key.',
125
- },
126
- };
127
- }
128
- // TODO: handle OpenID connect
129
- }, [authorization]);
130
- function renderAuth() {
131
- if (!auth)
132
- return null;
133
- if (fields?.auth)
134
- return renderCustomField('authorization', auth.field, fields.auth);
135
- return (_jsx(FieldSet, { fieldName: "authorization", name: "Authorization", field: auth.field, isRequired: true }));
136
- }
137
122
  return (_jsxs(_Fragment, { children: [params.map((param, i) => {
123
+ if (param.length === 0)
124
+ return;
138
125
  const name = paramNames[i];
139
126
  const type = paramTypes[i];
140
- if ((!auth || type !== auth.in) && param.length === 0)
141
- return;
142
- return (_jsxs(CollapsiblePanel, { title: name, children: [auth && type === auth.in ? renderAuth() : null, param.map((field) => {
143
- const fieldName = `${type}.${field.name}`;
144
- if (fields?.parameter) {
145
- return renderCustomField(fieldName, field.schema, fields.parameter, field.name);
146
- }
147
- return (_jsx(FieldSet, { name: field.name, fieldName: fieldName, field: field.schema }, fieldName));
148
- })] }, name));
127
+ return (_jsx(CollapsiblePanel, { title: name, children: param.map((field) => {
128
+ const fieldName = `${type}.${field.name}`;
129
+ if (fields?.parameter) {
130
+ return renderCustomField(fieldName, field.schema, fields.parameter, field.name);
131
+ }
132
+ return (_jsx(FieldSet, { name: field.name, fieldName: fieldName, field: field.schema }, fieldName));
133
+ }) }, name));
149
134
  }), body && (_jsx(CollapsiblePanel, { title: "Body", children: fields.body ? (renderCustomField('body', body.schema, fields.body)) : (_jsx(BodyInput, { field: body.schema })) }))] }));
150
135
  }
151
136
  function BodyInput({ field: _field }) {
@@ -163,6 +148,108 @@ function BodyInput({ field: _field }) {
163
148
  className: 'p-2',
164
149
  })), onClick: () => setIsJson(true), type: "button", children: "Open JSON Editor" }) })) }));
165
150
  }
151
+ /**
152
+ * manipulate values without mutating the original object
153
+ *
154
+ * @returns a new manipulated object
155
+ */
156
+ function manipulateValues(values, fieldName, update) {
157
+ const root = { ...values };
158
+ let current = root;
159
+ const segments = fieldName.split('.');
160
+ for (let i = 0; i < segments.length; i++) {
161
+ const segment = segments[i];
162
+ if (i !== segments.length - 1) {
163
+ let v = current[segment];
164
+ v = { ...v };
165
+ current[segment] = v;
166
+ current = v;
167
+ continue;
168
+ }
169
+ const updated = update(current[segment]);
170
+ if (updated === undefined) {
171
+ delete current[segment];
172
+ }
173
+ else {
174
+ current[segment] = updated;
175
+ }
176
+ }
177
+ return root;
178
+ }
179
+ function useAuthInputs(securities) {
180
+ const inputs = useMemo(() => {
181
+ const result = [];
182
+ if (!securities)
183
+ return result;
184
+ for (const security of securities) {
185
+ if (security.type === 'http' && security.scheme === 'basic') {
186
+ const fieldName = `header.Authorization`;
187
+ result.push({
188
+ fieldName,
189
+ original: security,
190
+ defaultValue: {
191
+ username: '',
192
+ password: '',
193
+ },
194
+ mapOutput(out) {
195
+ if (out && typeof out === 'object') {
196
+ return `Basic ${btoa(`${'username' in out ? out.username : ''}:${'password' in out ? out.password : ''}`)}`;
197
+ }
198
+ return out;
199
+ },
200
+ children: (_jsx(FieldSet, { name: "Authorization (header)", field: {
201
+ type: 'object',
202
+ required: ['username', 'password'],
203
+ description: security.description,
204
+ properties: {
205
+ username: {
206
+ type: 'string',
207
+ },
208
+ password: {
209
+ type: 'string',
210
+ },
211
+ },
212
+ }, fieldName: fieldName })),
213
+ });
214
+ }
215
+ else if (security.type === 'http' || security.type === 'oauth2') {
216
+ const fieldName = 'header.Authorization';
217
+ result.push({
218
+ fieldName: fieldName,
219
+ original: security,
220
+ defaultValue: 'Bearer ',
221
+ children: (_jsxs(_Fragment, { children: [_jsx(FieldSet, { name: "Authorization (header)", fieldName: fieldName, field: {
222
+ type: 'string',
223
+ description: security.description ?? `The Authorization access token.`,
224
+ } }), _jsx(OAuth, { authorization: security })] })),
225
+ });
226
+ }
227
+ else if (security.type === 'apiKey') {
228
+ const fieldName = `${security.in}.${security.name}`;
229
+ result.push({
230
+ fieldName,
231
+ defaultValue: '',
232
+ original: security,
233
+ children: (_jsx(FieldSet, { fieldName: fieldName, name: security.name, field: {
234
+ type: 'string',
235
+ description: security.description ?? `The API key in ${security.in}.`,
236
+ } })),
237
+ });
238
+ }
239
+ // TODO: handle OpenID connect
240
+ }
241
+ return result;
242
+ }, [securities]);
243
+ const mapInputs = useEffectEvent((values) => {
244
+ for (const item of inputs) {
245
+ if (!item.mapOutput)
246
+ continue;
247
+ values = manipulateValues(values, item.fieldName, item.mapOutput);
248
+ }
249
+ return values;
250
+ });
251
+ return { inputs, mapInputs };
252
+ }
166
253
  function renderCustomField(fieldName, info, field, key) {
167
254
  return (_jsx(Controller, {
168
255
  // @ts-expect-error we use string here
@@ -174,7 +261,7 @@ const OauthDialog = lazy(() => import('./auth/oauth-dialog.js').then((mod) => ({
174
261
  const OauthDialogTrigger = lazy(() => import('./auth/oauth-dialog.js').then((mod) => ({
175
262
  default: mod.OauthDialogTrigger,
176
263
  })));
177
- function AuthFooter({ authorization }) {
264
+ function OAuth({ authorization }) {
178
265
  const form = useFormContext();
179
266
  if (authorization.type !== 'oauth2')
180
267
  return;
@@ -197,61 +284,6 @@ function DefaultResultDisplay({ data }) {
197
284
  ? data.data
198
285
  : JSON.stringify(data.data, null, 2), options: shikiOptions })) : null] }));
199
286
  }
200
- function usePersistentAuthInfo(authorization) {
201
- const key = authorization
202
- ? `__fumadocs_auth_${authorization.persistentId}`
203
- : null;
204
- const [info, setInfo] = useState(() => {
205
- if (!authorization || authorization.type === 'apiKey')
206
- return '';
207
- if (authorization.type === 'http' && authorization.scheme === 'basic') {
208
- return {
209
- username: '',
210
- password: '',
211
- };
212
- }
213
- return 'Bearer';
214
- });
215
- useEffect(() => {
216
- if (!key)
217
- return;
218
- const item = localStorage.getItem(key);
219
- if (item) {
220
- setInfo(JSON.parse(item));
221
- }
222
- }, [key]);
223
- return {
224
- info,
225
- saveInfo: useEffectEvent((value) => {
226
- if (!key)
227
- return;
228
- localStorage.setItem(key, JSON.stringify(value));
229
- }),
230
- };
231
- }
232
287
  function CollapsiblePanel({ title, children, ...props }) {
233
288
  return (_jsxs(Collapsible, { ...props, className: "border-b last:border-b-0", children: [_jsxs(CollapsibleTrigger, { className: "group w-full flex items-center gap-2 p-3 text-sm font-medium", children: [title, _jsx(ChevronDown, { className: "ms-auto size-3.5 text-fd-muted-foreground group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { children: _jsx("div", { className: "flex flex-col gap-3 p-3 pt-1", children: children }) })] }));
234
289
  }
235
- function writeAuthInfo(authorization, input, header, query, cookie) {
236
- if (authorization.type === 'apiKey') {
237
- if (authorization.in === 'header') {
238
- header[authorization.name] = input;
239
- }
240
- if (authorization.in === 'query') {
241
- query[authorization.name] = input;
242
- }
243
- if (authorization.in === 'cookie') {
244
- cookie[authorization.name] = input;
245
- }
246
- return;
247
- }
248
- if (authorization.type === 'http' &&
249
- authorization.scheme === 'basic' &&
250
- typeof input === 'object') {
251
- header.Authorization = `Basic ${btoa(`${input.username}:${input.password}`)}`;
252
- return;
253
- }
254
- if (typeof input === 'string') {
255
- header.Authorization = input;
256
- }
257
- }
@@ -1,4 +1,4 @@
1
- import type { MethodInformation, RenderContext } from '../types.js';
1
+ import type { MethodInformation, RenderContext, SecuritySchemeObject } from '../types.js';
2
2
  import { type ParsedSchema } from '../utils/schema.js';
3
3
  import { type ClientProps } from './client.js';
4
4
  export type ParameterField = {
@@ -15,5 +15,9 @@ export interface APIPlaygroundProps {
15
15
  client?: Partial<ClientProps>;
16
16
  }
17
17
  export type { ClientProps, CustomField } from './client.js';
18
+ export type SecurityEntry = SecuritySchemeObject & {
19
+ scopes: string[];
20
+ id: string;
21
+ };
18
22
  export declare function APIPlayground({ path, method, ctx, client, }: APIPlaygroundProps): Promise<import("react/jsx-runtime").JSX.Element>;
19
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/playground/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,EAAE,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC;AAQzC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,aAAa,CAAC;IAEnB,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC/B;AAED,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzD,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,MAAM,EACN,GAAG,EACH,MAAM,GACP,EAAE,kBAAkB,oDAuCpB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/playground/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACrB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,EAAE,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC;AAQzC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,aAAa,CAAC;IAEnB,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC/B;AAED,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzD,MAAM,MAAM,aAAa,GAAG,oBAAoB,GAAG;IACjD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,MAAM,EACN,GAAG,EACH,MAAM,GACP,EAAE,kBAAkB,oDAuCpB"}
@@ -1,6 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getPreferredType } from '../utils/schema.js';
3
- import { getSecurities } from '../utils/get-security.js';
4
3
  import { ClientLazy } from '../ui/lazy.js';
5
4
  export async function APIPlayground({ path, method, ctx, client, }) {
6
5
  let currentId = 0;
@@ -14,7 +13,7 @@ export async function APIPlayground({ path, method, ctx, client, }) {
14
13
  registered: new WeakMap(),
15
14
  };
16
15
  const props = {
17
- authorization: getAuthorizationField(method, ctx),
16
+ securities: parseSecurities(method, ctx),
18
17
  method: method.method,
19
18
  route: path,
20
19
  parameters: method.parameters?.map((v) => ({
@@ -68,32 +67,24 @@ function writeReferences(schema, ctx, stack = new WeakMap()) {
68
67
  }
69
68
  return output;
70
69
  }
71
- function getAuthorizationField(method, { schema: { document } }) {
70
+ function parseSecurities(method, { schema: { document } }) {
71
+ const result = [];
72
72
  const security = method.security ?? document.security ?? [];
73
73
  if (security.length === 0)
74
- return;
75
- let item;
76
- for (const requirements of security) {
77
- const keys = Object.keys(requirements).length;
78
- if (keys === 0)
79
- return;
80
- else if (keys === 1)
81
- item = requirements;
82
- }
83
- if (!item) {
84
- console.warn(`Cannot find suitable security scheme for API Playground from ${JSON.stringify(security, null, 2)}. Only schemes with one requirement are allowed at the moment.`);
85
- return;
86
- }
87
- const scheme = getSecurities(item, document)[0];
88
- if (scheme.type === 'oauth2') {
89
- const flow = Object.keys(scheme.flows).at(0);
90
- if (!flow)
91
- throw new Error("security scheme's `flows` must not be empty");
92
- if (flow === 'implicit' || flow === 'password')
93
- throw new Error(`OAuth 2.0 flow type: ${flow} is not supported, consider other types like \`authorizationCode\` instead.`);
74
+ return result;
75
+ for (const map of security) {
76
+ const list = [];
77
+ for (const [key, scopes] of Object.entries(map)) {
78
+ const scheme = document.components?.securitySchemes?.[key];
79
+ if (!scheme)
80
+ continue;
81
+ list.push({
82
+ ...scheme,
83
+ scopes,
84
+ id: key,
85
+ });
86
+ }
87
+ result.push(list);
94
88
  }
95
- return {
96
- persistentId: Object.keys(item)[0],
97
- ...scheme,
98
- };
89
+ return result;
99
90
  }
@@ -1,4 +1,4 @@
1
1
  import type { MethodInformation, RenderContext } from '../../types.js';
2
2
  import type { RequestData } from '../../requests/_shared.js';
3
- export declare function getRequestData(path: string, method: MethodInformation, sampleKey: string | null, { schema: { document } }: RenderContext): RequestData;
3
+ export declare function getRequestData(path: string, method: MethodInformation, sampleKey: string | null, _ctx: RenderContext): RequestData;
4
4
  //# sourceMappingURL=get-request-data.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-request-data.d.ts","sourceRoot":"","sources":["../../../src/render/operation/get-request-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAKhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,aAAa,GACtC,WAAW,CAwEb"}
1
+ {"version":3,"file":"get-request-data.d.ts","sourceRoot":"","sources":["../../../src/render/operation/get-request-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,IAAI,EAAE,aAAa,GAClB,WAAW,CA8Db"}
@@ -1,7 +1,6 @@
1
1
  import { getPreferredType } from '../../utils/schema.js';
2
2
  import { sample } from 'openapi-sampler';
3
- import { getSecurities, getSecurityPrefix } from '../../utils/get-security.js';
4
- export function getRequestData(path, method, sampleKey, { schema: { document } }) {
3
+ export function getRequestData(path, method, sampleKey, _ctx) {
5
4
  const result = {
6
5
  path: {},
7
6
  cookie: {},
@@ -36,14 +35,6 @@ export function getRequestData(path, method, sampleKey, { schema: { document } }
36
35
  result.path[param.name] = value;
37
36
  }
38
37
  }
39
- const requirements = method.security ?? document.security;
40
- if (requirements && requirements.length > 0) {
41
- for (const security of getSecurities(requirements[0], document)) {
42
- const prefix = getSecurityPrefix(security);
43
- const name = security.type === 'apiKey' ? security.name : 'Authorization';
44
- result.header[name] = prefix ? `${prefix} <token>` : '<token>';
45
- }
46
- }
47
38
  if (method.requestBody) {
48
39
  const body = method.requestBody.content;
49
40
  const type = getPreferredType(body);
@@ -67,5 +58,6 @@ function generateBody(method, schema) {
67
58
  return sample(schema, {
68
59
  skipReadOnly: method !== 'GET',
69
60
  skipWriteOnly: method === 'GET',
61
+ skipNonRequired: true,
70
62
  });
71
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/render/operation/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,YAAY,EAAkB,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EAEV,iBAAiB,EAEjB,aAAa,EAEd,MAAM,SAAS,CAAC;AAmBjB,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAK1D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,KAAK,CAAC;CAC3C;AASD,wBAAgB,SAAS,CAAC,EACxB,IAAkB,EAClB,IAAI,EACJ,MAAM,EACN,GAAG,EACH,OAAO,EACP,YAAgB,GACjB,EAAE;IACD,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,aAAa,CAAC;IAEnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,YAAY,CA6Kf"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/render/operation/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,YAAY,EAAkB,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EAEV,iBAAiB,EAEjB,aAAa,EAEd,MAAM,SAAS,CAAC;AAkBjB,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAW1D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,KAAK,CAAC;CAC3C;AASD,wBAAgB,SAAS,CAAC,EACxB,IAAkB,EAClB,IAAI,EACJ,MAAM,EACN,GAAG,EACH,OAAO,EACP,YAAgB,GACjB,EAAE;IACD,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,aAAa,CAAC;IAEnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,YAAY,CA6Kf"}
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Fragment } from 'react';
3
3
  import { getPreferredType, } from '../../utils/schema.js';
4
- import { getSecurities, getSecurityPrefix } from '../../utils/get-security.js';
5
4
  import { idToTitle } from '../../utils/id-to-title.js';
6
5
  import { Markdown } from '../markdown.js';
7
6
  import { heading } from '../heading.js';
@@ -10,7 +9,7 @@ import { createMethod } from '../../server/create-method.js';
10
9
  import { methodKeys } from '../../build-routes.js';
11
10
  import { APIExample, APIExampleProvider, getAPIExamples, } from '../../render/operation/api-example.js';
12
11
  import { Badge, MethodLabel } from '../../ui/components/method-label.js';
13
- import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
12
+ import { Tab, Tabs, TabsContent, TabsList, TabsTrigger, } from 'fumadocs-ui/components/tabs';
14
13
  import { getTypescriptSchema } from '../../utils/get-typescript-schema.js';
15
14
  import { CopyResponseTypeScript } from '../../ui/client.js';
16
15
  const ParamTypes = {
@@ -96,22 +95,23 @@ function WebhookCallback({ callback, ctx, headingLevel, }) {
96
95
  });
97
96
  }
98
97
  function AuthSection({ ctx: { schema: { document }, renderer, }, requirements, }) {
99
- let id = 0;
100
- const info = [];
101
- for (const requirement of requirements) {
102
- for (const schema of getSecurities(requirement, document)) {
103
- const prefix = getSecurityPrefix(schema);
104
- const scopeElement = schema.scopes.length > 0 ? (_jsxs("p", { children: ["Scope: ", _jsx("code", { children: schema.scopes.join(', ') })] })) : null;
105
- if (schema.type === 'http' || schema.type === 'oauth2') {
106
- info.push(_jsxs(renderer.Property, { name: "Authorization", type: prefix ? `${prefix} <token>` : '<token>', required: true, children: [schema.description ? _jsx(Markdown, { text: schema.description }) : null, _jsxs("p", { children: ["In: ", _jsx("code", { children: "header" })] }), scopeElement] }, id++));
107
- }
108
- if (schema.type === 'apiKey') {
109
- info.push(_jsxs(renderer.Property, { name: schema.name, type: "<token>", children: [schema.description ? _jsx(Markdown, { text: schema.description }) : null, _jsxs("p", { children: ["In: ", _jsx("code", { children: schema.in }), scopeElement] })] }, id++));
110
- }
111
- if (schema.type === 'openIdConnect') {
112
- info.push(_jsxs(renderer.Property, { name: "OpenID Connect", type: "<token>", required: true, children: [schema.description ? _jsx(Markdown, { text: schema.description }) : null, scopeElement] }, id++));
113
- }
114
- }
115
- }
116
- return info;
98
+ return (_jsxs(Tabs, { defaultValue: "0", children: [_jsx(TabsList, { children: requirements.map((securities, i) => (_jsx(TabsTrigger, { value: i.toString(), children: Object.keys(securities).join(' & ') }, i))) }), requirements.map((securities, i) => {
99
+ return (_jsx(TabsContent, { value: i.toString(), className: "flex flex-col gap-4", children: Object.entries(securities).map(([key, scopes]) => {
100
+ const schema = document.components?.securitySchemes?.[key];
101
+ if (!schema)
102
+ return;
103
+ const scopeElement = scopes.length > 0 ? (_jsxs("p", { children: ["Scope: ", _jsx("code", { children: scopes.join(', ') })] })) : null;
104
+ if (schema.type === 'http' || schema.type === 'oauth2') {
105
+ return (_jsxs(renderer.Property, { name: "Authorization", type: schema.type === 'http' && schema.scheme === 'basic'
106
+ ? `Basic <token>`
107
+ : 'Bearer <token>', required: true, children: [schema.description && (_jsx(Markdown, { text: schema.description })), _jsxs("p", { children: ["In: ", _jsx("code", { children: "header" })] }), scopeElement] }, key));
108
+ }
109
+ if (schema.type === 'apiKey') {
110
+ return (_jsxs(renderer.Property, { name: schema.name, type: "<token>", children: [schema.description && (_jsx(Markdown, { text: schema.description })), _jsxs("p", { children: ["In: ", _jsx("code", { children: schema.in }), scopeElement] })] }, key));
111
+ }
112
+ if (schema.type === 'openIdConnect') {
113
+ return (_jsxs(renderer.Property, { name: "OpenID Connect", type: "<token>", required: true, children: [schema.description && (_jsx(Markdown, { text: schema.description })), scopeElement] }, key));
114
+ }
115
+ }) }, i));
116
+ })] }));
117
117
  }
@@ -12,14 +12,8 @@ export declare function CodeExampleProvider({ route, examples, initialKey, child
12
12
  }): import("react/jsx-runtime").JSX.Element;
13
13
  export declare function CodeExample(props: CodeSample): import("react/jsx-runtime").JSX.Element | null;
14
14
  export declare function CodeExampleSelector({ items }: SamplesProps): import("react/jsx-runtime").JSX.Element;
15
- export declare function useRequestData(): {
16
- /**
17
- * initial request data
18
- */
19
- data: RequestData;
20
- /**
21
- * Save changes to request data, it won't trigger re-render on the component itself, which makes it safe to call in an effect with `data` as dep
22
- */
23
- saveData: (data: RequestData) => void;
15
+ export declare function useRequestInitialData(): RequestData;
16
+ export declare function useRequestDataUpdater(): {
17
+ setData: (data: RequestData) => void;
24
18
  };
25
19
  //# sourceMappingURL=code-example.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"code-example.d.ts","sourceRoot":"","sources":["../../../src/ui/contexts/code-example.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAUtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAqBtD,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,WAAW,CAAC;KACnB,EAAE,CAAC;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,2CAqDA;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,kDAwC5C;AAED,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,EAAE,YAAY,2CAoB1D;AAgBD,wBAAgB,cAAc;IAUxB;;OAEG;;IAEH;;OAEG;qBAxKS,WAAW,KAAK,IAAI;EA6KrC"}
1
+ {"version":3,"file":"code-example.d.ts","sourceRoot":"","sources":["../../../src/ui/contexts/code-example.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAUtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAqBtD,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,WAAW,CAAC;KACnB,EAAE,CAAC;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,2CAwDA;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,kDAwC5C;AAED,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,EAAE,YAAY,2CAoB1D;AAgBD,wBAAgB,qBAAqB,gBAOpC;AAED,wBAAgB,qBAAqB;oBApKnB,WAAW,KAAK,IAAI;EAuKrC"}
@@ -32,6 +32,9 @@ export function CodeExampleProvider({ route, examples, initialKey, children, })
32
32
  }
33
33
  });
34
34
  const addListener = useEffectEvent((listener) => {
35
+ // initial call to listeners to ensure their data is the latest
36
+ // this is necessary to avoid race conditions between `useEffect()`
37
+ listener(examples.find((example) => example.key === key).data);
35
38
  listeners.current.push(listener);
36
39
  });
37
40
  const removeListener = useEffectEvent((listener) => {
@@ -85,17 +88,11 @@ export function CodeExampleSelector({ items }) {
85
88
  function SelectDisplay({ item, ...props }) {
86
89
  return (_jsxs("div", { ...props, children: [_jsx("span", { className: "font-medium text-sm", children: item.title }), _jsx("span", { className: "text-fd-muted-foreground", children: item.description })] }));
87
90
  }
88
- export function useRequestData() {
89
- const { examples, key, setData } = useContext(CodeExampleContext);
90
- const data = useMemo(() => examples.find((example) => example.key === key).data, [examples, key]);
91
- return useMemo(() => ({
92
- /**
93
- * initial request data
94
- */
95
- data,
96
- /**
97
- * Save changes to request data, it won't trigger re-render on the component itself, which makes it safe to call in an effect with `data` as dep
98
- */
99
- saveData: setData,
100
- }), [data, setData]);
91
+ export function useRequestInitialData() {
92
+ const { examples, key } = useContext(CodeExampleContext);
93
+ return useMemo(() => examples.find((example) => example.key === key).data, [examples, key]);
94
+ }
95
+ export function useRequestDataUpdater() {
96
+ const { setData } = useContext(CodeExampleContext);
97
+ return { setData };
101
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "9.0.2",
3
+ "version": "9.0.3",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -61,10 +61,10 @@
61
61
  "remark": "^15.0.1",
62
62
  "remark-rehype": "^11.1.2",
63
63
  "shiki": "^3.4.2",
64
- "tinyglobby": "^0.2.13",
64
+ "tinyglobby": "^0.2.14",
65
65
  "xml-js": "^1.6.11",
66
- "fumadocs-core": "15.4.1",
67
- "fumadocs-ui": "15.4.1"
66
+ "fumadocs-core": "15.4.2",
67
+ "fumadocs-ui": "15.4.2"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@scalar/api-client-react": "^1.3.3",
@@ -1,8 +0,0 @@
1
- import type { Document, SecurityRequirementObject, SecuritySchemeObject } from '../types.js';
2
- import { type NoReference } from '../utils/schema.js';
3
- export type Security = SecuritySchemeObject & {
4
- scopes: string[];
5
- };
6
- export declare function getSecurities(requirement: NoReference<SecurityRequirementObject>, document: NoReference<Document>): Security[];
7
- export declare function getSecurityPrefix(security: Security): string | undefined;
8
- //# sourceMappingURL=get-security.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"get-security.d.ts","sourceRoot":"","sources":["../../src/utils/get-security.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,MAAM,QAAQ,GAAG,oBAAoB,GAAG;IAC5C,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,wBAAgB,aAAa,CAC3B,WAAW,EAAE,WAAW,CAAC,yBAAyB,CAAC,EACnD,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,GAC9B,QAAQ,EAAE,CAkBZ;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAQxE"}
@@ -1,23 +0,0 @@
1
- export function getSecurities(requirement, document) {
2
- const results = [];
3
- const schemas = document.components?.securitySchemes ?? {};
4
- for (const [key, scopes] of Object.entries(requirement)) {
5
- if (!(key in schemas))
6
- throw new Error(`Security scheme with name ${key} is not found, make sure to define referenced schemes.`);
7
- const schema = schemas[key];
8
- results.push({
9
- ...schema,
10
- scopes,
11
- });
12
- }
13
- return results;
14
- }
15
- export function getSecurityPrefix(security) {
16
- if (security.type === 'http')
17
- return {
18
- basic: 'Basic',
19
- bearer: 'Bearer',
20
- }[security.scheme];
21
- if (security.type === 'oauth2')
22
- return 'Bearer';
23
- }