fumadocs-openapi 10.6.5 → 10.6.6
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/css/generated/shared.css +32 -5
- package/dist/generate-file.js +3 -2
- package/dist/i18n.d.ts +1 -0
- package/dist/i18n.js +1 -0
- package/dist/playground/auth.js +92 -0
- package/dist/playground/client.d.ts +2 -1
- package/dist/playground/client.js +254 -180
- package/dist/playground/components/oauth-dialog.js +125 -162
- package/dist/playground/components/spinner.js +14 -0
- package/dist/requests/generators/csharp.js +2 -1
- package/dist/requests/generators/curl.js +5 -5
- package/dist/requests/generators/go.js +5 -5
- package/dist/requests/generators/java.js +4 -4
- package/dist/requests/generators/javascript.js +3 -3
- package/dist/requests/generators/python.js +5 -4
- package/dist/requests/media/adapter.js +7 -7
- package/dist/requests/string-utils.js +25 -4
- package/dist/requests/types.d.ts +3 -2
- package/dist/ui/api-page.js +3 -1
- package/dist/ui/base.d.ts +8 -4
- package/dist/ui/base.js +5 -0
- package/dist/ui/client/boundary.lazy.js +2 -1
- package/dist/ui/client/storage-key.js +1 -1
- package/dist/ui/contexts/api.d.ts +6 -3
- package/dist/ui/contexts/api.js +7 -2
- package/dist/ui/create-client.js +5 -0
- package/dist/ui/operation/request-tabs.d.ts +10 -0
- package/dist/ui/operation/request-tabs.js +43 -37
- package/dist/utils/pages/to-text.js +5 -4
- package/dist/utils/schema/index.d.ts +3 -4
- package/dist/utils/schema/index.js +4 -9
- package/dist/utils/use-query.js +2 -1
- package/package.json +2 -2
|
@@ -1,15 +1,26 @@
|
|
|
1
|
+
import { useApiContext } from "../../ui/contexts/api.js";
|
|
1
2
|
import { cn } from "../../utils/cn.js";
|
|
2
3
|
import { useQuery } from "../../utils/use-query.js";
|
|
3
4
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../ui/components/select.js";
|
|
4
5
|
import { Input, labelVariants } from "../../ui/components/input.js";
|
|
5
6
|
import { useTranslations } from "../../ui/client/i18n.js";
|
|
6
7
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "../../ui/components/dialog.js";
|
|
7
|
-
import {
|
|
8
|
+
import { useAuth } from "../auth.js";
|
|
9
|
+
import { useMemo, useState } from "react";
|
|
8
10
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
11
|
import { buttonVariants } from "fumadocs-ui/components/ui/button";
|
|
10
12
|
import { useForm } from "react-hook-form";
|
|
11
13
|
//#region src/playground/components/oauth-dialog.tsx
|
|
12
|
-
|
|
14
|
+
const OAuthDialog = Dialog;
|
|
15
|
+
function OAuthDialogContent(props) {
|
|
16
|
+
const t = useTranslations();
|
|
17
|
+
return /* @__PURE__ */ jsxs(DialogContent, { children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: t.authorization }), /* @__PURE__ */ jsx(DialogDescription, { children: t.obtainAccessToken })] }), /* @__PURE__ */ jsx(Content, { ...props })] });
|
|
18
|
+
}
|
|
19
|
+
function Content({ schemeId, scopes, setToken, setOpen }) {
|
|
20
|
+
const { schemes } = useApiContext();
|
|
21
|
+
const tokenInfo = useAuth().store[schemeId];
|
|
22
|
+
const scheme = schemes[schemeId];
|
|
23
|
+
if (!scheme || scheme.type !== "oauth2") throw new Error("unexpected schemaId: must be type oauth2");
|
|
13
24
|
const [type, setType] = useState(() => {
|
|
14
25
|
return Object.keys(scheme.flows)[0];
|
|
15
26
|
});
|
|
@@ -41,58 +52,14 @@ function OauthDialog({ scheme, scopes, setToken, children, open, setOpen }) {
|
|
|
41
52
|
supported: false
|
|
42
53
|
}
|
|
43
54
|
}), [t]);
|
|
44
|
-
const form = useForm({ defaultValues: {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const res = await fetch(value.tokenUrl, {
|
|
53
|
-
method: "POST",
|
|
54
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
55
|
-
body: new URLSearchParams({
|
|
56
|
-
grant_type: "authorization_code",
|
|
57
|
-
code,
|
|
58
|
-
redirect_uri: state.redirect_uri,
|
|
59
|
-
client_id: state.client_id,
|
|
60
|
-
client_secret: state.client_secret
|
|
61
|
-
})
|
|
62
|
-
});
|
|
63
|
-
if (!res.ok) throw new Error(await res.text());
|
|
64
|
-
const { access_token, token_type = "Bearer" } = await res.json();
|
|
65
|
-
setToken(`${token_type} ${access_token}`);
|
|
66
|
-
setOpen(false);
|
|
67
|
-
});
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
if (scheme.flows.authorizationCode) {
|
|
70
|
-
const params = new URLSearchParams(window.location.search);
|
|
71
|
-
const state = params.get("state");
|
|
72
|
-
const code = params.get("code");
|
|
73
|
-
if (state && code) {
|
|
74
|
-
const parsedState = JSON.parse(state);
|
|
75
|
-
setOpen(true);
|
|
76
|
-
form.setValue("clientId", parsedState.client_id);
|
|
77
|
-
form.setValue("clientSecret", parsedState.client_secret);
|
|
78
|
-
authCodeCallback.start(code, parsedState);
|
|
79
|
-
window.history.replaceState(null, "", window.location.pathname);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (scheme.flows.implicit && window.location.hash.length > 1) {
|
|
84
|
-
const params = new URLSearchParams(window.location.hash.slice(1));
|
|
85
|
-
const state = params.get("state");
|
|
86
|
-
const token = params.get("access_token");
|
|
87
|
-
const type = params.get("token_type") ?? "Bearer";
|
|
88
|
-
if (state && token) {
|
|
89
|
-
const parsedState = JSON.parse(state);
|
|
90
|
-
form.setValue("clientId", parsedState.client_id);
|
|
91
|
-
setToken(`${type} ${token}`);
|
|
92
|
-
window.history.replaceState(null, "", window.location.pathname);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}, []);
|
|
55
|
+
const form = useForm({ defaultValues: useMemo(() => {
|
|
56
|
+
return {
|
|
57
|
+
clientId: tokenInfo?.client_id ?? "",
|
|
58
|
+
clientSecret: tokenInfo?.type === "authorization_code" ? tokenInfo.client_secret : "",
|
|
59
|
+
username: "",
|
|
60
|
+
password: ""
|
|
61
|
+
};
|
|
62
|
+
}, [tokenInfo]) });
|
|
96
63
|
const authorize = useQuery(async (values) => {
|
|
97
64
|
if (type === "implicit") {
|
|
98
65
|
const value = scheme.flows[type];
|
|
@@ -102,6 +69,7 @@ function OauthDialog({ scheme, scopes, setToken, children, open, setOpen }) {
|
|
|
102
69
|
params.set("redirect_uri", window.location.href);
|
|
103
70
|
params.set("scope", scopes.join("+"));
|
|
104
71
|
params.set("state", JSON.stringify({
|
|
72
|
+
scheme: schemeId,
|
|
105
73
|
client_id: values.clientId,
|
|
106
74
|
redirect_uri: window.location.href
|
|
107
75
|
}));
|
|
@@ -118,7 +86,8 @@ function OauthDialog({ scheme, scopes, setToken, children, open, setOpen }) {
|
|
|
118
86
|
params.set("state", JSON.stringify({
|
|
119
87
|
client_id: values.clientId,
|
|
120
88
|
client_secret: values.clientSecret,
|
|
121
|
-
redirect_uri: window.location.href
|
|
89
|
+
redirect_uri: window.location.href,
|
|
90
|
+
scheme: schemeId
|
|
122
91
|
}));
|
|
123
92
|
window.location.replace(`${value.authorizationUrl}?${params.toString()}`);
|
|
124
93
|
return;
|
|
@@ -157,127 +126,121 @@ function OauthDialog({ scheme, scopes, setToken, children, open, setOpen }) {
|
|
|
157
126
|
setOpen(false);
|
|
158
127
|
}
|
|
159
128
|
});
|
|
129
|
+
const isLoading = authorize.isLoading;
|
|
160
130
|
const onSubmit = form.handleSubmit((values) => {
|
|
161
131
|
return authorize.start(values);
|
|
162
132
|
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
children:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
(type === "authorizationCode" || type === "clientCredentials" || type === "implicit") && /* @__PURE__ */ jsxs("fieldset", {
|
|
193
|
-
className: "flex flex-col gap-1.5",
|
|
194
|
-
children: [
|
|
195
|
-
/* @__PURE__ */ jsx("label", {
|
|
196
|
-
htmlFor: "client_id",
|
|
197
|
-
className: cn(labelVariants()),
|
|
198
|
-
children: t.clientId
|
|
199
|
-
}),
|
|
200
|
-
/* @__PURE__ */ jsx("p", {
|
|
201
|
-
className: "text-fd-muted-foreground text-sm",
|
|
202
|
-
children: t.clientIdHint
|
|
203
|
-
}),
|
|
204
|
-
/* @__PURE__ */ jsx(Input, {
|
|
205
|
-
id: "client_id",
|
|
206
|
-
placeholder: t.inputPlaceholder,
|
|
207
|
-
type: "text",
|
|
208
|
-
autoComplete: "off",
|
|
209
|
-
disabled: isLoading,
|
|
210
|
-
...form.register("clientId", { required: true })
|
|
211
|
-
})
|
|
212
|
-
]
|
|
213
|
-
}),
|
|
214
|
-
(type === "authorizationCode" || type === "clientCredentials") && /* @__PURE__ */ jsxs("fieldset", {
|
|
215
|
-
className: "flex flex-col gap-1.5",
|
|
216
|
-
children: [
|
|
217
|
-
/* @__PURE__ */ jsx("label", {
|
|
218
|
-
htmlFor: "client_secret",
|
|
219
|
-
className: cn(labelVariants()),
|
|
220
|
-
children: t.clientSecret
|
|
221
|
-
}),
|
|
222
|
-
/* @__PURE__ */ jsx("p", {
|
|
223
|
-
className: "text-fd-muted-foreground text-sm",
|
|
224
|
-
children: t.clientSecretHint
|
|
225
|
-
}),
|
|
226
|
-
/* @__PURE__ */ jsx(Input, {
|
|
227
|
-
id: "client_secret",
|
|
228
|
-
placeholder: t.inputPlaceholder,
|
|
229
|
-
type: "password",
|
|
230
|
-
autoComplete: "off",
|
|
231
|
-
disabled: isLoading,
|
|
232
|
-
...form.register("clientSecret", { required: true })
|
|
233
|
-
})
|
|
234
|
-
]
|
|
235
|
-
}),
|
|
236
|
-
type === "password" && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("fieldset", {
|
|
237
|
-
className: "flex flex-col gap-1.5",
|
|
238
|
-
children: [/* @__PURE__ */ jsx("label", {
|
|
239
|
-
htmlFor: "username",
|
|
133
|
+
return /* @__PURE__ */ jsxs("form", {
|
|
134
|
+
className: "flex flex-col gap-6",
|
|
135
|
+
onSubmit: (e) => {
|
|
136
|
+
onSubmit(e);
|
|
137
|
+
e.stopPropagation();
|
|
138
|
+
},
|
|
139
|
+
children: [
|
|
140
|
+
/* @__PURE__ */ jsxs(Select, {
|
|
141
|
+
value: type ?? "",
|
|
142
|
+
onValueChange: setType,
|
|
143
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a flow" }) }), /* @__PURE__ */ jsx(SelectContent, { children: Object.keys(scheme.flows).map((key) => {
|
|
144
|
+
const { name, description } = allFlows[key];
|
|
145
|
+
return /* @__PURE__ */ jsxs(SelectItem, {
|
|
146
|
+
value: key,
|
|
147
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
148
|
+
className: "font-medium",
|
|
149
|
+
children: name
|
|
150
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
151
|
+
className: "text-fd-muted-foreground",
|
|
152
|
+
children: description
|
|
153
|
+
})]
|
|
154
|
+
}, key);
|
|
155
|
+
}) })]
|
|
156
|
+
}),
|
|
157
|
+
(type === "authorizationCode" || type === "clientCredentials" || type === "implicit") && /* @__PURE__ */ jsxs("fieldset", {
|
|
158
|
+
className: "flex flex-col gap-1.5",
|
|
159
|
+
children: [
|
|
160
|
+
/* @__PURE__ */ jsx("label", {
|
|
161
|
+
htmlFor: "client_id",
|
|
240
162
|
className: cn(labelVariants()),
|
|
241
|
-
children: t.
|
|
242
|
-
}),
|
|
243
|
-
|
|
163
|
+
children: t.clientId
|
|
164
|
+
}),
|
|
165
|
+
/* @__PURE__ */ jsx("p", {
|
|
166
|
+
className: "text-fd-muted-foreground text-sm",
|
|
167
|
+
children: t.clientIdHint
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ jsx(Input, {
|
|
170
|
+
id: "client_id",
|
|
244
171
|
placeholder: t.inputPlaceholder,
|
|
245
172
|
type: "text",
|
|
246
|
-
disabled: isLoading,
|
|
247
173
|
autoComplete: "off",
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
174
|
+
disabled: isLoading,
|
|
175
|
+
...form.register("clientId", { required: true })
|
|
176
|
+
})
|
|
177
|
+
]
|
|
178
|
+
}),
|
|
179
|
+
(type === "authorizationCode" || type === "clientCredentials") && /* @__PURE__ */ jsxs("fieldset", {
|
|
180
|
+
className: "flex flex-col gap-1.5",
|
|
181
|
+
children: [
|
|
182
|
+
/* @__PURE__ */ jsx("label", {
|
|
183
|
+
htmlFor: "client_secret",
|
|
254
184
|
className: cn(labelVariants()),
|
|
255
185
|
children: t.clientSecret
|
|
256
|
-
}),
|
|
257
|
-
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ jsx("p", {
|
|
188
|
+
className: "text-fd-muted-foreground text-sm",
|
|
189
|
+
children: t.clientSecretHint
|
|
190
|
+
}),
|
|
191
|
+
/* @__PURE__ */ jsx(Input, {
|
|
192
|
+
id: "client_secret",
|
|
258
193
|
placeholder: t.inputPlaceholder,
|
|
259
194
|
type: "password",
|
|
260
195
|
autoComplete: "off",
|
|
261
196
|
disabled: isLoading,
|
|
262
|
-
...form.register("
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
197
|
+
...form.register("clientSecret", { required: true })
|
|
198
|
+
})
|
|
199
|
+
]
|
|
200
|
+
}),
|
|
201
|
+
type === "password" && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("fieldset", {
|
|
202
|
+
className: "flex flex-col gap-1.5",
|
|
203
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
204
|
+
htmlFor: "username",
|
|
205
|
+
className: cn(labelVariants()),
|
|
206
|
+
children: t.usernameField
|
|
207
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
208
|
+
id: "username",
|
|
209
|
+
placeholder: t.inputPlaceholder,
|
|
210
|
+
type: "text",
|
|
211
|
+
autoComplete: "off",
|
|
270
212
|
disabled: isLoading,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
213
|
+
...form.register("username", { required: true })
|
|
214
|
+
})]
|
|
215
|
+
}), /* @__PURE__ */ jsxs("fieldset", {
|
|
216
|
+
className: "flex flex-col gap-1.5",
|
|
217
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
218
|
+
htmlFor: "password",
|
|
219
|
+
className: cn(labelVariants()),
|
|
220
|
+
children: t.clientSecret
|
|
221
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
222
|
+
id: "password",
|
|
223
|
+
placeholder: t.inputPlaceholder,
|
|
224
|
+
type: "password",
|
|
225
|
+
autoComplete: "off",
|
|
226
|
+
disabled: isLoading,
|
|
227
|
+
...form.register("password", { required: true })
|
|
228
|
+
})]
|
|
229
|
+
})] }),
|
|
230
|
+
type && allFlows[type].supported ? /* @__PURE__ */ jsxs(Fragment$1, { children: [authorize.error ? /* @__PURE__ */ jsx("p", {
|
|
231
|
+
className: "text-red-400 font-medium text-sm",
|
|
232
|
+
children: String(authorize.error)
|
|
233
|
+
}) : null, /* @__PURE__ */ jsx("button", {
|
|
234
|
+
type: "submit",
|
|
235
|
+
className: cn(buttonVariants({ color: "primary" })),
|
|
236
|
+
children: t.submit
|
|
237
|
+
})] }) : /* @__PURE__ */ jsx("p", {
|
|
238
|
+
className: "text-fd-muted-foreground bg-fd-muted p-2 rounded-lg border",
|
|
239
|
+
children: t.unsupported
|
|
240
|
+
})
|
|
241
|
+
]
|
|
279
242
|
});
|
|
280
243
|
}
|
|
281
|
-
const
|
|
244
|
+
const OAuthDialogTrigger = DialogTrigger;
|
|
282
245
|
//#endregion
|
|
283
|
-
export {
|
|
246
|
+
export { OAuthDialog, OAuthDialogContent, OAuthDialogTrigger };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Loader2Icon } from "lucide-react";
|
|
4
|
+
//#region src/playground/components/spinner.tsx
|
|
5
|
+
function Spinner({ className, ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(Loader2Icon, {
|
|
7
|
+
role: "status",
|
|
8
|
+
"aria-label": "Loading",
|
|
9
|
+
className: cn("size-4 animate-spin", className),
|
|
10
|
+
...props
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Spinner };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { doubleQuote } from "../string-utils.js";
|
|
1
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
2
3
|
import "../media/adapter.js";
|
|
3
4
|
//#region src/requests/generators/csharp.ts
|
|
@@ -25,7 +26,7 @@ const csharp = {
|
|
|
25
26
|
s.push("var client = new HttpClient();");
|
|
26
27
|
const headerLines = [];
|
|
27
28
|
function addHeader(key, value) {
|
|
28
|
-
headerLines.push(`client.DefaultRequestHeaders.Add("${key}", ${
|
|
29
|
+
headerLines.push(`client.DefaultRequestHeaders.Add("${key}", ${doubleQuote(value)});`);
|
|
29
30
|
}
|
|
30
31
|
for (const k in headers) addHeader(k, headers[k].value);
|
|
31
32
|
if (Object.keys(data.cookie).length > 0) addHeader("cookie", Object.entries(data.cookie).map(([key, param]) => `${key}=${param.value}`).join("; "));
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { doubleQuote, indent, inputToString, singleQuote } from "../string-utils.js";
|
|
2
2
|
//#region src/requests/generators/curl.ts
|
|
3
3
|
const curl = {
|
|
4
4
|
label: "cURL",
|
|
5
5
|
lang: "bash",
|
|
6
6
|
generate(url, data) {
|
|
7
7
|
const s = [];
|
|
8
|
-
s.push(`curl -X ${data.method} "${url}"`);
|
|
8
|
+
s.push(`curl -X ${data.method.toUpperCase()} "${url}"`);
|
|
9
9
|
for (const header in data.header) {
|
|
10
10
|
const value = `${header}: ${data.header[header].value}`;
|
|
11
11
|
s.push(`-H "${value}"`);
|
|
12
12
|
}
|
|
13
13
|
for (const k in data.cookie) {
|
|
14
14
|
const param = data.cookie[k];
|
|
15
|
-
s.push(`--cookie ${
|
|
15
|
+
s.push(`--cookie ${doubleQuote(`${k}=${param.value}`)}`);
|
|
16
16
|
}
|
|
17
17
|
if (data.body && data.bodyMediaType === "multipart/form-data") {
|
|
18
18
|
if (typeof data.body !== "object") throw new Error("[CURL] request body must be an object.");
|
|
19
|
-
for (const [key, value] of Object.entries(data.body)) s.push(`-F ${key}=${
|
|
19
|
+
for (const [key, value] of Object.entries(data.body)) s.push(`-F ${key}=${doubleQuote(inputToString(value))}`);
|
|
20
20
|
} else if (data.body && data.bodyMediaType) {
|
|
21
|
-
const escaped =
|
|
21
|
+
const escaped = singleQuote(inputToString(data.body, data.bodyMediaType));
|
|
22
22
|
s.push(`-H "Content-Type: ${data.bodyMediaType}"`);
|
|
23
23
|
s.push(`-d ${escaped}`);
|
|
24
24
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "../string-utils.js";
|
|
1
|
+
import { doubleQuote, indent } from "../string-utils.js";
|
|
2
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
3
3
|
import "../media/adapter.js";
|
|
4
4
|
//#region src/requests/generators/go.ts
|
|
@@ -13,10 +13,10 @@ const go = {
|
|
|
13
13
|
];
|
|
14
14
|
const headers = /* @__PURE__ */ new Map();
|
|
15
15
|
const variables = /* @__PURE__ */ new Map();
|
|
16
|
-
variables.set("url",
|
|
17
|
-
for (const header in data.header) headers.set(header,
|
|
16
|
+
variables.set("url", doubleQuote(url));
|
|
17
|
+
for (const header in data.header) headers.set(header, doubleQuote(data.header[header].value));
|
|
18
18
|
const cookies = Object.entries(data.cookie);
|
|
19
|
-
if (cookies.length > 0) headers.set("Cookie",
|
|
19
|
+
if (cookies.length > 0) headers.set("Cookie", doubleQuote(cookies.map(([k, param]) => `${k}=${param.value}`).join("; ")));
|
|
20
20
|
let body;
|
|
21
21
|
if (data.body && data.bodyMediaType) {
|
|
22
22
|
const adapter = resolveMediaAdapter(data.bodyMediaType, mediaAdapters);
|
|
@@ -37,7 +37,7 @@ ${indent(imports.map((v) => `"${v}"`).join("\n"))}
|
|
|
37
37
|
func main() {
|
|
38
38
|
${Array.from(variables.entries()).map(([k, v]) => indent(`${k} := ${v}`)).join("\n")}
|
|
39
39
|
${body ? indent(body) : ""}
|
|
40
|
-
req, _ := http.NewRequest("${data.method}", url, ${body ? "body" : "nil"})
|
|
40
|
+
req, _ := http.NewRequest("${data.method.toUpperCase()}", url, ${body ? "body" : "nil"})
|
|
41
41
|
${indent(Array.from(headers.entries()).map(([key, value]) => `req.Header.Add("${key}", ${value})`).join("\n"))}
|
|
42
42
|
res, _ := http.DefaultClient.Do(req)
|
|
43
43
|
defer res.Body.Close()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "../string-utils.js";
|
|
1
|
+
import { doubleQuote, indent } from "../string-utils.js";
|
|
2
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
3
3
|
import "../media/adapter.js";
|
|
4
4
|
//#region src/requests/generators/java.ts
|
|
@@ -31,13 +31,13 @@ const java = {
|
|
|
31
31
|
s.push(indent(".build();"));
|
|
32
32
|
s.push("");
|
|
33
33
|
s.push("HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()");
|
|
34
|
-
s.push(indent(`.uri(URI.create(${
|
|
35
|
-
for (const [key, param] of Object.entries(headers)) s.push(indent(`.header(${
|
|
34
|
+
s.push(indent(`.uri(URI.create(${doubleQuote(url)}))`));
|
|
35
|
+
for (const [key, param] of Object.entries(headers)) s.push(indent(`.header(${doubleQuote(key)}, ${doubleQuote(param.value)})`));
|
|
36
36
|
if (data.bodyMediaType) s.push(indent(`.header("Content-Type", "${data.bodyMediaType}")`));
|
|
37
37
|
const cookies = Object.entries(data.cookie);
|
|
38
38
|
if (cookies.length > 0) {
|
|
39
39
|
const cookieString = cookies.map(([key, param]) => `${key}=${param.value}`).join("; ");
|
|
40
|
-
s.push(indent(`.header("Cookie", ${
|
|
40
|
+
s.push(indent(`.header("Cookie", ${doubleQuote(cookieString)})`));
|
|
41
41
|
}
|
|
42
42
|
const arg = body ? "body" : "";
|
|
43
43
|
s.push(indent(`.${data.method.toUpperCase()}(${arg})`));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "../string-utils.js";
|
|
1
|
+
import { doubleQuote, indent } from "../string-utils.js";
|
|
2
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
3
3
|
import "../media/adapter.js";
|
|
4
4
|
//#region src/requests/generators/javascript.ts
|
|
@@ -9,7 +9,7 @@ const javascript = {
|
|
|
9
9
|
const s = [];
|
|
10
10
|
const options = /* @__PURE__ */ new Map();
|
|
11
11
|
const headers = {};
|
|
12
|
-
options.set("method",
|
|
12
|
+
options.set("method", `"${data.method.toUpperCase()}"`);
|
|
13
13
|
if (data.bodyMediaType) headers["Content-Type"] = data.bodyMediaType;
|
|
14
14
|
for (const [k, v] of Object.entries(data.header)) headers[k] = v.value;
|
|
15
15
|
const cookies = Object.entries(data.cookie);
|
|
@@ -26,7 +26,7 @@ const javascript = {
|
|
|
26
26
|
s.push(body);
|
|
27
27
|
options.set("body", "body");
|
|
28
28
|
}
|
|
29
|
-
const params = [
|
|
29
|
+
const params = [doubleQuote(url)];
|
|
30
30
|
if (options.size > 0) {
|
|
31
31
|
const str = Array.from(options.entries()).map(([k, v]) => indent(k === v ? k : `${k}: ${v}`)).join(",\n");
|
|
32
32
|
params.push(`{\n${str}\n}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { doubleQuote } from "../string-utils.js";
|
|
1
2
|
import { resolveMediaAdapter } from "../media/resolve-adapter.js";
|
|
2
3
|
import "../media/adapter.js";
|
|
3
4
|
//#region src/requests/generators/python.ts
|
|
@@ -7,7 +8,7 @@ const python = {
|
|
|
7
8
|
generate(url, data, { mediaAdapters }) {
|
|
8
9
|
const headers = {};
|
|
9
10
|
const imports = /* @__PURE__ */ new Set();
|
|
10
|
-
const params = [`"${data.method}"`, "url"];
|
|
11
|
+
const params = [`"${data.method.toUpperCase()}"`, "url"];
|
|
11
12
|
let body;
|
|
12
13
|
imports.add("requests");
|
|
13
14
|
if (data.body && data.bodyMediaType) {
|
|
@@ -26,7 +27,7 @@ const python = {
|
|
|
26
27
|
}
|
|
27
28
|
return `${Array.from(imports).map((name) => "import " + name).join("\n")}
|
|
28
29
|
|
|
29
|
-
url = ${
|
|
30
|
+
url = ${doubleQuote(url)}
|
|
30
31
|
${body ?? ""}
|
|
31
32
|
response = requests.request(${params.join(", ")})
|
|
32
33
|
|
|
@@ -36,13 +37,13 @@ print(response.text)`;
|
|
|
36
37
|
function generatePythonObject(v, imports) {
|
|
37
38
|
if (v === null) return "None";
|
|
38
39
|
else if (typeof v === "boolean") return v ? "True" : "False";
|
|
39
|
-
else if (typeof v === "string") return
|
|
40
|
+
else if (typeof v === "string") return doubleQuote(v);
|
|
40
41
|
else if (typeof v === "number") return v.toString();
|
|
41
42
|
else if (Array.isArray(v)) return `[${v.map((item) => generatePythonObject(item, imports)).join(", ")}]`;
|
|
42
43
|
else if (v instanceof Date) {
|
|
43
44
|
imports.add("datetime");
|
|
44
45
|
return `datetime.datetime(${v.getFullYear()}, ${v.getMonth() + 1}, ${v.getDate()}, ${v.getHours()}, ${v.getMinutes()}, ${v.getSeconds()}, ${v.getMilliseconds()})`;
|
|
45
|
-
} else if (typeof v === "object") return `{\n${Object.entries(v).map(([key, value]) => ` ${
|
|
46
|
+
} else if (typeof v === "object") return `{\n${Object.entries(v).map(([key, value]) => ` ${doubleQuote(key)}: ${generatePythonObject(value, imports)}`).join(", \n")}\n}`;
|
|
46
47
|
else throw new Error(`Unsupported type: ${typeof v}`);
|
|
47
48
|
}
|
|
48
49
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { backtickQuote, inputToString, tripleDoubleQuote } from "../string-utils.js";
|
|
2
2
|
import "./resolve-adapter.js";
|
|
3
3
|
import js2xml from "xml-js/lib/js2xml.js";
|
|
4
4
|
//#region src/requests/media/adapter.ts
|
|
@@ -80,7 +80,7 @@ const defaultAdapters = {
|
|
|
80
80
|
s.push("mp := multipart.NewWriter(payload)");
|
|
81
81
|
for (const [key, value] of Object.entries(data.body)) {
|
|
82
82
|
if (!value) continue;
|
|
83
|
-
const escaped =
|
|
83
|
+
const escaped = backtickQuote(inputToString(value, "application/json"));
|
|
84
84
|
s.push(`mp.WriteField("${key}", ${escaped})`);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -103,20 +103,20 @@ const defaultAdapters = {
|
|
|
103
103
|
function str(init, mediaType, ctx) {
|
|
104
104
|
if (ctx.lang === "js") {
|
|
105
105
|
if (mediaType === "application/json") return `const body = JSON.stringify(${JSON.stringify(init, null, 2)})`;
|
|
106
|
-
return `const body = ${
|
|
106
|
+
return `const body = ${backtickQuote(inputToString(init, mediaType))}`;
|
|
107
107
|
}
|
|
108
|
-
if (ctx.lang === "python") return `body = ${
|
|
108
|
+
if (ctx.lang === "python") return `body = ${tripleDoubleQuote(inputToString(init, mediaType))}`;
|
|
109
109
|
if (ctx.lang === "go") {
|
|
110
110
|
const { addImport } = ctx;
|
|
111
111
|
addImport("strings");
|
|
112
|
-
return `body := strings.NewReader(${
|
|
112
|
+
return `body := strings.NewReader(${backtickQuote(inputToString(init, mediaType))})`;
|
|
113
113
|
}
|
|
114
114
|
if (ctx.lang === "java") {
|
|
115
115
|
const { addImport } = ctx;
|
|
116
116
|
addImport("java.net.http.HttpRequest.BodyPublishers");
|
|
117
|
-
return `var body = BodyPublishers.ofString(${
|
|
117
|
+
return `var body = BodyPublishers.ofString(${tripleDoubleQuote(inputToString(init, mediaType))});`;
|
|
118
118
|
}
|
|
119
|
-
if (ctx.lang === "csharp") return `var body = new StringContent(${
|
|
119
|
+
if (ctx.lang === "csharp") return `var body = new StringContent(${tripleDoubleQuote(`\n${inputToString(init, mediaType)}\n`)}, Encoding.UTF8, "${mediaType}");`;
|
|
120
120
|
}
|
|
121
121
|
//#endregion
|
|
122
122
|
export { defaultAdapters };
|
|
@@ -18,13 +18,34 @@ function inputToString(value, format = "application/json") {
|
|
|
18
18
|
spaces: 2
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Returns the input string wrapped in single quotes, escaping internal single quotes and backslashes.
|
|
23
|
+
*/
|
|
24
|
+
function singleQuote(str) {
|
|
25
|
+
return `'${str.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns the input string wrapped in double quotes, escaping internal double quotes and backslashes.
|
|
29
|
+
*/
|
|
30
|
+
function doubleQuote(str) {
|
|
31
|
+
return `"${str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Returns the input string wrapped in Python triple double-quotes,
|
|
35
|
+
* escaping embedded triple quotes and backslashes.
|
|
36
|
+
*/
|
|
37
|
+
function tripleDoubleQuote(str) {
|
|
38
|
+
return `"""${str.replace(/\\/g, "\\\\").replace(/"""/g, "\\\"\\\"\\\"")}"""`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the input string wrapped in backticks, escaping internal backticks and backslashes.
|
|
42
|
+
*/
|
|
43
|
+
function backtickQuote(str) {
|
|
44
|
+
return `\`${str.replace(/\\/g, "\\\\").replace(/`/g, "\\`")}\``;
|
|
24
45
|
}
|
|
25
46
|
function indent(code, tab = 1) {
|
|
26
47
|
const p = " ".repeat(tab);
|
|
27
48
|
return code.split("\n").map((v) => v.length === 0 ? v : p + v).join("\n");
|
|
28
49
|
}
|
|
29
50
|
//#endregion
|
|
30
|
-
export {
|
|
51
|
+
export { backtickQuote, doubleQuote, indent, inputToString, singleQuote, tripleDoubleQuote };
|
package/dist/requests/types.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { EncodedParameter, EncodedParameterMultiple } from "./media/encode.js";
|
|
2
|
+
import { HttpMethods } from "../types.js";
|
|
2
3
|
|
|
3
4
|
//#region src/requests/types.d.ts
|
|
4
5
|
interface RawRequestData {
|
|
5
|
-
method:
|
|
6
|
+
method: HttpMethods;
|
|
6
7
|
path: Record<string, unknown>;
|
|
7
8
|
query: Record<string, unknown>;
|
|
8
9
|
header: Record<string, unknown>;
|
|
@@ -11,7 +12,7 @@ interface RawRequestData {
|
|
|
11
12
|
bodyMediaType?: string;
|
|
12
13
|
}
|
|
13
14
|
interface RequestData {
|
|
14
|
-
method:
|
|
15
|
+
method: HttpMethods;
|
|
15
16
|
path: Record<string, EncodedParameter>;
|
|
16
17
|
query: Record<string, EncodedParameterMultiple>;
|
|
17
18
|
header: Record<string, EncodedParameter>;
|
package/dist/ui/api-page.js
CHANGED
|
@@ -9,7 +9,7 @@ function APIPage({ showTitle: hasHead = false, showDescription, operations, webh
|
|
|
9
9
|
className: "flex flex-col gap-24 text-sm @container",
|
|
10
10
|
children: [slots.operations?.map((op) => op.children), slots.webhooks?.map((op) => op.children)]
|
|
11
11
|
});
|
|
12
|
-
|
|
12
|
+
let content = renderPageLayout({
|
|
13
13
|
operations: operations?.map((item) => {
|
|
14
14
|
const pathItem = dereferenced.paths?.[item.path];
|
|
15
15
|
if (!pathItem) throw new Error(`[Fumadocs OpenAPI] Path not found in OpenAPI schema: ${item.path}`);
|
|
@@ -44,7 +44,9 @@ function APIPage({ showTitle: hasHead = false, showDescription, operations, webh
|
|
|
44
44
|
};
|
|
45
45
|
})
|
|
46
46
|
}, ctx);
|
|
47
|
+
if (ctx.playground?.enabled !== false && ctx.playground?.provider) content = ctx.playground.provider({ children: content });
|
|
47
48
|
return /* @__PURE__ */ jsx(ctx.clientBoundary.ApiProvider, {
|
|
49
|
+
schemes: dereferenced.components?.securitySchemes ?? {},
|
|
48
50
|
shikiOptions: ctx.shikiOptions,
|
|
49
51
|
client: ctx.client ?? {},
|
|
50
52
|
children: /* @__PURE__ */ jsx(ctx.clientBoundary.ServerProvider, {
|