@oaspect/react 0.1.0
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/LICENSE +21 -0
- package/README.md +14 -0
- package/dist/api-reference.js +476 -0
- package/dist/api-reference.js.map +1 -0
- package/dist/auth-panel.js +116 -0
- package/dist/auth-panel.js.map +1 -0
- package/dist/code-block.js +105 -0
- package/dist/code-block.js.map +1 -0
- package/dist/i18n/ar.js +89 -0
- package/dist/i18n/ar.js.map +1 -0
- package/dist/i18n/context.js +50 -0
- package/dist/i18n/context.js.map +1 -0
- package/dist/i18n/en.js +89 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/index.js +37 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/tr.js +89 -0
- package/dist/i18n/tr.js.map +1 -0
- package/dist/icons.js +55 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/intro.js +132 -0
- package/dist/intro.js.map +1 -0
- package/dist/lazy.js +41 -0
- package/dist/lazy.js.map +1 -0
- package/dist/logo.js +12 -0
- package/dist/logo.js.map +1 -0
- package/dist/markdown.js +170 -0
- package/dist/markdown.js.map +1 -0
- package/dist/method-badge.js +37 -0
- package/dist/method-badge.js.map +1 -0
- package/dist/models.js +27 -0
- package/dist/models.js.map +1 -0
- package/dist/oaspect.css +1703 -0
- package/dist/operation.js +267 -0
- package/dist/operation.js.map +1 -0
- package/dist/samples.js +129 -0
- package/dist/samples.js.map +1 -0
- package/dist/schema-tree.js +160 -0
- package/dist/schema-tree.js.map +1 -0
- package/dist/send.js +58 -0
- package/dist/send.js.map +1 -0
- package/dist/sidebar.js +125 -0
- package/dist/sidebar.js.map +1 -0
- package/dist/spec-context.js +23 -0
- package/dist/spec-context.js.map +1 -0
- package/dist/storage.js +30 -0
- package/dist/storage.js.map +1 -0
- package/dist/try-it.js +277 -0
- package/dist/try-it.js.map +1 -0
- package/package.json +66 -0
- package/tailwind.css +21 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 oaspect contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @oaspect/react
|
|
2
|
+
|
|
3
|
+
The [oaspect](https://github.com/oaspect/oaspect) API reference as a React component.
|
|
4
|
+
|
|
5
|
+
```jsx
|
|
6
|
+
import { ApiReference } from "@oaspect/react";
|
|
7
|
+
import "@oaspect/react/styles.css";
|
|
8
|
+
|
|
9
|
+
<ApiReference specUrl="/openapi.yaml" />;
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Every module carries `"use client"`, so it works in React Server Components
|
|
13
|
+
frameworks such as Next.js. See the [main README](https://github.com/oaspect/oaspect#configuration)
|
|
14
|
+
for all props. MIT licensed.
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { applySecurity, buildModel, loadSpec, loadSpecText, normalizeSpec, securityRequirements, serverUrl } from "@oaspect/core";
|
|
5
|
+
import { LocaleProvider, useAvailableLocales, useLocale, useLocalized, useSetLocale, useT } from "./i18n/context.js";
|
|
6
|
+
import { LOCALE_NAMES, directionOf, mergeMessages } from "./i18n/index.js";
|
|
7
|
+
import { LazyContext, scrollToAnchor } from "./lazy.js";
|
|
8
|
+
import { createStorage } from "./storage.js";
|
|
9
|
+
import Intro from "./intro.js";
|
|
10
|
+
import Markdown from "./markdown.js";
|
|
11
|
+
import Models from "./models.js";
|
|
12
|
+
import Operation from "./operation.js";
|
|
13
|
+
import Sidebar from "./sidebar.js";
|
|
14
|
+
import { ModelContext, SettingsContext, SpecContext } from "./spec-context.js";
|
|
15
|
+
import TryIt from "./try-it.js";
|
|
16
|
+
import { MenuIcon, MoonIcon, SunIcon } from "./icons.js";
|
|
17
|
+
const SPEC_SOURCE_HEADER = "x-oaspect-spec-source";
|
|
18
|
+
function LanguageToggle() {
|
|
19
|
+
const locale = useLocale();
|
|
20
|
+
const setLocale = useSetLocale();
|
|
21
|
+
const locales = useAvailableLocales();
|
|
22
|
+
const t = useT();
|
|
23
|
+
return (
|
|
24
|
+
// The native select sits invisibly on top: the button shows the short
|
|
25
|
+
// code ("TR"), the opened list shows full language names.
|
|
26
|
+
/* @__PURE__ */ jsxs(
|
|
27
|
+
"label",
|
|
28
|
+
{
|
|
29
|
+
title: t("header.language"),
|
|
30
|
+
className: "relative flex cursor-pointer items-center gap-1.5 rounded-lg border border-border px-2.5 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground",
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", className: "size-4", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
33
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
34
|
+
/* @__PURE__ */ jsx("path", { d: "M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" })
|
|
35
|
+
] }),
|
|
36
|
+
/* @__PURE__ */ jsx("span", { className: "uppercase", children: locale }),
|
|
37
|
+
/* @__PURE__ */ jsx(
|
|
38
|
+
"select",
|
|
39
|
+
{
|
|
40
|
+
value: locale,
|
|
41
|
+
onChange: (event) => setLocale(event.target.value),
|
|
42
|
+
"aria-label": t("header.language"),
|
|
43
|
+
className: "absolute inset-0 cursor-pointer opacity-0",
|
|
44
|
+
children: locales.map((item) => /* @__PURE__ */ jsx("option", { value: item, children: LOCALE_NAMES[item] ?? item }, item))
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
function ThemeToggle({ theme, onChange }) {
|
|
53
|
+
const t = useT();
|
|
54
|
+
return /* @__PURE__ */ jsxs(
|
|
55
|
+
"button",
|
|
56
|
+
{
|
|
57
|
+
type: "button",
|
|
58
|
+
onClick: () => onChange(theme === "dark" ? "light" : "dark"),
|
|
59
|
+
"aria-label": t("header.theme"),
|
|
60
|
+
title: t("header.theme"),
|
|
61
|
+
className: "rounded-lg border border-border p-2 text-muted-foreground hover:text-foreground",
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ jsx(MoonIcon, { className: "size-4 dark:hidden" }),
|
|
64
|
+
/* @__PURE__ */ jsx(SunIcon, { className: "hidden size-4 dark:block" })
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function SourceMenu({ source, defaultLabel, onUrl, onFile, onDefault }) {
|
|
70
|
+
const [draft, setDraft] = useState(source.type === "url" ? source.url : "");
|
|
71
|
+
const fileInput = useRef(null);
|
|
72
|
+
const menu = useRef(null);
|
|
73
|
+
const close = () => menu.current?.removeAttribute("open");
|
|
74
|
+
const t = useT();
|
|
75
|
+
return /* @__PURE__ */ jsxs("details", { ref: menu, className: "relative", children: [
|
|
76
|
+
/* @__PURE__ */ jsxs(
|
|
77
|
+
"summary",
|
|
78
|
+
{
|
|
79
|
+
title: t("source.title"),
|
|
80
|
+
className: "flex cursor-pointer list-none items-center gap-1.5 rounded-lg border border-border px-2.5 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground [&::-webkit-details-marker]:hidden",
|
|
81
|
+
children: [
|
|
82
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", className: "size-4", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
83
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
84
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2v6h6M10 13l-2 2 2 2M14 13l2 2-2 2" })
|
|
85
|
+
] }),
|
|
86
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t("source.title") })
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute end-0 z-40 mt-2 w-80 space-y-3 rounded-xl border border-border bg-background p-4 shadow-xl", children: [
|
|
91
|
+
/* @__PURE__ */ jsxs(
|
|
92
|
+
"button",
|
|
93
|
+
{
|
|
94
|
+
type: "button",
|
|
95
|
+
onClick: () => {
|
|
96
|
+
onDefault();
|
|
97
|
+
close();
|
|
98
|
+
},
|
|
99
|
+
className: `block w-full rounded-lg border px-3 py-2 text-start text-sm ${source.type === "default" ? "border-primary text-primary" : "border-border hover:border-primary"}`,
|
|
100
|
+
children: [
|
|
101
|
+
t("source.default"),
|
|
102
|
+
defaultLabel && /* @__PURE__ */ jsx("span", { className: "block truncate font-mono text-[11px] text-muted-foreground", children: defaultLabel })
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
/* @__PURE__ */ jsxs(
|
|
107
|
+
"form",
|
|
108
|
+
{
|
|
109
|
+
onSubmit: (event) => {
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
if (draft.trim()) {
|
|
112
|
+
onUrl(draft.trim());
|
|
113
|
+
close();
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
className: "space-y-2",
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsx(
|
|
119
|
+
"input",
|
|
120
|
+
{
|
|
121
|
+
type: "url",
|
|
122
|
+
value: draft,
|
|
123
|
+
onChange: (event) => setDraft(event.target.value),
|
|
124
|
+
placeholder: "https://\u2026/swagger.json",
|
|
125
|
+
dir: "ltr",
|
|
126
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-1.5 font-mono text-xs outline-none focus:border-primary"
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
/* @__PURE__ */ jsx("button", { type: "submit", className: "w-full rounded-lg border border-border px-3 py-1.5 text-sm hover:border-primary hover:text-primary", children: t("source.loadUrl") })
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
),
|
|
133
|
+
/* @__PURE__ */ jsx(
|
|
134
|
+
"button",
|
|
135
|
+
{
|
|
136
|
+
type: "button",
|
|
137
|
+
onClick: () => fileInput.current?.click(),
|
|
138
|
+
className: "w-full rounded-lg border border-dashed border-border px-3 py-2 text-sm hover:border-primary hover:text-primary",
|
|
139
|
+
children: source.type === "file" ? t("source.file", { name: source.name }) : t("source.chooseFile")
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
/* @__PURE__ */ jsx(
|
|
143
|
+
"input",
|
|
144
|
+
{
|
|
145
|
+
ref: fileInput,
|
|
146
|
+
type: "file",
|
|
147
|
+
accept: ".json,.yaml,.yml,application/json,application/yaml,text/yaml",
|
|
148
|
+
className: "hidden",
|
|
149
|
+
onChange: (event) => {
|
|
150
|
+
const file = event.target.files?.[0];
|
|
151
|
+
event.target.value = "";
|
|
152
|
+
if (file) {
|
|
153
|
+
onFile(file);
|
|
154
|
+
close();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
] })
|
|
160
|
+
] });
|
|
161
|
+
}
|
|
162
|
+
async function readText(url) {
|
|
163
|
+
const response = await fetch(url);
|
|
164
|
+
if (!response.ok) throw new Error(`${url} \u2192 HTTP ${response.status}`);
|
|
165
|
+
return response.text();
|
|
166
|
+
}
|
|
167
|
+
function systemTheme() {
|
|
168
|
+
try {
|
|
169
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
170
|
+
} catch {
|
|
171
|
+
return "light";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const LAZY_THRESHOLD = 40;
|
|
175
|
+
const EAGER_OPERATIONS = 8;
|
|
176
|
+
const DEFAULT_FEATURES = {
|
|
177
|
+
sourceMenu: true,
|
|
178
|
+
languageSwitcher: true,
|
|
179
|
+
themeToggle: true,
|
|
180
|
+
tryIt: true,
|
|
181
|
+
models: true
|
|
182
|
+
};
|
|
183
|
+
function Viewer({
|
|
184
|
+
spec: inlineSpec,
|
|
185
|
+
specUrl,
|
|
186
|
+
title,
|
|
187
|
+
logo,
|
|
188
|
+
storagePrefix,
|
|
189
|
+
proxyUrl,
|
|
190
|
+
features: featureOverrides,
|
|
191
|
+
defaultSnippet,
|
|
192
|
+
urlParam,
|
|
193
|
+
theme: themeProp,
|
|
194
|
+
lazy
|
|
195
|
+
}) {
|
|
196
|
+
const features = { ...DEFAULT_FEATURES, ...featureOverrides };
|
|
197
|
+
const storage = useMemo(() => createStorage(storagePrefix), [storagePrefix]);
|
|
198
|
+
const rootRef = useRef(null);
|
|
199
|
+
const locale = useLocale();
|
|
200
|
+
const [nestedInMain, setNestedInMain] = useState(false);
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
setNestedInMain(Boolean(rootRef.current?.parentElement?.closest("main, [role=main]")));
|
|
203
|
+
}, []);
|
|
204
|
+
const [source, setSource] = useState({ type: "pending" });
|
|
205
|
+
const normalizedInline = useMemo(() => inlineSpec ? normalizeSpec(inlineSpec) : null, [inlineSpec]);
|
|
206
|
+
const [spec, setSpec] = useState(normalizedInline);
|
|
207
|
+
const [error, setError] = useState("");
|
|
208
|
+
const [usingFallback, setUsingFallback] = useState(false);
|
|
209
|
+
const [activeAnchor, setActiveAnchor] = useState("introduction");
|
|
210
|
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
211
|
+
const [tryOperation, setTryOperation] = useState(null);
|
|
212
|
+
const searchRef = useRef(null);
|
|
213
|
+
const t = useT();
|
|
214
|
+
const localized = useLocalized();
|
|
215
|
+
const [serverIndex, setServerIndex] = useState(0);
|
|
216
|
+
const [customServer, setCustomServer] = useState("");
|
|
217
|
+
const [serverVariables, setServerVariables] = useState({});
|
|
218
|
+
const [token, setToken] = useState("");
|
|
219
|
+
const [credentials, setCredentials] = useState({});
|
|
220
|
+
const [language, setLanguage] = useState(defaultSnippet);
|
|
221
|
+
const [theme, setTheme] = useState(themeProp ?? null);
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
setToken(storage.get("token", ""));
|
|
224
|
+
try {
|
|
225
|
+
setCredentials(JSON.parse(storage.get("credentials", "{}")) ?? {});
|
|
226
|
+
} catch {
|
|
227
|
+
}
|
|
228
|
+
setLanguage(storage.get("language", defaultSnippet));
|
|
229
|
+
if (!themeProp) setTheme(storage.get("theme") ?? document.documentElement.dataset.theme ?? systemTheme());
|
|
230
|
+
const url = urlParam ? new URLSearchParams(window.location.search).get(urlParam) : null;
|
|
231
|
+
setSource(url ? { type: "url", url } : { type: "default" });
|
|
232
|
+
}, [storage, defaultSnippet, urlParam, themeProp]);
|
|
233
|
+
useEffect(() => storage.set("token", token), [storage, token]);
|
|
234
|
+
useEffect(() => storage.set("credentials", Object.keys(credentials).length ? JSON.stringify(credentials) : null), [storage, credentials]);
|
|
235
|
+
useEffect(() => storage.set("language", language), [storage, language]);
|
|
236
|
+
function changeTheme(next) {
|
|
237
|
+
setTheme(next);
|
|
238
|
+
storage.set("theme", next);
|
|
239
|
+
if (document.documentElement.hasAttribute("data-theme")) document.documentElement.dataset.theme = next;
|
|
240
|
+
}
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
if (source.type === "default" && normalizedInline) {
|
|
243
|
+
setSpec(normalizedInline);
|
|
244
|
+
setUsingFallback(false);
|
|
245
|
+
setError("");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (source.type !== "default" && source.type !== "url") return;
|
|
249
|
+
const url = source.type === "url" ? source.url : specUrl;
|
|
250
|
+
if (!url) return;
|
|
251
|
+
let cancelled = false;
|
|
252
|
+
if (urlParam) {
|
|
253
|
+
const params = new URLSearchParams(window.location.search);
|
|
254
|
+
if (source.type === "url") params.set(urlParam, url);
|
|
255
|
+
else params.delete(urlParam);
|
|
256
|
+
const search = params.toString();
|
|
257
|
+
window.history.replaceState(null, "", `${window.location.pathname}${search ? `?${search}` : ""}${window.location.hash}`);
|
|
258
|
+
}
|
|
259
|
+
fetch(url).then(async (response) => {
|
|
260
|
+
if (!response.ok) throw new Error(`${url} \u2192 HTTP ${response.status}`);
|
|
261
|
+
return { text: await response.text(), fallback: response.headers.get(SPEC_SOURCE_HEADER) === "fallback" };
|
|
262
|
+
}).then(async ({ text, fallback }) => {
|
|
263
|
+
const json = await loadSpec(text, { baseUrl: new URL(url, window.location.href).href, read: readText });
|
|
264
|
+
if (!cancelled) {
|
|
265
|
+
setSpec(json);
|
|
266
|
+
setUsingFallback(fallback);
|
|
267
|
+
setError("");
|
|
268
|
+
}
|
|
269
|
+
}).catch((err) => !cancelled && setError(err.message));
|
|
270
|
+
return () => {
|
|
271
|
+
cancelled = true;
|
|
272
|
+
};
|
|
273
|
+
}, [source, specUrl, normalizedInline, urlParam]);
|
|
274
|
+
async function loadFile(file) {
|
|
275
|
+
try {
|
|
276
|
+
const json = loadSpecText(await file.text());
|
|
277
|
+
setSource({ type: "file", name: file.name });
|
|
278
|
+
setSpec(json);
|
|
279
|
+
setUsingFallback(false);
|
|
280
|
+
setError("");
|
|
281
|
+
} catch (err) {
|
|
282
|
+
setError({ key: "spec.unreadableFile", params: { name: file.name, error: err.message } });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const model = useMemo(() => spec ? buildModel(spec) : null, [spec]);
|
|
286
|
+
const lazyEnabled = lazy === true || lazy === "auto" && (model?.operations.length ?? 0) > LAZY_THRESHOLD;
|
|
287
|
+
const eagerAnchors = useMemo(() => new Set(model?.operations.slice(0, EAGER_OPERATIONS).map((operation) => operation.anchor)), [model]);
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
setServerIndex(0);
|
|
290
|
+
}, [model]);
|
|
291
|
+
const schemes = useMemo(() => model?.securitySchemes ?? {}, [model]);
|
|
292
|
+
const settings = useMemo(() => {
|
|
293
|
+
const servers = model?.servers ?? [];
|
|
294
|
+
const selected = serverIndex === -1 ? { url: customServer } : servers[serverIndex] ?? servers[0];
|
|
295
|
+
const variableValues = serverVariables[serverIndex] ?? {};
|
|
296
|
+
const server = selected ? { url: serverUrl(selected, variableValues) } : void 0;
|
|
297
|
+
return {
|
|
298
|
+
servers,
|
|
299
|
+
server,
|
|
300
|
+
selectedServer: selected,
|
|
301
|
+
variableValues,
|
|
302
|
+
setVariable: (name, value) => setServerVariables((current) => ({ ...current, [serverIndex]: { ...current[serverIndex], [name]: value } })),
|
|
303
|
+
serverIndex,
|
|
304
|
+
setServerIndex,
|
|
305
|
+
customServer,
|
|
306
|
+
setCustomServer,
|
|
307
|
+
token,
|
|
308
|
+
setToken,
|
|
309
|
+
credentials,
|
|
310
|
+
setCredential: (name, patch) => setCredentials((current) => ({ ...current, [name]: { ...current[name], ...patch } })),
|
|
311
|
+
securitySchemes: schemes,
|
|
312
|
+
// Headers, query parameters and cookies an operation needs. Without
|
|
313
|
+
// securitySchemes in the document, the generic Bearer token applies.
|
|
314
|
+
authFor: (operation) => Object.keys(schemes).length ? applySecurity(schemes, securityRequirements(operation), credentials) : { headers: token ? { Authorization: `Bearer ${token}` } : {}, query: {}, cookies: {} },
|
|
315
|
+
language,
|
|
316
|
+
setLanguage,
|
|
317
|
+
proxyUrl,
|
|
318
|
+
storage,
|
|
319
|
+
features
|
|
320
|
+
};
|
|
321
|
+
}, [model, schemes, serverIndex, customServer, serverVariables, token, credentials, language, proxyUrl, storage, features.tryIt]);
|
|
322
|
+
useEffect(() => {
|
|
323
|
+
if (!model || !rootRef.current) return;
|
|
324
|
+
const observer = new IntersectionObserver(
|
|
325
|
+
(entries) => {
|
|
326
|
+
const visible = entries.filter((entry) => entry.isIntersecting);
|
|
327
|
+
if (visible.length) setActiveAnchor(visible[visible.length - 1].target.id);
|
|
328
|
+
},
|
|
329
|
+
{ rootMargin: "-72px 0px -70% 0px" }
|
|
330
|
+
);
|
|
331
|
+
rootRef.current.querySelectorAll("[data-anchor]").forEach((node) => observer.observe(node));
|
|
332
|
+
const target = decodeURIComponent(window.location.hash.slice(1));
|
|
333
|
+
if (target) scrollToAnchor(target);
|
|
334
|
+
const onHash = () => scrollToAnchor(decodeURIComponent(window.location.hash.slice(1)));
|
|
335
|
+
window.addEventListener("hashchange", onHash);
|
|
336
|
+
return () => {
|
|
337
|
+
observer.disconnect();
|
|
338
|
+
window.removeEventListener("hashchange", onHash);
|
|
339
|
+
};
|
|
340
|
+
}, [model]);
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
function onKey(event) {
|
|
343
|
+
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
344
|
+
event.preventDefault();
|
|
345
|
+
setSidebarOpen(true);
|
|
346
|
+
requestAnimationFrame(() => searchRef.current?.focus());
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
window.addEventListener("keydown", onKey);
|
|
350
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
351
|
+
}, []);
|
|
352
|
+
const closeSidebar = useCallback(() => setSidebarOpen(false), []);
|
|
353
|
+
const openTry = useCallback((operation) => setTryOperation(operation), []);
|
|
354
|
+
const heading = title ?? (model ? localized(model.info, "title") : "");
|
|
355
|
+
return /* @__PURE__ */ jsx(SpecContext.Provider, { value: spec, children: /* @__PURE__ */ jsx(ModelContext.Provider, { value: model, children: /* @__PURE__ */ jsx(SettingsContext.Provider, { value: settings, children: /* @__PURE__ */ jsx("div", { ref: rootRef, className: "oaspect", "data-theme": theme ?? void 0, dir: directionOf(locale), lang: locale, children: /* @__PURE__ */ jsxs("div", { className: "min-h-dvh bg-background text-foreground", children: [
|
|
356
|
+
/* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-30 flex h-14 items-center gap-3 border-b border-border bg-background/85 px-4 backdrop-blur", children: [
|
|
357
|
+
/* @__PURE__ */ jsx(
|
|
358
|
+
"a",
|
|
359
|
+
{
|
|
360
|
+
href: "#oaspect-content",
|
|
361
|
+
className: "absolute start-2 top-2 z-50 -translate-y-20 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground focus:translate-y-0",
|
|
362
|
+
children: t("header.skip")
|
|
363
|
+
}
|
|
364
|
+
),
|
|
365
|
+
/* @__PURE__ */ jsx(
|
|
366
|
+
"button",
|
|
367
|
+
{
|
|
368
|
+
type: "button",
|
|
369
|
+
onClick: () => setSidebarOpen(!sidebarOpen),
|
|
370
|
+
"aria-label": t("header.menu"),
|
|
371
|
+
className: "rounded-lg border border-border p-2 lg:hidden",
|
|
372
|
+
children: /* @__PURE__ */ jsx(MenuIcon, { className: "size-4" })
|
|
373
|
+
}
|
|
374
|
+
),
|
|
375
|
+
/* @__PURE__ */ jsx("a", { href: "#introduction", className: "flex min-w-0 items-center gap-2", children: logo ?? /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold", children: heading }) }),
|
|
376
|
+
model && logo && /* @__PURE__ */ jsxs("span", { className: "hidden truncate text-sm text-muted-foreground md:inline", children: [
|
|
377
|
+
heading,
|
|
378
|
+
" ",
|
|
379
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-xs", children: [
|
|
380
|
+
"v",
|
|
381
|
+
model.info.version
|
|
382
|
+
] })
|
|
383
|
+
] }),
|
|
384
|
+
/* @__PURE__ */ jsxs("div", { className: "ms-auto flex items-center gap-2", children: [
|
|
385
|
+
features.sourceMenu && source.type !== "pending" && /* @__PURE__ */ jsx(
|
|
386
|
+
SourceMenu,
|
|
387
|
+
{
|
|
388
|
+
source,
|
|
389
|
+
defaultLabel: specUrl,
|
|
390
|
+
onDefault: () => setSource({ type: "default" }),
|
|
391
|
+
onUrl: (url) => setSource({ type: "url", url }),
|
|
392
|
+
onFile: loadFile
|
|
393
|
+
},
|
|
394
|
+
`${source.type}:${source.url ?? source.name ?? ""}`
|
|
395
|
+
),
|
|
396
|
+
features.languageSwitcher && /* @__PURE__ */ jsx(LanguageToggle, {}),
|
|
397
|
+
features.themeToggle && /* @__PURE__ */ jsx(ThemeToggle, { theme, onChange: changeTheme })
|
|
398
|
+
] })
|
|
399
|
+
] }),
|
|
400
|
+
error && /* @__PURE__ */ jsx("div", { className: "border-b border-rose-500/30 bg-rose-500/10 px-4 py-2 text-sm text-rose-700 dark:text-rose-300", children: t("spec.loadFailed", { error: typeof error === "string" ? t(error) : t(error.key, error.params) }) }),
|
|
401
|
+
usingFallback && source.type === "default" && /* @__PURE__ */ jsx("div", { className: "border-b border-amber-500/30 bg-amber-500/10 px-4 py-2 text-sm text-amber-800 dark:text-amber-200", children: t("spec.fallback") }),
|
|
402
|
+
!model ? !error && /* @__PURE__ */ jsx("p", { className: "p-10 text-center text-sm text-muted-foreground", children: t("spec.loading") }) : /* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
403
|
+
/* @__PURE__ */ jsx(
|
|
404
|
+
"aside",
|
|
405
|
+
{
|
|
406
|
+
className: `fixed inset-y-0 start-0 top-14 z-20 w-72 shrink-0 border-e border-border bg-card transition-transform lg:sticky lg:h-[calc(100dvh-3.5rem)] lg:translate-x-0 lg:rtl:translate-x-0 ${sidebarOpen ? "translate-x-0" : "-translate-x-full rtl:translate-x-full"}`,
|
|
407
|
+
children: /* @__PURE__ */ jsx(Sidebar, { model, activeAnchor, searchRef, onNavigate: closeSidebar, showModels: features.models })
|
|
408
|
+
}
|
|
409
|
+
),
|
|
410
|
+
sidebarOpen && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 top-14 z-10 bg-black/30 lg:hidden", onClick: closeSidebar, "aria-hidden": "true" }),
|
|
411
|
+
/* @__PURE__ */ jsx(LazyContext.Provider, { value: lazyEnabled, children: /* @__PURE__ */ jsx("div", { id: "oaspect-content", tabIndex: -1, role: nestedInMain ? "region" : "main", "aria-label": nestedInMain ? heading : void 0, className: "min-w-0 flex-1 px-4 outline-none sm:px-8 xl:px-12", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-[88rem]", children: [
|
|
412
|
+
/* @__PURE__ */ jsx(Intro, { spec, model }),
|
|
413
|
+
model.tags.map((tag) => /* @__PURE__ */ jsxs("section", { id: tag.anchor, "data-anchor": true, className: "pt-14", children: [
|
|
414
|
+
/* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold tracking-tight [overflow-wrap:anywhere]", children: localized(tag, "name") }),
|
|
415
|
+
/* @__PURE__ */ jsx(Markdown, { source: localized(tag, "description"), className: "mt-3 max-w-3xl text-muted-foreground" }),
|
|
416
|
+
tag.operations.map((operation) => /* @__PURE__ */ jsx(
|
|
417
|
+
Operation,
|
|
418
|
+
{
|
|
419
|
+
operation,
|
|
420
|
+
onTry: features.tryIt ? openTry : null,
|
|
421
|
+
eager: eagerAnchors.has(operation.anchor)
|
|
422
|
+
},
|
|
423
|
+
operation.anchor
|
|
424
|
+
))
|
|
425
|
+
] }, tag.name)),
|
|
426
|
+
model.webhooks.length > 0 && /* @__PURE__ */ jsxs("section", { id: "webhooks", "data-anchor": true, className: "pt-14", children: [
|
|
427
|
+
/* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold tracking-tight", children: t("webhooks.title") }),
|
|
428
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 max-w-3xl text-sm text-muted-foreground", children: t("webhooks.hint") }),
|
|
429
|
+
model.webhooks.map((operation) => /* @__PURE__ */ jsx(Operation, { operation, onTry: null }, operation.anchor))
|
|
430
|
+
] }),
|
|
431
|
+
features.models && /* @__PURE__ */ jsx(Models, { schemas: model.schemas })
|
|
432
|
+
] }) }) })
|
|
433
|
+
] }),
|
|
434
|
+
tryOperation && /* @__PURE__ */ jsx(TryIt, { operation: tryOperation, onClose: () => setTryOperation(null) }, tryOperation.anchor)
|
|
435
|
+
] }) }) }) }) });
|
|
436
|
+
}
|
|
437
|
+
function ApiReference({
|
|
438
|
+
spec,
|
|
439
|
+
specUrl,
|
|
440
|
+
title,
|
|
441
|
+
logo,
|
|
442
|
+
locale,
|
|
443
|
+
defaultLocale = "en",
|
|
444
|
+
onLocaleChange,
|
|
445
|
+
messages,
|
|
446
|
+
storagePrefix = "oaspect",
|
|
447
|
+
proxyUrl = null,
|
|
448
|
+
features,
|
|
449
|
+
defaultSnippet = "shell:curl",
|
|
450
|
+
urlParam = "url",
|
|
451
|
+
theme,
|
|
452
|
+
lazy = "auto"
|
|
453
|
+
}) {
|
|
454
|
+
const merged = useMemo(() => mergeMessages(messages), [messages]);
|
|
455
|
+
return /* @__PURE__ */ jsx(LocaleProvider, { locale, defaultLocale, onLocaleChange, messages: merged, children: /* @__PURE__ */ jsx(
|
|
456
|
+
Viewer,
|
|
457
|
+
{
|
|
458
|
+
spec,
|
|
459
|
+
specUrl,
|
|
460
|
+
title,
|
|
461
|
+
logo,
|
|
462
|
+
storagePrefix,
|
|
463
|
+
proxyUrl,
|
|
464
|
+
features,
|
|
465
|
+
defaultSnippet,
|
|
466
|
+
urlParam,
|
|
467
|
+
theme,
|
|
468
|
+
lazy
|
|
469
|
+
}
|
|
470
|
+
) });
|
|
471
|
+
}
|
|
472
|
+
export {
|
|
473
|
+
ApiReference,
|
|
474
|
+
SPEC_SOURCE_HEADER
|
|
475
|
+
};
|
|
476
|
+
//# sourceMappingURL=api-reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-reference.jsx"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { applySecurity, buildModel, loadSpec, loadSpecText, normalizeSpec, securityRequirements, serverUrl } from \"@oaspect/core\";\nimport { LocaleProvider, useAvailableLocales, useLocale, useLocalized, useSetLocale, useT } from \"./i18n/context\";\nimport { LOCALE_NAMES, directionOf, mergeMessages } from \"./i18n/index\";\nimport { LazyContext, scrollToAnchor } from \"./lazy\";\nimport { createStorage } from \"./storage\";\nimport Intro from \"./intro\";\nimport Markdown from \"./markdown\";\nimport Models from \"./models\";\nimport Operation from \"./operation\";\nimport Sidebar from \"./sidebar\";\nimport { ModelContext, SettingsContext, SpecContext } from \"./spec-context\";\nimport TryIt from \"./try-it\";\nimport { MenuIcon, MoonIcon, SunIcon } from \"./icons\";\n\n// Header a spec endpoint can send to say it served a fallback copy, e.g.\n// when the live document was unreachable. The viewer then shows a notice.\nexport const SPEC_SOURCE_HEADER = \"x-oaspect-spec-source\";\n\n// Errors are kept as translation keys (or { key, params }) and translated\n// when rendered, so they follow later language switches too.\n\nfunction LanguageToggle() {\n const locale = useLocale();\n const setLocale = useSetLocale();\n const locales = useAvailableLocales();\n const t = useT();\n\n return (\n // The native select sits invisibly on top: the button shows the short\n // code (\"TR\"), the opened list shows full language names.\n <label\n title={t(\"header.language\")}\n className=\"relative flex cursor-pointer items-center gap-1.5 rounded-lg border border-border px-2.5 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" className=\"size-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\" />\n </svg>\n <span className=\"uppercase\">{locale}</span>\n <select\n value={locale}\n onChange={(event) => setLocale(event.target.value)}\n aria-label={t(\"header.language\")}\n className=\"absolute inset-0 cursor-pointer opacity-0\"\n >\n {locales.map((item) => (\n <option key={item} value={item}>\n {LOCALE_NAMES[item] ?? item}\n </option>\n ))}\n </select>\n </label>\n );\n}\n\nfunction ThemeToggle({ theme, onChange }) {\n const t = useT();\n\n return (\n <button\n type=\"button\"\n onClick={() => onChange(theme === \"dark\" ? \"light\" : \"dark\")}\n aria-label={t(\"header.theme\")}\n title={t(\"header.theme\")}\n className=\"rounded-lg border border-border p-2 text-muted-foreground hover:text-foreground\"\n >\n <MoonIcon className=\"size-4 dark:hidden\" />\n <SunIcon className=\"hidden size-4 dark:block\" />\n </button>\n );\n}\n\nfunction SourceMenu({ source, defaultLabel, onUrl, onFile, onDefault }) {\n const [draft, setDraft] = useState(source.type === \"url\" ? source.url : \"\");\n const fileInput = useRef(null);\n const menu = useRef(null);\n const close = () => menu.current?.removeAttribute(\"open\");\n const t = useT();\n\n return (\n <details ref={menu} className=\"relative\">\n <summary\n title={t(\"source.title\")}\n className=\"flex cursor-pointer list-none items-center gap-1.5 rounded-lg border border-border px-2.5 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground [&::-webkit-details-marker]:hidden\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" className=\"size-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <path d=\"M14 2v6h6M10 13l-2 2 2 2M14 13l2 2-2 2\" />\n </svg>\n <span className=\"hidden sm:inline\">{t(\"source.title\")}</span>\n </summary>\n <div className=\"absolute end-0 z-40 mt-2 w-80 space-y-3 rounded-xl border border-border bg-background p-4 shadow-xl\">\n <button\n type=\"button\"\n onClick={() => {\n onDefault();\n close();\n }}\n className={`block w-full rounded-lg border px-3 py-2 text-start text-sm ${\n source.type === \"default\" ? \"border-primary text-primary\" : \"border-border hover:border-primary\"\n }`}\n >\n {t(\"source.default\")}\n {defaultLabel && <span className=\"block truncate font-mono text-[11px] text-muted-foreground\">{defaultLabel}</span>}\n </button>\n <form\n onSubmit={(event) => {\n event.preventDefault();\n if (draft.trim()) {\n onUrl(draft.trim());\n close();\n }\n }}\n className=\"space-y-2\"\n >\n <input\n type=\"url\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n placeholder=\"https://…/swagger.json\"\n dir=\"ltr\"\n className=\"w-full rounded-lg border border-border bg-background px-3 py-1.5 font-mono text-xs outline-none focus:border-primary\"\n />\n <button type=\"submit\" className=\"w-full rounded-lg border border-border px-3 py-1.5 text-sm hover:border-primary hover:text-primary\">\n {t(\"source.loadUrl\")}\n </button>\n </form>\n <button\n type=\"button\"\n onClick={() => fileInput.current?.click()}\n className=\"w-full rounded-lg border border-dashed border-border px-3 py-2 text-sm hover:border-primary hover:text-primary\"\n >\n {source.type === \"file\" ? t(\"source.file\", { name: source.name }) : t(\"source.chooseFile\")}\n </button>\n <input\n ref={fileInput}\n type=\"file\"\n accept=\".json,.yaml,.yml,application/json,application/yaml,text/yaml\"\n className=\"hidden\"\n onChange={(event) => {\n const file = event.target.files?.[0];\n event.target.value = \"\";\n if (file) {\n onFile(file);\n close();\n }\n }}\n />\n </div>\n </details>\n );\n}\n\n// Loads a referenced document for external $refs.\nasync function readText(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(`${url} → HTTP ${response.status}`);\n return response.text();\n}\n\nfunction systemTheme() {\n try {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n } catch {\n return \"light\";\n }\n}\n\nconst LAZY_THRESHOLD = 40;\nconst EAGER_OPERATIONS = 8;\n\nconst DEFAULT_FEATURES = {\n sourceMenu: true,\n languageSwitcher: true,\n themeToggle: true,\n tryIt: true,\n models: true,\n};\n\nfunction Viewer({\n spec: inlineSpec,\n specUrl,\n title,\n logo,\n storagePrefix,\n proxyUrl,\n features: featureOverrides,\n defaultSnippet,\n urlParam,\n theme: themeProp,\n lazy,\n}) {\n const features = { ...DEFAULT_FEATURES, ...featureOverrides };\n const storage = useMemo(() => createStorage(storagePrefix), [storagePrefix]);\n const rootRef = useRef(null);\n const locale = useLocale();\n const [nestedInMain, setNestedInMain] = useState(false);\n useEffect(() => {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- reads the host DOM once\n setNestedInMain(Boolean(rootRef.current?.parentElement?.closest(\"main, [role=main]\")));\n }, []);\n\n const [source, setSource] = useState({ type: \"pending\" });\n // Inline documents are normalized too (Swagger 2.0 → OpenAPI 3).\n const normalizedInline = useMemo(() => (inlineSpec ? normalizeSpec(inlineSpec) : null), [inlineSpec]);\n const [spec, setSpec] = useState(normalizedInline);\n const [error, setError] = useState(\"\");\n // True when the spec endpoint answered with SPEC_SOURCE_HEADER: fallback.\n const [usingFallback, setUsingFallback] = useState(false);\n const [activeAnchor, setActiveAnchor] = useState(\"introduction\");\n const [sidebarOpen, setSidebarOpen] = useState(false);\n const [tryOperation, setTryOperation] = useState(null);\n const searchRef = useRef(null);\n const t = useT();\n const localized = useLocalized();\n\n const [serverIndex, setServerIndex] = useState(0);\n const [customServer, setCustomServer] = useState(\"\");\n // Server variable values per server index: { 0: { region: \"us\" } }.\n const [serverVariables, setServerVariables] = useState({});\n const [token, setToken] = useState(\"\");\n // Credentials per security scheme name (components.securitySchemes).\n const [credentials, setCredentials] = useState({});\n const [language, setLanguage] = useState(defaultSnippet);\n // null until mounted: the host's own data-theme (if any) applies meanwhile,\n // so a server-rendered page does not flash the wrong theme.\n const [theme, setTheme] = useState(themeProp ?? null);\n\n // Restore per-browser preferences and the ?url= source after hydration.\n useEffect(() => {\n /* eslint-disable react-hooks/set-state-in-effect -- one-time sync from browser-only state */\n setToken(storage.get(\"token\", \"\"));\n try {\n setCredentials(JSON.parse(storage.get(\"credentials\", \"{}\")) ?? {});\n } catch {\n // Ignore unreadable stored credentials.\n }\n setLanguage(storage.get(\"language\", defaultSnippet));\n if (!themeProp) setTheme(storage.get(\"theme\") ?? document.documentElement.dataset.theme ?? systemTheme());\n const url = urlParam ? new URLSearchParams(window.location.search).get(urlParam) : null;\n setSource(url ? { type: \"url\", url } : { type: \"default\" });\n /* eslint-enable react-hooks/set-state-in-effect */\n }, [storage, defaultSnippet, urlParam, themeProp]);\n\n useEffect(() => storage.set(\"token\", token), [storage, token]);\n useEffect(() => storage.set(\"credentials\", Object.keys(credentials).length ? JSON.stringify(credentials) : null), [storage, credentials]);\n useEffect(() => storage.set(\"language\", language), [storage, language]);\n\n function changeTheme(next) {\n setTheme(next);\n storage.set(\"theme\", next);\n // Hosts that theme <html data-theme> (e.g. with a no-flash script) stay in\n // sync, otherwise their attribute would keep matching the dark: variant.\n if (document.documentElement.hasAttribute(\"data-theme\")) document.documentElement.dataset.theme = next;\n }\n\n // Inline specs need no fetching; specUrl and ?url= sources do. File\n // sources are loaded directly in loadFile().\n useEffect(() => {\n if (source.type === \"default\" && normalizedInline) {\n /* eslint-disable react-hooks/set-state-in-effect -- adopting a new inline spec */\n setSpec(normalizedInline);\n setUsingFallback(false);\n setError(\"\");\n /* eslint-enable react-hooks/set-state-in-effect */\n return;\n }\n if (source.type !== \"default\" && source.type !== \"url\") return;\n const url = source.type === \"url\" ? source.url : specUrl;\n if (!url) return;\n let cancelled = false;\n\n if (urlParam) {\n const params = new URLSearchParams(window.location.search);\n if (source.type === \"url\") params.set(urlParam, url);\n else params.delete(urlParam);\n const search = params.toString();\n window.history.replaceState(null, \"\", `${window.location.pathname}${search ? `?${search}` : \"\"}${window.location.hash}`);\n }\n\n fetch(url)\n .then(async (response) => {\n if (!response.ok) throw new Error(`${url} → HTTP ${response.status}`);\n return { text: await response.text(), fallback: response.headers.get(SPEC_SOURCE_HEADER) === \"fallback\" };\n })\n .then(async ({ text, fallback }) => {\n // JSON or YAML; Swagger 2.0 is converted to OpenAPI 3; external\n // $refs resolve relative to the document's URL.\n const json = await loadSpec(text, { baseUrl: new URL(url, window.location.href).href, read: readText });\n if (!cancelled) {\n setSpec(json);\n setUsingFallback(fallback);\n setError(\"\");\n }\n })\n .catch((err) => !cancelled && setError(err.message));\n\n return () => {\n cancelled = true;\n };\n }, [source, specUrl, normalizedInline, urlParam]);\n\n async function loadFile(file) {\n try {\n const json = loadSpecText(await file.text());\n setSource({ type: \"file\", name: file.name });\n setSpec(json);\n setUsingFallback(false);\n setError(\"\");\n } catch (err) {\n setError({ key: \"spec.unreadableFile\", params: { name: file.name, error: err.message } });\n }\n }\n\n const model = useMemo(() => (spec ? buildModel(spec) : null), [spec]);\n // \"auto\": render sections lazily only for documents large enough to need it.\n const lazyEnabled = lazy === true || (lazy === \"auto\" && (model?.operations.length ?? 0) > LAZY_THRESHOLD);\n // The first operations render eagerly so the top of the page is complete.\n const eagerAnchors = useMemo(() => new Set(model?.operations.slice(0, EAGER_OPERATIONS).map((operation) => operation.anchor)), [model]);\n\n // Reset the server choice whenever a new spec is loaded.\n useEffect(() => {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- derived reset on spec change\n setServerIndex(0);\n }, [model]);\n\n const schemes = useMemo(() => model?.securitySchemes ?? {}, [model]);\n\n const settings = useMemo(() => {\n const servers = model?.servers ?? [];\n const selected = serverIndex === -1 ? { url: customServer } : servers[serverIndex] ?? servers[0];\n const variableValues = serverVariables[serverIndex] ?? {};\n // Variables are resolved here so requests and samples see a plain URL.\n const server = selected ? { url: serverUrl(selected, variableValues) } : undefined;\n return {\n servers,\n server,\n selectedServer: selected,\n variableValues,\n setVariable: (name, value) =>\n setServerVariables((current) => ({ ...current, [serverIndex]: { ...current[serverIndex], [name]: value } })),\n serverIndex,\n setServerIndex,\n customServer,\n setCustomServer,\n token,\n setToken,\n credentials,\n setCredential: (name, patch) => setCredentials((current) => ({ ...current, [name]: { ...current[name], ...patch } })),\n securitySchemes: schemes,\n // Headers, query parameters and cookies an operation needs. Without\n // securitySchemes in the document, the generic Bearer token applies.\n authFor: (operation) =>\n Object.keys(schemes).length\n ? applySecurity(schemes, securityRequirements(operation), credentials)\n : { headers: token ? { Authorization: `Bearer ${token}` } : {}, query: {}, cookies: {} },\n language,\n setLanguage,\n proxyUrl,\n storage,\n features,\n };\n // features is rebuilt each render from props; its fields are what matter.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [model, schemes, serverIndex, customServer, serverVariables, token, credentials, language, proxyUrl, storage, features.tryIt]);\n\n // Scroll spy: the section crossing the band under the header is active.\n useEffect(() => {\n if (!model || !rootRef.current) return;\n const observer = new IntersectionObserver(\n (entries) => {\n const visible = entries.filter((entry) => entry.isIntersecting);\n if (visible.length) setActiveAnchor(visible[visible.length - 1].target.id);\n },\n { rootMargin: \"-72px 0px -70% 0px\" },\n );\n rootRef.current.querySelectorAll(\"[data-anchor]\").forEach((node) => observer.observe(node));\n\n const target = decodeURIComponent(window.location.hash.slice(1));\n if (target) scrollToAnchor(target);\n\n // Anchor links jump to a section whose neighbours may still render;\n // keep the target in place while they do.\n const onHash = () => scrollToAnchor(decodeURIComponent(window.location.hash.slice(1)));\n window.addEventListener(\"hashchange\", onHash);\n\n return () => {\n observer.disconnect();\n window.removeEventListener(\"hashchange\", onHash);\n };\n }, [model]);\n\n // ⌘K / Ctrl+K focuses the sidebar search.\n useEffect(() => {\n function onKey(event) {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"k\") {\n event.preventDefault();\n setSidebarOpen(true);\n requestAnimationFrame(() => searchRef.current?.focus());\n }\n }\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, []);\n\n const closeSidebar = useCallback(() => setSidebarOpen(false), []);\n const openTry = useCallback((operation) => setTryOperation(operation), []);\n const heading = title ?? (model ? localized(model.info, \"title\") : \"\");\n\n return (\n <SpecContext.Provider value={spec}>\n <ModelContext.Provider value={model}>\n <SettingsContext.Provider value={settings}>\n {/* .oaspect scopes the stylesheet and carries theme, direction and\n language; layout utilities live on the inner element because\n scoped selectors only match descendants. */}\n <div ref={rootRef} className=\"oaspect\" data-theme={theme ?? undefined} dir={directionOf(locale)} lang={locale}>\n <div className=\"min-h-dvh bg-background text-foreground\">\n <header className=\"sticky top-0 z-30 flex h-14 items-center gap-3 border-b border-border bg-background/85 px-4 backdrop-blur\">\n <a\n href=\"#oaspect-content\"\n className=\"absolute start-2 top-2 z-50 -translate-y-20 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground focus:translate-y-0\"\n >\n {t(\"header.skip\")}\n </a>\n <button\n type=\"button\"\n onClick={() => setSidebarOpen(!sidebarOpen)}\n aria-label={t(\"header.menu\")}\n className=\"rounded-lg border border-border p-2 lg:hidden\"\n >\n <MenuIcon className=\"size-4\" />\n </button>\n <a href=\"#introduction\" className=\"flex min-w-0 items-center gap-2\">\n {logo ?? <span className=\"truncate text-sm font-semibold\">{heading}</span>}\n </a>\n {model && logo && (\n <span className=\"hidden truncate text-sm text-muted-foreground md:inline\">\n {heading} <span className=\"font-mono text-xs\">v{model.info.version}</span>\n </span>\n )}\n <div className=\"ms-auto flex items-center gap-2\">\n {features.sourceMenu && source.type !== \"pending\" && (\n <SourceMenu\n key={`${source.type}:${source.url ?? source.name ?? \"\"}`}\n source={source}\n defaultLabel={specUrl}\n onDefault={() => setSource({ type: \"default\" })}\n onUrl={(url) => setSource({ type: \"url\", url })}\n onFile={loadFile}\n />\n )}\n {features.languageSwitcher && <LanguageToggle />}\n {features.themeToggle && <ThemeToggle theme={theme} onChange={changeTheme} />}\n </div>\n </header>\n\n {error && (\n <div className=\"border-b border-rose-500/30 bg-rose-500/10 px-4 py-2 text-sm text-rose-700 dark:text-rose-300\">\n {t(\"spec.loadFailed\", { error: typeof error === \"string\" ? t(error) : t(error.key, error.params) })}\n </div>\n )}\n\n {usingFallback && source.type === \"default\" && (\n <div className=\"border-b border-amber-500/30 bg-amber-500/10 px-4 py-2 text-sm text-amber-800 dark:text-amber-200\">\n {t(\"spec.fallback\")}\n </div>\n )}\n\n {!model ? (\n !error && <p className=\"p-10 text-center text-sm text-muted-foreground\">{t(\"spec.loading\")}</p>\n ) : (\n <div className=\"flex\">\n <aside\n className={`fixed inset-y-0 start-0 top-14 z-20 w-72 shrink-0 border-e border-border bg-card transition-transform lg:sticky lg:h-[calc(100dvh-3.5rem)] lg:translate-x-0 lg:rtl:translate-x-0 ${\n sidebarOpen ? \"translate-x-0\" : \"-translate-x-full rtl:translate-x-full\"\n }`}\n >\n <Sidebar model={model} activeAnchor={activeAnchor} searchRef={searchRef} onNavigate={closeSidebar} showModels={features.models} />\n </aside>\n {sidebarOpen && (\n <div className=\"fixed inset-0 top-14 z-10 bg-black/30 lg:hidden\" onClick={closeSidebar} aria-hidden=\"true\" />\n )}\n\n <LazyContext.Provider value={lazyEnabled}>\n {/* role=\"main\" unless the host page already has a main landmark around us. */}\n <div id=\"oaspect-content\" tabIndex={-1} role={nestedInMain ? \"region\" : \"main\"} aria-label={nestedInMain ? heading : undefined} className=\"min-w-0 flex-1 px-4 outline-none sm:px-8 xl:px-12\">\n <div className=\"mx-auto max-w-[88rem]\">\n <Intro spec={spec} model={model} />\n {model.tags.map((tag) => (\n <section key={tag.name} id={tag.anchor} data-anchor className=\"pt-14\">\n <h2 className=\"text-3xl font-bold tracking-tight [overflow-wrap:anywhere]\">{localized(tag, \"name\")}</h2>\n <Markdown source={localized(tag, \"description\")} className=\"mt-3 max-w-3xl text-muted-foreground\" />\n {tag.operations.map((operation) => (\n <Operation\n key={operation.anchor}\n operation={operation}\n onTry={features.tryIt ? openTry : null}\n eager={eagerAnchors.has(operation.anchor)}\n />\n ))}\n </section>\n ))}\n {model.webhooks.length > 0 && (\n <section id=\"webhooks\" data-anchor className=\"pt-14\">\n <h2 className=\"text-3xl font-bold tracking-tight\">{t(\"webhooks.title\")}</h2>\n <p className=\"mt-3 max-w-3xl text-sm text-muted-foreground\">{t(\"webhooks.hint\")}</p>\n {model.webhooks.map((operation) => (\n <Operation key={operation.anchor} operation={operation} onTry={null} />\n ))}\n </section>\n )}\n {features.models && <Models schemas={model.schemas} />}\n </div>\n </div>\n </LazyContext.Provider>\n </div>\n )}\n\n {tryOperation && <TryIt key={tryOperation.anchor} operation={tryOperation} onClose={() => setTryOperation(null)} />}\n </div>\n </div>\n </SettingsContext.Provider>\n </ModelContext.Provider>\n </SpecContext.Provider>\n );\n}\n\n/**\n * Interactive API reference for an OpenAPI 3.x document.\n *\n * @param {import(\"./index\").ApiReferenceProps} props\n */\nexport function ApiReference({\n spec,\n specUrl,\n title,\n logo,\n locale,\n defaultLocale = \"en\",\n onLocaleChange,\n messages,\n storagePrefix = \"oaspect\",\n proxyUrl = null,\n features,\n defaultSnippet = \"shell:curl\",\n urlParam = \"url\",\n theme,\n lazy = \"auto\",\n}) {\n const merged = useMemo(() => mergeMessages(messages), [messages]);\n\n return (\n <LocaleProvider locale={locale} defaultLocale={defaultLocale} onLocaleChange={onLocaleChange} messages={merged}>\n <Viewer\n spec={spec}\n specUrl={specUrl}\n title={title}\n logo={logo}\n storagePrefix={storagePrefix}\n proxyUrl={proxyUrl}\n features={features}\n defaultSnippet={defaultSnippet}\n urlParam={urlParam}\n theme={theme}\n lazy={lazy}\n />\n </LocaleProvider>\n );\n}\n"],"mappings":"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","names":[]}
|