fumadocs-openapi 10.6.5 → 10.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/css/generated/shared.css +59 -6
  2. package/dist/generate-file.js +3 -2
  3. package/dist/i18n.d.ts +3 -1
  4. package/dist/i18n.js +3 -1
  5. package/dist/playground/auth.js +92 -0
  6. package/dist/playground/client.d.ts +5 -12
  7. package/dist/playground/client.js +257 -217
  8. package/dist/playground/components/inputs.js +2 -2
  9. package/dist/playground/components/oauth-dialog.js +126 -163
  10. package/dist/playground/components/result-display.d.ts +16 -0
  11. package/dist/playground/components/result-display.js +141 -0
  12. package/dist/playground/components/server-select.js +1 -1
  13. package/dist/playground/components/spinner.js +14 -0
  14. package/dist/playground/fetcher.d.ts +9 -3
  15. package/dist/playground/fetcher.js +7 -18
  16. package/dist/playground/status-info.js +27 -23
  17. package/dist/requests/generators/csharp.js +2 -1
  18. package/dist/requests/generators/curl.js +5 -5
  19. package/dist/requests/generators/go.js +5 -5
  20. package/dist/requests/generators/java.js +4 -4
  21. package/dist/requests/generators/javascript.js +3 -3
  22. package/dist/requests/generators/python.js +5 -4
  23. package/dist/requests/media/adapter.js +7 -7
  24. package/dist/requests/string-utils.js +25 -4
  25. package/dist/requests/types.d.ts +3 -2
  26. package/dist/ui/api-page.js +3 -1
  27. package/dist/ui/base.d.ts +8 -4
  28. package/dist/ui/base.js +6 -1
  29. package/dist/ui/client/boundary.lazy.js +2 -1
  30. package/dist/ui/client/i18n.js +6 -4
  31. package/dist/ui/client/storage-key.js +1 -1
  32. package/dist/ui/components/dialog.js +1 -1
  33. package/dist/ui/contexts/api.d.ts +6 -3
  34. package/dist/ui/contexts/api.js +7 -2
  35. package/dist/ui/create-client.js +5 -0
  36. package/dist/ui/operation/client.js +1 -1
  37. package/dist/ui/operation/index.js +1 -1
  38. package/dist/ui/operation/request-tabs.d.ts +10 -0
  39. package/dist/ui/operation/request-tabs.js +44 -38
  40. package/dist/ui/operation/usage-tabs/client.js +1 -1
  41. package/dist/ui/schema/client.js +2 -2
  42. package/dist/ui/schema/index.js +1 -1
  43. package/dist/utils/pages/to-text.js +5 -4
  44. package/dist/utils/schema/index.d.ts +3 -4
  45. package/dist/utils/schema/index.js +4 -9
  46. package/dist/utils/use-query.js +2 -1
  47. package/package.json +11 -10
@@ -3,8 +3,9 @@ import { joinURL, resolveRequestData, resolveServerUrl, withBase } from "../util
3
3
  import { getPreferredType } from "../utils/schema/index.js";
4
4
  import { useStorageKey } from "../ui/client/storage-key.js";
5
5
  import { useApiContext, useServerContext } from "../ui/contexts/api.js";
6
- import { getStatusInfo } from "./status-info.js";
6
+ import { useTranslations } from "../ui/client/i18n.js";
7
7
  import { cn } from "../utils/cn.js";
8
+ import { DefaultResultDisplay } from "./components/result-display.js";
8
9
  import { MethodLabel } from "../ui/components/method-label.js";
9
10
  import { useQuery } from "../utils/use-query.js";
10
11
  import { encodeRequestData } from "../requests/media/encode.js";
@@ -12,19 +13,20 @@ import { dereferenceSwallow } from "../utils/schema/dereference.js";
12
13
  import { SchemaProvider, anyFields, useResolvedSchema } from "./schema.js";
13
14
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/components/select.js";
14
15
  import { labelVariants } from "../ui/components/input.js";
15
- import { useTranslations } from "../ui/client/i18n.js";
16
16
  import ServerSelect from "./components/server-select.js";
17
17
  import { FieldInput, FieldSet, JsonInput, ObjectInput } from "./components/inputs.js";
18
- import { ClientCodeBlock } from "../ui/components/codeblock.js";
19
18
  import { useOperationContext } from "../ui/operation/client.js";
20
- import { OauthDialog, OauthDialogTrigger } from "./components/oauth-dialog.js";
19
+ import { useAuth } from "./auth.js";
20
+ import { OAuthDialog, OAuthDialogContent, OAuthDialogTrigger } from "./components/oauth-dialog.js";
21
+ import { Spinner } from "./components/spinner.js";
21
22
  import { Fragment, useEffect, useMemo, useRef, useState } from "react";
22
23
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
23
24
  import { ChevronDown, LoaderCircle } from "lucide-react";
24
- import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "fumadocs-ui/components/ui/collapsible";
25
25
  import { buttonVariants } from "fumadocs-ui/components/ui/button";
26
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "fumadocs-ui/components/ui/collapsible";
26
27
  import { StfProvider, useDataEngine, useFieldValue, useListener, useStf } from "@fumari/stf";
27
- import { objectGet, objectSet, stringifyFieldKey } from "@fumari/stf/lib/utils";
28
+ import { arrayStartsWith, objectGet, objectSet, stringifyFieldKey } from "@fumari/stf/lib/utils";
29
+ import { useOnChange } from "fumadocs-core/utils/use-on-change";
28
30
  //#region src/playground/client.tsx
29
31
  function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly, readOnly, ...rest }) {
30
32
  const t = useTranslations();
@@ -51,13 +53,7 @@ function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly,
51
53
  ]);
52
54
  const { example: exampleId, examples, setExampleData } = useOperationContext();
53
55
  const { server } = useServerContext();
54
- const storageKeys = useStorageKey();
55
- const { mediaAdapters, client: { playground: { components: { ResultDisplay = DefaultResultDisplay, CollapsiblePanel = DefaultCollapsiblePanel } = {}, requestTimeout, fetchOptions = { requestTimeout }, transformAuthInputs, renderBodyField } = {} } } = useApiContext();
56
- const [securityId, setSecurityId] = useState(() => {
57
- const idx = securities.findIndex((s) => s.every((entry) => !entry.deprecated));
58
- return idx === -1 ? 0 : idx;
59
- });
60
- const { inputs, mapInputs, initAuthValues } = useAuthInputs(securities[securityId], transformAuthInputs);
56
+ const { mediaAdapters, client: { playground: { components: { ResultDisplay = DefaultResultDisplay, CollapsiblePanel = DefaultCollapsiblePanel } = {}, requestTimeout, fetchOptions = { requestTimeout }, renderBodyField } = {} } } = useApiContext();
61
57
  const defaultValues = useMemo(() => {
62
58
  const requestData = examples.find((example) => example.id === exampleId)?.data;
63
59
  return {
@@ -69,6 +65,7 @@ function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly,
69
65
  };
70
66
  }, [examples, exampleId]);
71
67
  const stf = useStf({ defaultValues });
68
+ const { inputs, requirementId, setRequirementId, mapInputs, initAuthInputs } = useAuthInputs(stf.dataEngine, securities);
72
69
  const testQuery = useQuery(async (input) => {
73
70
  const fetcher = await import("./fetcher.js").then((mod) => mod.createBrowserFetcher(mediaAdapters, {
74
71
  proxyUrl,
@@ -82,23 +79,21 @@ function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly,
82
79
  return fetcher.fetch(joinURL(withBase(server ? resolveServerUrl(server.url, server.variables) : "/", window.location.origin), resolveRequestData(route, encoded)), encoded);
83
80
  });
84
81
  const timerRef = useRef(null);
82
+ const stfSync = useRef(false);
83
+ function triggerExampleUpdate() {
84
+ const data = {
85
+ ...mapInputs(stf.dataEngine.getData()),
86
+ method,
87
+ bodyMediaType: body?.mediaType
88
+ };
89
+ setExampleData(data, encodeRequestData(data, mediaAdapters, parameters));
90
+ }
85
91
  useListener({
86
92
  stf,
87
93
  onUpdate() {
94
+ if (!stfSync.current) return;
88
95
  if (timerRef.current) window.clearTimeout(timerRef.current);
89
- timerRef.current = window.setTimeout(() => {
90
- const values = stf.dataEngine.getData();
91
- for (const item of inputs) {
92
- const value = stf.dataEngine.get(item.fieldName);
93
- if (value) localStorage.setItem(storageKeys.AuthField(item), JSON.stringify(value));
94
- }
95
- const data = {
96
- ...mapInputs(values),
97
- method,
98
- bodyMediaType: body?.mediaType
99
- };
100
- setExampleData(data, encodeRequestData(data, mediaAdapters, parameters));
101
- }, timerRef.current ? 400 : 0);
96
+ timerRef.current = window.setTimeout(triggerExampleUpdate, 400);
102
97
  }
103
98
  });
104
99
  useEffect(() => {
@@ -106,7 +101,13 @@ function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly,
106
101
  stf.dataEngine.reset(defaultValues);
107
102
  }, [defaultValues]);
108
103
  useEffect(() => {
109
- return initAuthValues(stf);
104
+ const reset = initAuthInputs();
105
+ triggerExampleUpdate();
106
+ stfSync.current = true;
107
+ return () => {
108
+ stfSync.current = false;
109
+ reset();
110
+ };
110
111
  }, [defaultValues, inputs]);
111
112
  return /* @__PURE__ */ jsx(StfProvider, {
112
113
  value: stf,
@@ -118,7 +119,7 @@ function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly,
118
119
  ...rest,
119
120
  className: cn("not-prose flex flex-col rounded-xl border shadow-md overflow-hidden bg-fd-card text-fd-card-foreground", rest.className),
120
121
  onSubmit: (e) => {
121
- testQuery.start(mapInputs(stf.dataEngine.getData()));
122
+ testQuery.start(stf.dataEngine.getData());
122
123
  e.preventDefault();
123
124
  },
124
125
  children: [
@@ -146,10 +147,10 @@ function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly,
146
147
  data: testQuery.data,
147
148
  reset: testQuery.reset
148
149
  }) : null,
149
- securities.length > 0 && /* @__PURE__ */ jsx(SecurityTabs, {
150
+ securities.length > 0 && /* @__PURE__ */ jsx(SecurityRequirements, {
150
151
  securities,
151
- securityId,
152
- setSecurityId,
152
+ securityId: requirementId,
153
+ setSecurityId: setRequirementId,
153
154
  children: inputs.map((input) => /* @__PURE__ */ jsx(Fragment, { children: input.children }, stringifyFieldKey(input.fieldName)))
154
155
  }),
155
156
  /* @__PURE__ */ jsx(ParametersForm, { parameters }),
@@ -163,50 +164,62 @@ function PlaygroundClient({ route, method, securities, doc, proxyUrl, writeOnly,
163
164
  })
164
165
  });
165
166
  }
166
- function SecurityTabsSelectItem({ security }) {
167
- return /* @__PURE__ */ jsx("div", {
168
- className: "flex flex-col gap-2 max-w-[600px]",
169
- children: security.map((item) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
170
- className: cn("font-mono font-medium", item.deprecated && "text-fd-muted-foreground line-through"),
171
- children: item.id
172
- }), /* @__PURE__ */ jsx("p", {
173
- className: "text-fd-muted-foreground whitespace-pre-wrap",
174
- children: item.description
175
- })] }, item.id))
176
- });
177
- }
178
- function SecurityTabs({ securities, setSecurityId, securityId, children }) {
179
- const [open, setOpen] = useState(false);
180
- const engine = useDataEngine();
167
+ function SecurityRequirements({ securities, setSecurityId, securityId, children }) {
181
168
  const t = useTranslations();
169
+ const { isLoading, error } = useAuth();
170
+ const defaultOpen = isLoading || error != null;
171
+ const [open, setOpen] = useState(defaultOpen);
182
172
  const { CollapsiblePanel = DefaultCollapsiblePanel } = useApiContext().client.playground?.components ?? {};
183
- const result = /* @__PURE__ */ jsxs(CollapsiblePanel, {
184
- title: t.authorization,
173
+ useOnChange(defaultOpen, () => {
174
+ if (defaultOpen) setOpen(true);
175
+ });
176
+ return /* @__PURE__ */ jsxs(CollapsiblePanel, {
177
+ title: /* @__PURE__ */ jsxs(Fragment$1, { children: [t.authorization, isLoading && /* @__PURE__ */ jsxs("span", {
178
+ className: "border-s ps-2 inline-flex items-center gap-1.5 text-fd-muted-foreground text-xs font-mono",
179
+ children: [
180
+ /* @__PURE__ */ jsx(Spinner, {}),
181
+ " ",
182
+ t.fetchingToken
183
+ ]
184
+ })] }),
185
185
  "data-type": "authorization",
186
- children: [/* @__PURE__ */ jsxs(Select, {
187
- value: securityId.toString(),
188
- onValueChange: (v) => setSecurityId(Number(v)),
189
- children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { children: /* @__PURE__ */ jsx(SecurityTabsSelectItem, { security: securities[securityId] }) }) }), /* @__PURE__ */ jsx(SelectContent, { children: securities.map((security, i) => /* @__PURE__ */ jsx(SelectItem, {
190
- value: i.toString(),
191
- children: /* @__PURE__ */ jsx(SecurityTabsSelectItem, { security })
192
- }, i)) })]
193
- }), children]
186
+ open,
187
+ onOpenChange: setOpen,
188
+ children: [
189
+ error != null && /* @__PURE__ */ jsxs("div", {
190
+ className: "p-2 border rounded-lg bg-fd-secondary",
191
+ children: [/* @__PURE__ */ jsx("p", {
192
+ className: "text-fd-muted-foreground font-medium mb-1",
193
+ children: t.fetchTokenError
194
+ }), /* @__PURE__ */ jsx("p", { children: String(error) })]
195
+ }),
196
+ /* @__PURE__ */ jsxs(Select, {
197
+ value: securityId.toString(),
198
+ onValueChange: (v) => setSecurityId(Number(v)),
199
+ children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { children: /* @__PURE__ */ jsx(SecurityRequirement, { requirement: securities[securityId] }) }) }), /* @__PURE__ */ jsx(SelectContent, { children: securities.map((security, i) => /* @__PURE__ */ jsx(SelectItem, {
200
+ value: i.toString(),
201
+ children: /* @__PURE__ */ jsx(SecurityRequirement, { requirement: security })
202
+ }, i)) })]
203
+ }),
204
+ children
205
+ ]
206
+ });
207
+ }
208
+ function SecurityRequirement({ requirement }) {
209
+ const { schemes } = useApiContext();
210
+ return /* @__PURE__ */ jsx("div", {
211
+ className: "flex flex-col gap-2 max-w-[600px]",
212
+ children: requirement.map((item) => {
213
+ const scheme = schemes[item.id];
214
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
215
+ className: cn("font-mono font-medium", scheme.deprecated && "text-fd-muted-foreground line-through"),
216
+ children: item.id
217
+ }), /* @__PURE__ */ jsx("p", {
218
+ className: "text-fd-muted-foreground whitespace-pre-wrap",
219
+ children: scheme.description
220
+ })] }, item.id);
221
+ })
194
222
  });
195
- for (let i = 0; i < securities.length; i++) {
196
- const security = securities[i];
197
- for (const item of security) if (item.type === "oauth2") return /* @__PURE__ */ jsx(OauthDialog, {
198
- scheme: item,
199
- scopes: item.scopes,
200
- open,
201
- setOpen: (v) => {
202
- setOpen(v);
203
- if (v) setSecurityId(i);
204
- },
205
- setToken: (token) => engine.update(["header", "Authorization"], token),
206
- children: result
207
- });
208
- }
209
- return result;
210
223
  }
211
224
  const ParamTypes = [
212
225
  "path",
@@ -287,98 +300,97 @@ function BodyInput({ field: _field }) {
287
300
  })
288
301
  });
289
302
  }
290
- function useAuthInputs(securities, transform) {
303
+ function useAuthInputs(engine, requirements) {
304
+ const authCtx = useAuth();
291
305
  const storageKeys = useStorageKey();
292
306
  const t = useTranslations();
293
- const inputs = useMemo(() => {
294
- const result = [];
295
- if (!securities) return result;
296
- for (const security of securities) if (security.type === "http" && security.scheme === "basic") {
297
- const fieldName = ["header", "Authorization"];
298
- result.push({
299
- fieldName,
300
- original: security,
301
- defaultValue: {
302
- username: "",
303
- password: ""
304
- },
305
- mapOutput(out) {
306
- if (out && typeof out === "object") {
307
- const obj = out;
308
- return `Basic ${btoa(`${obj.username ?? ""}:${obj.password ?? ""}`)}`;
309
- }
310
- return out;
311
- },
312
- children: /* @__PURE__ */ jsx(ObjectInput, {
313
- field: {
314
- type: "object",
315
- properties: {
316
- username: { type: "string" },
317
- password: { type: "string" }
307
+ const { schemes, client: { playground: { transformAuthInputs } = {} } } = useApiContext();
308
+ const [requirementId, setRequirementId] = useState(() => {
309
+ if (requirements.length === 0) return -1;
310
+ const idx = requirements.findIndex((s) => s.every((item) => !schemes[item.id].deprecated));
311
+ return idx !== -1 ? idx : 0;
312
+ });
313
+ const requirement = requirementId === -1 ? null : requirements[requirementId];
314
+ let inputs = useMemo(() => {
315
+ if (!requirement) return [];
316
+ return requirement.map((item) => {
317
+ const scheme = schemes[item.id];
318
+ if (scheme.type === "http" && scheme.scheme === "basic") {
319
+ const fieldName = ["header", "Authorization"];
320
+ return {
321
+ fieldName,
322
+ schemeId: item.id,
323
+ storageKey: storageKeys.AuthField(item.id),
324
+ defaultValue: {
325
+ username: "",
326
+ password: ""
327
+ },
328
+ mapOutput(out) {
329
+ if (out && typeof out === "object") {
330
+ const obj = out;
331
+ return `Basic ${btoa(`${obj.username ?? ""}:${obj.password ?? ""}`)}`;
318
332
  }
333
+ return out;
319
334
  },
320
- fieldName
321
- })
322
- });
323
- } else if (security.type === "oauth2") {
324
- const fieldName = ["header", "Authorization"];
325
- result.push({
326
- fieldName,
327
- original: security,
328
- defaultValue: "Bearer ",
329
- children: /* @__PURE__ */ jsxs("fieldset", {
330
- className: "flex flex-col gap-2",
331
- children: [/* @__PURE__ */ jsx("label", {
332
- htmlFor: stringifyFieldKey(fieldName),
333
- className: cn(labelVariants()),
334
- children: t.accessToken
335
- }), /* @__PURE__ */ jsxs("div", {
336
- className: "flex gap-2",
337
- children: [/* @__PURE__ */ jsx(FieldInput, {
338
- fieldName,
339
- field: { type: "string" },
340
- className: "flex-1"
341
- }), /* @__PURE__ */ jsx(OauthDialogTrigger, {
342
- type: "button",
343
- className: cn(buttonVariants({
344
- size: "sm",
345
- color: "secondary"
346
- })),
347
- children: t.authorize
348
- })]
349
- })]
350
- })
351
- });
352
- } else if (security.type === "http") {
353
- const fieldName = ["header", "Authorization"];
354
- result.push({
355
- fieldName,
356
- original: security,
357
- defaultValue: "Bearer ",
358
- children: /* @__PURE__ */ jsx(FieldSet, {
359
- name: `${t.authorization} (${t.header})`,
335
+ children: /* @__PURE__ */ jsx(ObjectInput, {
336
+ field: {
337
+ type: "object",
338
+ properties: {
339
+ username: { type: "string" },
340
+ password: { type: "string" }
341
+ }
342
+ },
343
+ fieldName
344
+ })
345
+ };
346
+ }
347
+ if (scheme.type === "oauth2") {
348
+ const fieldName = ["header", "Authorization"];
349
+ return {
360
350
  fieldName,
361
- field: { type: "string" }
362
- })
363
- });
364
- } else if (security.type === "apiKey") {
365
- const fieldName = [security.in, security.name];
366
- result.push({
367
- fieldName,
368
- defaultValue: "",
369
- original: security,
370
- children: /* @__PURE__ */ jsx(FieldSet, {
351
+ schemeId: item.id,
352
+ storageKey: storageKeys.AuthField(item.id),
353
+ defaultValue: "Bearer ",
354
+ children: /* @__PURE__ */ jsx(OAuth2Input, {
355
+ fieldName,
356
+ security: item
357
+ })
358
+ };
359
+ }
360
+ if (scheme.type === "http") {
361
+ const fieldName = ["header", "Authorization"];
362
+ return {
371
363
  fieldName,
372
- name: `${security.name} (${security.in})`,
373
- field: { type: "string" }
374
- })
375
- });
376
- } else {
364
+ schemeId: item.id,
365
+ storageKey: storageKeys.AuthField(item.id),
366
+ defaultValue: "Bearer ",
367
+ children: /* @__PURE__ */ jsx(FieldSet, {
368
+ name: `${t.authorization} (${t.header})`,
369
+ fieldName,
370
+ field: { type: "string" }
371
+ })
372
+ };
373
+ }
374
+ if (scheme.type === "apiKey") {
375
+ const fieldName = [scheme.in, scheme.name];
376
+ return {
377
+ fieldName,
378
+ schemeId: item.id,
379
+ defaultValue: "",
380
+ storageKey: storageKeys.AuthField(item.id),
381
+ children: /* @__PURE__ */ jsx(FieldSet, {
382
+ fieldName,
383
+ name: `${scheme.name} (${scheme.in})`,
384
+ field: { type: "string" }
385
+ })
386
+ };
387
+ }
377
388
  const fieldName = ["header", "Authorization"];
378
- result.push({
389
+ return {
379
390
  fieldName,
391
+ schemeId: item.id,
380
392
  defaultValue: "",
381
- original: security,
393
+ storageKey: storageKeys.AuthField(item.id),
382
394
  children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(FieldSet, {
383
395
  name: `${t.authorization} (${t.header})`,
384
396
  fieldName,
@@ -387,45 +399,106 @@ function useAuthInputs(securities, transform) {
387
399
  className: "text-fd-muted-foreground text-xs",
388
400
  children: t.openIdUnsupported
389
401
  })] })
390
- });
391
- }
392
- return transform ? transform(result) : result;
402
+ };
403
+ });
393
404
  }, [
394
- securities,
395
- transform,
405
+ requirement,
406
+ storageKeys,
407
+ schemes,
396
408
  t
397
409
  ]);
398
- const mapInputs = (values) => {
399
- const cloned = structuredClone(values);
400
- for (const item of inputs) {
401
- if (!item.mapOutput) continue;
402
- objectSet(cloned, item.fieldName, item.mapOutput(objectGet(cloned, item.fieldName)));
403
- }
404
- return cloned;
405
- };
406
- const initAuthValues = (stf) => {
407
- const { dataEngine } = stf;
408
- for (const item of inputs) {
409
- const stored = localStorage.getItem(storageKeys.AuthField(item));
410
- if (stored) {
411
- const parsed = JSON.parse(stored);
412
- if (typeof parsed === typeof item.defaultValue) {
413
- dataEngine.init(item.fieldName, parsed);
414
- continue;
415
- }
410
+ if (transformAuthInputs) inputs = transformAuthInputs(inputs);
411
+ useListener({
412
+ stf: engine,
413
+ onUpdate(key) {
414
+ for (const item of inputs) {
415
+ if (!arrayStartsWith(item.fieldName, key)) continue;
416
+ const value = engine.get(item.fieldName);
417
+ if (value != null) localStorage.setItem(item.storageKey, JSON.stringify(value));
416
418
  }
417
- dataEngine.init(item.fieldName, item.defaultValue);
418
419
  }
419
- return () => {
420
- for (const item of inputs) stf.dataEngine.delete(item.fieldName);
421
- };
422
- };
420
+ });
421
+ useOnChange(authCtx.updatedSchemeId, () => {
422
+ const { updatedSchemeId } = authCtx;
423
+ if (!updatedSchemeId) return;
424
+ const { token } = authCtx.store[updatedSchemeId];
425
+ const input = inputs.find((input) => input.schemeId === updatedSchemeId);
426
+ if (input) {
427
+ engine.update(input.fieldName, token);
428
+ return;
429
+ }
430
+ const idx = requirements.findIndex((requirement) => requirement.some((item) => item.id === updatedSchemeId));
431
+ if (idx !== -1) {
432
+ localStorage.setItem(storageKeys.AuthField(updatedSchemeId), JSON.stringify(token));
433
+ setRequirementId(idx);
434
+ }
435
+ });
423
436
  return {
424
437
  inputs,
425
- mapInputs,
426
- initAuthValues
438
+ requirementId,
439
+ setRequirementId,
440
+ mapInputs(values) {
441
+ const cloned = structuredClone(values);
442
+ for (const item of inputs) {
443
+ if (!item.mapOutput) continue;
444
+ objectSet(cloned, item.fieldName, item.mapOutput(objectGet(cloned, item.fieldName)));
445
+ }
446
+ return cloned;
447
+ },
448
+ initAuthInputs() {
449
+ for (const item of inputs) {
450
+ const stored = localStorage.getItem(item.storageKey);
451
+ if (stored) {
452
+ const parsed = JSON.parse(stored);
453
+ if (typeof parsed === typeof item.defaultValue) {
454
+ engine.init(item.fieldName, parsed);
455
+ continue;
456
+ }
457
+ }
458
+ engine.init(item.fieldName, item.defaultValue);
459
+ }
460
+ return () => {
461
+ for (const item of inputs) engine.delete(item.fieldName);
462
+ };
463
+ }
427
464
  };
428
465
  }
466
+ function OAuth2Input({ fieldName, security }) {
467
+ const [open, setOpen] = useState(false);
468
+ const engine = useDataEngine();
469
+ const t = useTranslations();
470
+ return /* @__PURE__ */ jsxs("fieldset", {
471
+ className: "flex flex-col gap-2",
472
+ children: [/* @__PURE__ */ jsx("label", {
473
+ htmlFor: stringifyFieldKey(fieldName),
474
+ className: cn(labelVariants()),
475
+ children: t.accessToken
476
+ }), /* @__PURE__ */ jsxs("div", {
477
+ className: "flex gap-2",
478
+ children: [/* @__PURE__ */ jsx(FieldInput, {
479
+ fieldName,
480
+ field: { type: "string" },
481
+ className: "flex-1"
482
+ }), /* @__PURE__ */ jsxs(OAuthDialog, {
483
+ open,
484
+ onOpenChange: setOpen,
485
+ children: [/* @__PURE__ */ jsx(OAuthDialogTrigger, {
486
+ type: "button",
487
+ className: cn(buttonVariants({
488
+ size: "sm",
489
+ color: "secondary"
490
+ })),
491
+ children: t.authorize
492
+ }), /* @__PURE__ */ jsx(OAuthDialogContent, {
493
+ setOpen,
494
+ schemeId: security.id,
495
+ scopes: security.scopes,
496
+ setToken: (token) => engine.update(["header", "Authorization"], token)
497
+ })]
498
+ })]
499
+ })]
500
+ });
501
+ }
429
502
  function Route({ route, ...props }) {
430
503
  return /* @__PURE__ */ jsx("div", {
431
504
  ...props,
@@ -442,39 +515,6 @@ function Route({ route, ...props }) {
442
515
  })] }, index))
443
516
  });
444
517
  }
445
- function DefaultResultDisplay({ data, reset, ...rest }) {
446
- const t = useTranslations();
447
- const statusInfo = useMemo(() => getStatusInfo(data.status, t), [data.status, t]);
448
- return /* @__PURE__ */ jsxs("div", {
449
- ...rest,
450
- className: cn("flex flex-col gap-3 mt-2 px-3 py-2 border-y bg-fd-secondary text-fd-secondary-foreground", rest.className),
451
- children: [
452
- /* @__PURE__ */ jsxs("div", {
453
- className: "flex justify-between items-center",
454
- children: [/* @__PURE__ */ jsxs("div", {
455
- className: "inline-flex items-center gap-1.5 text-sm font-medium",
456
- children: [/* @__PURE__ */ jsx(statusInfo.icon, { className: cn("size-4", statusInfo.color) }), statusInfo.description]
457
- }), /* @__PURE__ */ jsx("button", {
458
- type: "button",
459
- className: cn(buttonVariants({
460
- size: "sm",
461
- variant: "outline"
462
- })),
463
- onClick: () => reset(),
464
- children: t.close
465
- })]
466
- }),
467
- /* @__PURE__ */ jsx("p", {
468
- className: "text-sm text-fd-muted-foreground",
469
- children: data.status
470
- }),
471
- data.data !== void 0 && /* @__PURE__ */ jsx(ClientCodeBlock, {
472
- lang: typeof data.data === "string" && data.data.length > 5e4 ? "text" : data.type,
473
- code: typeof data.data === "string" ? data.data : JSON.stringify(data.data, null, 2)
474
- })
475
- ]
476
- });
477
- }
478
518
  function DefaultCollapsiblePanel({ title, children, ...props }) {
479
519
  return /* @__PURE__ */ jsxs(Collapsible, {
480
520
  ...props,
@@ -1,15 +1,15 @@
1
1
  "use client";
2
+ import { useTranslations } from "../../ui/client/i18n.js";
2
3
  import { cn } from "../../utils/cn.js";
3
4
  import { FormatFlags } from "../../utils/schema/to-string.js";
4
5
  import { anyFields, useFieldInfo, useResolvedSchema, useSchemaScope, useSchemaUtils } from "../schema.js";
5
6
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../ui/components/select.js";
6
7
  import { Input, labelVariants } from "../../ui/components/input.js";
7
- import { useTranslations } from "../../ui/client/i18n.js";
8
8
  import { useState } from "react";
9
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { ChevronRight, Plus, Trash2, X } from "lucide-react";
11
- import { cva } from "class-variance-authority";
12
11
  import { buttonVariants } from "fumadocs-ui/components/ui/button";
12
+ import { cva } from "class-variance-authority";
13
13
  import { useArray, useDataEngine, useFieldValue, useObject } from "@fumari/stf";
14
14
  import { stringifyFieldKey } from "@fumari/stf/lib/utils";
15
15
  //#region src/playground/components/inputs.tsx