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.
@@ -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 { useEffect, useMemo, useState } from "react";
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
- function OauthDialog({ scheme, scopes, setToken, children, open, setOpen }) {
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
- clientId: "",
46
- clientSecret: "",
47
- username: "",
48
- password: ""
49
- } });
50
- const authCodeCallback = useQuery(async (code, state) => {
51
- const value = scheme.flows.authorizationCode;
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
- const isLoading = authorize.isLoading || authCodeCallback.isLoading;
164
- const error = authCodeCallback.error ?? authorize.error;
165
- return /* @__PURE__ */ jsxs(Dialog, {
166
- open,
167
- onOpenChange: setOpen,
168
- children: [children, /* @__PURE__ */ jsxs(DialogContent, { children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: t.authorization }), /* @__PURE__ */ jsx(DialogDescription, { children: t.obtainAccessToken })] }), /* @__PURE__ */ jsxs("form", {
169
- className: "flex flex-col gap-6",
170
- onSubmit: (e) => {
171
- onSubmit(e);
172
- e.stopPropagation();
173
- },
174
- children: [
175
- /* @__PURE__ */ jsxs(Select, {
176
- value: type,
177
- onValueChange: setType,
178
- children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }), /* @__PURE__ */ jsx(SelectContent, { children: Object.keys(scheme.flows).map((key) => {
179
- const { name, description } = allFlows[key];
180
- return /* @__PURE__ */ jsxs(SelectItem, {
181
- value: key,
182
- children: [/* @__PURE__ */ jsx("p", {
183
- className: "font-medium",
184
- children: name
185
- }), /* @__PURE__ */ jsx("p", {
186
- className: "text-fd-muted-foreground",
187
- children: description
188
- })]
189
- }, key);
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.usernameField
242
- }), /* @__PURE__ */ jsx(Input, {
243
- id: "username",
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
- ...form.register("username", { required: true })
249
- })]
250
- }), /* @__PURE__ */ jsxs("fieldset", {
251
- className: "flex flex-col gap-1.5",
252
- children: [/* @__PURE__ */ jsx("label", {
253
- htmlFor: "password",
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
- }), /* @__PURE__ */ jsx(Input, {
257
- id: "password",
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("password", { required: true })
263
- })]
264
- })] }),
265
- allFlows[type].supported ? /* @__PURE__ */ jsxs(Fragment$1, { children: [error ? /* @__PURE__ */ jsx("p", {
266
- className: "text-red-400 font-medium text-sm",
267
- children: String(error)
268
- }) : null, /* @__PURE__ */ jsx("button", {
269
- type: "submit",
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
- className: cn(buttonVariants({ color: "primary" })),
272
- children: authCodeCallback.isLoading ? t.fetchingToken : t.submit
273
- })] }) : /* @__PURE__ */ jsx("p", {
274
- className: "text-fd-muted-foreground bg-fd-muted p-2 rounded-lg border",
275
- children: t.unsupported
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 OauthDialogTrigger = DialogTrigger;
244
+ const OAuthDialogTrigger = DialogTrigger;
282
245
  //#endregion
283
- export { OauthDialog, OauthDialogTrigger };
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}", ${JSON.stringify(value)});`);
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 { escapeString, indent, inputToString } from "../string-utils.js";
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 ${JSON.stringify(`${k}=${param.value}`)}`);
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}=${JSON.stringify(inputToString(value))}`);
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 = escapeString(inputToString(data.body, data.bodyMediaType), "'");
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", JSON.stringify(url));
17
- for (const header in data.header) headers.set(header, JSON.stringify(data.header[header].value));
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", JSON.stringify(cookies.map(([k, param]) => `${k}=${param.value}`).join("; ")));
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(${JSON.stringify(url)}))`));
35
- for (const [key, param] of Object.entries(headers)) s.push(indent(`.header(${JSON.stringify(key)}, ${JSON.stringify(param.value)})`));
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", ${JSON.stringify(cookieString)})`));
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", JSON.stringify(data.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 = [JSON.stringify(url)];
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 = ${JSON.stringify(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 JSON.stringify(v);
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]) => ` ${JSON.stringify(key)}: ${generatePythonObject(value, imports)}`).join(", \n")}\n}`;
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 { escapeString, inputToString } from "../string-utils.js";
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 = escapeString(inputToString(value, "application/json"), "`");
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 = ${escapeString(inputToString(init, mediaType), "`")}`;
106
+ return `const body = ${backtickQuote(inputToString(init, mediaType))}`;
107
107
  }
108
- if (ctx.lang === "python") return `body = ${escapeString(inputToString(init, mediaType), "\"\"\"")}`;
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(${escapeString(inputToString(init, mediaType), "`")})`;
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(${escapeString(inputToString(init, mediaType), "\"\"\"")});`;
117
+ return `var body = BodyPublishers.ofString(${tripleDoubleQuote(inputToString(init, mediaType))});`;
118
118
  }
119
- if (ctx.lang === "csharp") return `var body = new StringContent(${escapeString(`\n${inputToString(init, mediaType)}\n`, "\"\"\"")}, Encoding.UTF8, "${mediaType}");`;
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
- function escapeString(str, delimit) {
22
- if (!delimit) return JSON.stringify(str);
23
- return `${delimit}${str.replaceAll(delimit, `\\${delimit}`)}${delimit}`;
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 { escapeString, indent, inputToString };
51
+ export { backtickQuote, doubleQuote, indent, inputToString, singleQuote, tripleDoubleQuote };
@@ -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: string;
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: string;
15
+ method: HttpMethods;
15
16
  path: Record<string, EncodedParameter>;
16
17
  query: Record<string, EncodedParameterMultiple>;
17
18
  header: Record<string, EncodedParameter>;
@@ -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
- const content = renderPageLayout({
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, {