vector-framework 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/dist/auth/protected.d.ts +4 -4
- package/dist/auth/protected.d.ts.map +1 -1
- package/dist/auth/protected.js +10 -7
- package/dist/auth/protected.js.map +1 -1
- package/dist/cache/manager.d.ts +2 -0
- package/dist/cache/manager.d.ts.map +1 -1
- package/dist/cache/manager.js +21 -4
- package/dist/cache/manager.js.map +1 -1
- package/dist/checkpoint/artifacts/compressor.d.ts +5 -0
- package/dist/checkpoint/artifacts/compressor.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/compressor.js +24 -0
- package/dist/checkpoint/artifacts/compressor.js.map +1 -0
- package/dist/checkpoint/artifacts/decompress-worker.d.ts +2 -0
- package/dist/checkpoint/artifacts/decompress-worker.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/decompress-worker.js +31 -0
- package/dist/checkpoint/artifacts/decompress-worker.js.map +1 -0
- package/dist/checkpoint/artifacts/hasher.d.ts +2 -0
- package/dist/checkpoint/artifacts/hasher.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/hasher.js +7 -0
- package/dist/checkpoint/artifacts/hasher.js.map +1 -0
- package/dist/checkpoint/artifacts/manifest.d.ts +6 -0
- package/dist/checkpoint/artifacts/manifest.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/manifest.js +55 -0
- package/dist/checkpoint/artifacts/manifest.js.map +1 -0
- package/dist/checkpoint/artifacts/materializer.d.ts +16 -0
- package/dist/checkpoint/artifacts/materializer.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/materializer.js +168 -0
- package/dist/checkpoint/artifacts/materializer.js.map +1 -0
- package/dist/checkpoint/artifacts/packager.d.ts +12 -0
- package/dist/checkpoint/artifacts/packager.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/packager.js +82 -0
- package/dist/checkpoint/artifacts/packager.js.map +1 -0
- package/dist/checkpoint/artifacts/repository.d.ts +11 -0
- package/dist/checkpoint/artifacts/repository.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/repository.js +29 -0
- package/dist/checkpoint/artifacts/repository.js.map +1 -0
- package/dist/checkpoint/artifacts/store.d.ts +13 -0
- package/dist/checkpoint/artifacts/store.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/store.js +85 -0
- package/dist/checkpoint/artifacts/store.js.map +1 -0
- package/dist/checkpoint/artifacts/types.d.ts +21 -0
- package/dist/checkpoint/artifacts/types.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/types.js +2 -0
- package/dist/checkpoint/artifacts/types.js.map +1 -0
- package/dist/checkpoint/artifacts/worker-decompressor.d.ts +17 -0
- package/dist/checkpoint/artifacts/worker-decompressor.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/worker-decompressor.js +148 -0
- package/dist/checkpoint/artifacts/worker-decompressor.js.map +1 -0
- package/dist/checkpoint/asset-store.d.ts +10 -0
- package/dist/checkpoint/asset-store.d.ts.map +1 -0
- package/dist/checkpoint/asset-store.js +46 -0
- package/dist/checkpoint/asset-store.js.map +1 -0
- package/dist/checkpoint/bundler.d.ts +15 -0
- package/dist/checkpoint/bundler.d.ts.map +1 -0
- package/dist/checkpoint/bundler.js +45 -0
- package/dist/checkpoint/bundler.js.map +1 -0
- package/dist/checkpoint/cli.d.ts +2 -0
- package/dist/checkpoint/cli.d.ts.map +1 -0
- package/dist/checkpoint/cli.js +157 -0
- package/dist/checkpoint/cli.js.map +1 -0
- package/dist/checkpoint/entrypoint-generator.d.ts +17 -0
- package/dist/checkpoint/entrypoint-generator.d.ts.map +1 -0
- package/dist/checkpoint/entrypoint-generator.js +251 -0
- package/dist/checkpoint/entrypoint-generator.js.map +1 -0
- package/dist/checkpoint/forwarder.d.ts +6 -0
- package/dist/checkpoint/forwarder.d.ts.map +1 -0
- package/dist/checkpoint/forwarder.js +74 -0
- package/dist/checkpoint/forwarder.js.map +1 -0
- package/dist/checkpoint/gateway.d.ts +11 -0
- package/dist/checkpoint/gateway.d.ts.map +1 -0
- package/dist/checkpoint/gateway.js +30 -0
- package/dist/checkpoint/gateway.js.map +1 -0
- package/dist/checkpoint/ipc.d.ts +12 -0
- package/dist/checkpoint/ipc.d.ts.map +1 -0
- package/dist/checkpoint/ipc.js +96 -0
- package/dist/checkpoint/ipc.js.map +1 -0
- package/dist/checkpoint/manager.d.ts +20 -0
- package/dist/checkpoint/manager.d.ts.map +1 -0
- package/dist/checkpoint/manager.js +214 -0
- package/dist/checkpoint/manager.js.map +1 -0
- package/dist/checkpoint/process-manager.d.ts +35 -0
- package/dist/checkpoint/process-manager.d.ts.map +1 -0
- package/dist/checkpoint/process-manager.js +203 -0
- package/dist/checkpoint/process-manager.js.map +1 -0
- package/dist/checkpoint/resolver.d.ts +25 -0
- package/dist/checkpoint/resolver.d.ts.map +1 -0
- package/dist/checkpoint/resolver.js +95 -0
- package/dist/checkpoint/resolver.js.map +1 -0
- package/dist/checkpoint/socket-path.d.ts +2 -0
- package/dist/checkpoint/socket-path.d.ts.map +1 -0
- package/dist/checkpoint/socket-path.js +51 -0
- package/dist/checkpoint/socket-path.js.map +1 -0
- package/dist/checkpoint/types.d.ts +54 -0
- package/dist/checkpoint/types.d.ts.map +1 -0
- package/dist/checkpoint/types.js +2 -0
- package/dist/checkpoint/types.js.map +1 -0
- package/dist/cli/index.js +10 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/option-resolution.d.ts +1 -1
- package/dist/cli/option-resolution.d.ts.map +1 -1
- package/dist/cli/option-resolution.js.map +1 -1
- package/dist/cli.js +3709 -328
- package/dist/core/config-loader.d.ts +1 -0
- package/dist/core/config-loader.d.ts.map +1 -1
- package/dist/core/config-loader.js +10 -2
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/router.d.ts +24 -3
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +398 -249
- package/dist/core/router.js.map +1 -1
- package/dist/core/server.d.ts +2 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +22 -8
- package/dist/core/server.js.map +1 -1
- package/dist/core/vector.d.ts +3 -0
- package/dist/core/vector.d.ts.map +1 -1
- package/dist/core/vector.js +51 -1
- package/dist/core/vector.js.map +1 -1
- package/dist/dev/route-scanner.d.ts.map +1 -1
- package/dist/dev/route-scanner.js +2 -1
- package/dist/dev/route-scanner.js.map +1 -1
- package/dist/http.d.ts +32 -7
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +144 -13
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +1297 -74
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1296 -73
- package/dist/middleware/manager.d.ts +3 -3
- package/dist/middleware/manager.d.ts.map +1 -1
- package/dist/middleware/manager.js +9 -8
- package/dist/middleware/manager.js.map +1 -1
- package/dist/openapi/docs-ui.d.ts.map +1 -1
- package/dist/openapi/docs-ui.js +1097 -61
- package/dist/openapi/docs-ui.js.map +1 -1
- package/dist/openapi/generator.d.ts +2 -1
- package/dist/openapi/generator.d.ts.map +1 -1
- package/dist/openapi/generator.js +240 -7
- package/dist/openapi/generator.js.map +1 -1
- package/dist/types/index.d.ts +71 -28
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +24 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +3 -2
- package/dist/utils/validation.js.map +1 -1
- package/package.json +2 -1
- package/src/auth/protected.ts +11 -8
- package/src/cache/manager.ts +23 -4
- package/src/checkpoint/artifacts/compressor.ts +30 -0
- package/src/checkpoint/artifacts/decompress-worker.ts +49 -0
- package/src/checkpoint/artifacts/hasher.ts +6 -0
- package/src/checkpoint/artifacts/manifest.ts +72 -0
- package/src/checkpoint/artifacts/materializer.ts +211 -0
- package/src/checkpoint/artifacts/packager.ts +100 -0
- package/src/checkpoint/artifacts/repository.ts +36 -0
- package/src/checkpoint/artifacts/store.ts +102 -0
- package/src/checkpoint/artifacts/types.ts +24 -0
- package/src/checkpoint/artifacts/worker-decompressor.ts +192 -0
- package/src/checkpoint/asset-store.ts +61 -0
- package/src/checkpoint/bundler.ts +64 -0
- package/src/checkpoint/cli.ts +177 -0
- package/src/checkpoint/entrypoint-generator.ts +275 -0
- package/src/checkpoint/forwarder.ts +84 -0
- package/src/checkpoint/gateway.ts +40 -0
- package/src/checkpoint/ipc.ts +107 -0
- package/src/checkpoint/manager.ts +254 -0
- package/src/checkpoint/process-manager.ts +250 -0
- package/src/checkpoint/resolver.ts +124 -0
- package/src/checkpoint/socket-path.ts +61 -0
- package/src/checkpoint/types.ts +63 -0
- package/src/cli/index.ts +11 -2
- package/src/cli/option-resolution.ts +5 -1
- package/src/core/config-loader.ts +11 -2
- package/src/core/router.ts +505 -264
- package/src/core/server.ts +36 -9
- package/src/core/vector.ts +60 -1
- package/src/dev/route-scanner.ts +2 -1
- package/src/http.ts +219 -19
- package/src/index.ts +3 -2
- package/src/middleware/manager.ts +10 -10
- package/src/openapi/docs-ui.ts +1097 -61
- package/src/openapi/generator.ts +265 -6
- package/src/types/index.ts +83 -30
- package/src/utils/validation.ts +5 -3
package/dist/openapi/docs-ui.js
CHANGED
|
@@ -133,6 +133,124 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
133
133
|
.dark .json-number { color: #7dc9ff; }
|
|
134
134
|
.dark .json-boolean { color: #93a4bf; }
|
|
135
135
|
.dark .json-null { color: #7c8ba3; }
|
|
136
|
+
.param-row {
|
|
137
|
+
--param-row-bg-rgb: 255 255 255;
|
|
138
|
+
}
|
|
139
|
+
.dark .param-row {
|
|
140
|
+
--param-row-bg-rgb: 10 10 10;
|
|
141
|
+
}
|
|
142
|
+
.param-row-head {
|
|
143
|
+
display: grid;
|
|
144
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
145
|
+
align-items: center;
|
|
146
|
+
gap: 0.5rem;
|
|
147
|
+
min-width: 0;
|
|
148
|
+
width: 100%;
|
|
149
|
+
}
|
|
150
|
+
.param-row-main {
|
|
151
|
+
min-width: 0;
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
gap: 0.4rem;
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
}
|
|
157
|
+
.param-tooltip-trigger {
|
|
158
|
+
border: 0;
|
|
159
|
+
margin: 0;
|
|
160
|
+
padding: 0;
|
|
161
|
+
background: transparent;
|
|
162
|
+
color: inherit;
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
font: inherit;
|
|
165
|
+
text-align: left;
|
|
166
|
+
min-width: 0;
|
|
167
|
+
}
|
|
168
|
+
.param-tooltip-trigger:focus-visible {
|
|
169
|
+
outline: 2px solid rgba(0, 161, 255, 0.65);
|
|
170
|
+
outline-offset: 2px;
|
|
171
|
+
border-radius: 0.375rem;
|
|
172
|
+
}
|
|
173
|
+
.param-name-trigger {
|
|
174
|
+
min-width: 0;
|
|
175
|
+
flex: 1 1 auto;
|
|
176
|
+
overflow: hidden;
|
|
177
|
+
}
|
|
178
|
+
.param-name-text {
|
|
179
|
+
display: block;
|
|
180
|
+
max-width: 100%;
|
|
181
|
+
overflow: hidden;
|
|
182
|
+
text-overflow: ellipsis;
|
|
183
|
+
white-space: nowrap;
|
|
184
|
+
mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 18px), transparent 100%);
|
|
185
|
+
-webkit-mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 18px), transparent 100%);
|
|
186
|
+
}
|
|
187
|
+
.param-type-fade {
|
|
188
|
+
position: relative;
|
|
189
|
+
z-index: 1;
|
|
190
|
+
display: block;
|
|
191
|
+
max-width: none;
|
|
192
|
+
overflow: visible;
|
|
193
|
+
text-overflow: clip;
|
|
194
|
+
padding-left: 1.25rem;
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
text-align: left;
|
|
197
|
+
justify-self: end;
|
|
198
|
+
background: linear-gradient(
|
|
199
|
+
90deg,
|
|
200
|
+
rgba(var(--param-row-bg-rgb), 0) 0%,
|
|
201
|
+
rgba(var(--param-row-bg-rgb), 0.76) 36%,
|
|
202
|
+
rgba(var(--param-row-bg-rgb), 0.94) 68%,
|
|
203
|
+
rgba(var(--param-row-bg-rgb), 1) 100%
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
#param-value-tooltip {
|
|
207
|
+
position: fixed;
|
|
208
|
+
top: 0;
|
|
209
|
+
left: 0;
|
|
210
|
+
z-index: 70;
|
|
211
|
+
width: min(42rem, calc(100vw - 0.75rem));
|
|
212
|
+
border-radius: 0.5rem;
|
|
213
|
+
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
214
|
+
background: rgba(255, 255, 255, 0.92);
|
|
215
|
+
color: #111111;
|
|
216
|
+
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.14);
|
|
217
|
+
backdrop-filter: blur(12px) saturate(145%);
|
|
218
|
+
-webkit-backdrop-filter: blur(12px) saturate(145%);
|
|
219
|
+
padding: 0.4rem 0.6rem;
|
|
220
|
+
opacity: 0;
|
|
221
|
+
pointer-events: none;
|
|
222
|
+
transform: translateY(6px) scale(0.98);
|
|
223
|
+
transition:
|
|
224
|
+
opacity var(--motion-fast) var(--motion-ease),
|
|
225
|
+
transform var(--motion-fast) var(--motion-ease);
|
|
226
|
+
}
|
|
227
|
+
#param-value-tooltip.is-visible {
|
|
228
|
+
opacity: 1;
|
|
229
|
+
pointer-events: auto;
|
|
230
|
+
transform: translateY(0) scale(1);
|
|
231
|
+
}
|
|
232
|
+
.dark #param-value-tooltip {
|
|
233
|
+
border-color: rgba(148, 163, 184, 0.24);
|
|
234
|
+
background: rgba(17, 17, 17, 0.9);
|
|
235
|
+
color: #ededed;
|
|
236
|
+
box-shadow: 0 14px 30px rgba(0, 0, 0, 0.45);
|
|
237
|
+
}
|
|
238
|
+
#param-tooltip-line {
|
|
239
|
+
margin: 0;
|
|
240
|
+
font-size: 11px;
|
|
241
|
+
line-height: 1.3;
|
|
242
|
+
font-family: "JetBrains Mono", monospace;
|
|
243
|
+
white-space: normal;
|
|
244
|
+
word-break: break-word;
|
|
245
|
+
}
|
|
246
|
+
#param-tooltip-description {
|
|
247
|
+
margin: 0.2rem 0 0;
|
|
248
|
+
font-size: 11px;
|
|
249
|
+
line-height: 1.3;
|
|
250
|
+
opacity: 0.8;
|
|
251
|
+
white-space: normal;
|
|
252
|
+
word-break: break-word;
|
|
253
|
+
}
|
|
136
254
|
</style>
|
|
137
255
|
</head>
|
|
138
256
|
<body class="bg-light-bg text-light-text dark:bg-dark-bg dark:text-dark-text font-sans antialiased flex h-screen overflow-hidden">
|
|
@@ -162,6 +280,16 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
162
280
|
/>
|
|
163
281
|
</div>
|
|
164
282
|
</div>
|
|
283
|
+
<div id="auth-panel" class="border-b border-light-border dark:border-dark-border">
|
|
284
|
+
<button id="auth-toggle" class="w-full flex items-center justify-between px-4 py-2.5 text-xs font-semibold uppercase tracking-wider opacity-60 hover:opacity-100 transition-opacity">
|
|
285
|
+
<span class="flex items-center gap-1.5">
|
|
286
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
|
|
287
|
+
Auth
|
|
288
|
+
</span>
|
|
289
|
+
<svg id="auth-chevron" class="w-3.5 h-3.5 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg>
|
|
290
|
+
</button>
|
|
291
|
+
<div id="auth-fields" class="px-4 pb-3 space-y-2"></div>
|
|
292
|
+
</div>
|
|
165
293
|
<nav class="flex-1 overflow-y-auto px-3 py-2 space-y-6 text-sm" id="sidebar-nav"></nav>
|
|
166
294
|
</aside>
|
|
167
295
|
|
|
@@ -195,6 +323,9 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
195
323
|
<span id="endpoint-method" class="px-2.5 py-0.5 rounded-full text-xs font-mono font-medium"></span>
|
|
196
324
|
<h2 class="text-xl font-semibold tracking-tight" id="endpoint-title">Operation</h2>
|
|
197
325
|
</div>
|
|
326
|
+
<div id="deprecated-banner" class="hidden mb-4 px-3 py-2 rounded border border-amber-300 dark:border-amber-700 bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-400 text-xs font-medium">
|
|
327
|
+
! This operation is deprecated
|
|
328
|
+
</div>
|
|
198
329
|
<p class="text-sm opacity-80 mb-8 font-mono" id="endpoint-path">/</p>
|
|
199
330
|
<div class="grid grid-cols-1 lg:grid-cols-12 gap-10">
|
|
200
331
|
<div class="lg:col-span-5 space-y-8" id="params-column"></div>
|
|
@@ -290,6 +421,10 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
290
421
|
<pre id="expand-viewer" class="hidden w-full h-[70vh] text-sm p-3 rounded border border-light-border dark:border-dark-border bg-light-bg dark:bg-dark-bg overflow-auto font-mono"></pre>
|
|
291
422
|
</div>
|
|
292
423
|
</div>
|
|
424
|
+
<div id="param-value-tooltip" aria-hidden="true" role="tooltip">
|
|
425
|
+
<p id="param-tooltip-line"></p>
|
|
426
|
+
<p id="param-tooltip-description" class="hidden"></p>
|
|
427
|
+
</div>
|
|
293
428
|
|
|
294
429
|
<script>
|
|
295
430
|
const spec = ${specJson};
|
|
@@ -360,14 +495,71 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
360
495
|
return ops;
|
|
361
496
|
}
|
|
362
497
|
|
|
498
|
+
const AUTH_STATE_KEY = "vector-docs-auth-v1";
|
|
499
|
+
const AUTH_SELECTION_KEY = "vector-docs-auth-selection-v1";
|
|
500
|
+
const HEADERS_STATE_KEY = "vector-docs-headers-v1";
|
|
501
|
+
|
|
502
|
+
function loadSavedHeaders() {
|
|
503
|
+
try {
|
|
504
|
+
const raw = localStorage.getItem(HEADERS_STATE_KEY);
|
|
505
|
+
if (raw) {
|
|
506
|
+
const parsed = JSON.parse(raw);
|
|
507
|
+
if (Array.isArray(parsed) && parsed.length > 0) return parsed;
|
|
508
|
+
}
|
|
509
|
+
} catch {}
|
|
510
|
+
return [{ key: "", value: "" }];
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function saveHeaders() {
|
|
514
|
+
try { localStorage.setItem(HEADERS_STATE_KEY, JSON.stringify(requestHeaders)); } catch {}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function loadAuthState() {
|
|
518
|
+
try {
|
|
519
|
+
const raw = localStorage.getItem(AUTH_STATE_KEY);
|
|
520
|
+
if (raw) return JSON.parse(raw);
|
|
521
|
+
} catch {}
|
|
522
|
+
return {};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function saveAuthState() {
|
|
526
|
+
try { localStorage.setItem(AUTH_STATE_KEY, JSON.stringify(authState)); } catch {}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function loadAuthSelectionState() {
|
|
530
|
+
try {
|
|
531
|
+
const raw = localStorage.getItem(AUTH_SELECTION_KEY);
|
|
532
|
+
if (raw) {
|
|
533
|
+
const parsed = JSON.parse(raw);
|
|
534
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
535
|
+
return parsed;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
} catch {}
|
|
539
|
+
return {};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function saveAuthSelectionState() {
|
|
543
|
+
try { localStorage.setItem(AUTH_SELECTION_KEY, JSON.stringify(authSelectionState)); } catch {}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const authSchemes = (spec.components && spec.components.securitySchemes) || {};
|
|
547
|
+
let authState = loadAuthState();
|
|
548
|
+
let authSelectionState = loadAuthSelectionState();
|
|
549
|
+
|
|
363
550
|
const operations = getOperations();
|
|
364
551
|
let selected = operations[0] || null;
|
|
365
552
|
const operationParamValues = new Map();
|
|
366
553
|
const operationBodyDrafts = new Map();
|
|
367
|
-
const requestHeaders =
|
|
554
|
+
const requestHeaders = loadSavedHeaders();
|
|
368
555
|
let expandModalMode = null;
|
|
369
556
|
let isMobileSidebarOpen = false;
|
|
370
557
|
let sidebarSearchQuery = "";
|
|
558
|
+
const paramTooltipRoot = document.getElementById("param-value-tooltip");
|
|
559
|
+
const paramTooltipLine = document.getElementById("param-tooltip-line");
|
|
560
|
+
const paramTooltipDescription = document.getElementById("param-tooltip-description");
|
|
561
|
+
let activeParamTooltipTrigger = null;
|
|
562
|
+
let paramTooltipHideTimer = null;
|
|
371
563
|
|
|
372
564
|
function setMobileSidebarOpen(open) {
|
|
373
565
|
const sidebar = document.getElementById("docs-sidebar");
|
|
@@ -387,6 +579,29 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
387
579
|
return op.method + " " + op.path;
|
|
388
580
|
}
|
|
389
581
|
|
|
582
|
+
function getOpHash(op) {
|
|
583
|
+
var tag = op.tag || "default";
|
|
584
|
+
var id = (op.operation && op.operation.operationId)
|
|
585
|
+
? op.operation.operationId
|
|
586
|
+
: op.method.toLowerCase() + "_" + op.path.split("/").filter(Boolean).join("_").replace(/[{}]/g, "");
|
|
587
|
+
return "#/" + encodeURIComponent(tag) + "/" + encodeURIComponent(id);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function findOpByHash(hash) {
|
|
591
|
+
if (!hash || hash.length <= 1) return null;
|
|
592
|
+
var parts = hash.slice(1).split("/").filter(Boolean);
|
|
593
|
+
if (parts.length < 2) return null;
|
|
594
|
+
var hashTag = decodeURIComponent(parts[0]);
|
|
595
|
+
var hashId = decodeURIComponent(parts[1]);
|
|
596
|
+
return operations.find(function(op) {
|
|
597
|
+
if (op.tag !== hashTag) return false;
|
|
598
|
+
var id = (op.operation && op.operation.operationId)
|
|
599
|
+
? op.operation.operationId
|
|
600
|
+
: op.method.toLowerCase() + "_" + op.path.split("/").filter(Boolean).join("_").replace(/[{}]/g, "");
|
|
601
|
+
return id === hashId;
|
|
602
|
+
}) || null;
|
|
603
|
+
}
|
|
604
|
+
|
|
390
605
|
function getOperationParameterGroups(op) {
|
|
391
606
|
const params =
|
|
392
607
|
op &&
|
|
@@ -436,7 +651,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
436
651
|
return resolved;
|
|
437
652
|
}
|
|
438
653
|
|
|
439
|
-
function buildRequestPath(op, pathParams, queryParams, values) {
|
|
654
|
+
function buildRequestPath(op, pathParams, queryParams, values, extraQuery) {
|
|
440
655
|
const resolvedPath = resolvePath(op.path, pathParams, values);
|
|
441
656
|
const query = new URLSearchParams();
|
|
442
657
|
|
|
@@ -448,6 +663,13 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
448
663
|
query.append(param.name, String(rawValue));
|
|
449
664
|
}
|
|
450
665
|
|
|
666
|
+
if (extraQuery) {
|
|
667
|
+
for (const key of Object.keys(extraQuery)) {
|
|
668
|
+
const val = extraQuery[key];
|
|
669
|
+
if (val) query.set(key, String(val));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
451
673
|
const queryString = query.toString();
|
|
452
674
|
return queryString ? resolvedPath + "?" + queryString : resolvedPath;
|
|
453
675
|
}
|
|
@@ -587,14 +809,25 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
587
809
|
|
|
588
810
|
const name = document.createElement("span");
|
|
589
811
|
name.textContent = op.name;
|
|
812
|
+
if (op.operation && op.operation.deprecated) {
|
|
813
|
+
name.style.textDecoration = "line-through";
|
|
814
|
+
name.style.opacity = "0.5";
|
|
815
|
+
}
|
|
590
816
|
|
|
591
817
|
row.appendChild(method);
|
|
592
818
|
row.appendChild(name);
|
|
819
|
+
if (op.operation && op.operation.deprecated) {
|
|
820
|
+
const badge = document.createElement("span");
|
|
821
|
+
badge.className = "text-[9px] px-1 py-0.5 rounded bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-500 font-semibold shrink-0";
|
|
822
|
+
badge.textContent = "deprecated";
|
|
823
|
+
row.appendChild(badge);
|
|
824
|
+
}
|
|
593
825
|
a.appendChild(row);
|
|
594
826
|
|
|
595
827
|
a.onclick = (e) => {
|
|
596
828
|
e.preventDefault();
|
|
597
829
|
selected = op;
|
|
830
|
+
history.pushState(null, "", getOpHash(op));
|
|
598
831
|
renderSidebar();
|
|
599
832
|
renderEndpoint();
|
|
600
833
|
if (window.innerWidth < 768) {
|
|
@@ -608,51 +841,257 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
608
841
|
}
|
|
609
842
|
}
|
|
610
843
|
|
|
844
|
+
function hideParamTooltip() {
|
|
845
|
+
if (!paramTooltipRoot) return;
|
|
846
|
+
if (paramTooltipHideTimer) {
|
|
847
|
+
window.clearTimeout(paramTooltipHideTimer);
|
|
848
|
+
paramTooltipHideTimer = null;
|
|
849
|
+
}
|
|
850
|
+
paramTooltipRoot.classList.remove("is-visible");
|
|
851
|
+
paramTooltipRoot.setAttribute("aria-hidden", "true");
|
|
852
|
+
if (activeParamTooltipTrigger) {
|
|
853
|
+
activeParamTooltipTrigger.setAttribute("aria-expanded", "false");
|
|
854
|
+
}
|
|
855
|
+
activeParamTooltipTrigger = null;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function scheduleParamTooltipHide() {
|
|
859
|
+
if (paramTooltipHideTimer) {
|
|
860
|
+
window.clearTimeout(paramTooltipHideTimer);
|
|
861
|
+
}
|
|
862
|
+
paramTooltipHideTimer = window.setTimeout(() => {
|
|
863
|
+
hideParamTooltip();
|
|
864
|
+
}, 95);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function positionParamTooltip(trigger) {
|
|
868
|
+
if (!paramTooltipRoot || !trigger) return;
|
|
869
|
+
const viewportPadding = 8;
|
|
870
|
+
const spacing = 10;
|
|
871
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
872
|
+
const tooltipRect = paramTooltipRoot.getBoundingClientRect();
|
|
873
|
+
let left = triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2);
|
|
874
|
+
left = Math.max(viewportPadding, Math.min(left, window.innerWidth - tooltipRect.width - viewportPadding));
|
|
875
|
+
let top = triggerRect.top - tooltipRect.height - spacing;
|
|
876
|
+
if (top < viewportPadding) {
|
|
877
|
+
top = triggerRect.bottom + spacing;
|
|
878
|
+
}
|
|
879
|
+
if (top + tooltipRect.height > window.innerHeight - viewportPadding) {
|
|
880
|
+
top = window.innerHeight - tooltipRect.height - viewportPadding;
|
|
881
|
+
}
|
|
882
|
+
paramTooltipRoot.style.left = Math.round(left) + "px";
|
|
883
|
+
paramTooltipRoot.style.top = Math.round(top) + "px";
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function showParamTooltip(trigger) {
|
|
887
|
+
if (
|
|
888
|
+
!paramTooltipRoot ||
|
|
889
|
+
!paramTooltipLine ||
|
|
890
|
+
!paramTooltipDescription ||
|
|
891
|
+
!trigger
|
|
892
|
+
) {
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
if (paramTooltipHideTimer) {
|
|
896
|
+
window.clearTimeout(paramTooltipHideTimer);
|
|
897
|
+
paramTooltipHideTimer = null;
|
|
898
|
+
}
|
|
899
|
+
const label = trigger.getAttribute("data-param-tooltip-label") || "Value";
|
|
900
|
+
const value = trigger.getAttribute("data-param-tooltip-value") || "";
|
|
901
|
+
const related = trigger.getAttribute("data-param-tooltip-related") || "";
|
|
902
|
+
const description = trigger.getAttribute("data-param-tooltip-description") || "";
|
|
903
|
+
if (activeParamTooltipTrigger && activeParamTooltipTrigger !== trigger) {
|
|
904
|
+
activeParamTooltipTrigger.setAttribute("aria-expanded", "false");
|
|
905
|
+
}
|
|
906
|
+
activeParamTooltipTrigger = trigger;
|
|
907
|
+
activeParamTooltipTrigger.setAttribute("aria-expanded", "true");
|
|
908
|
+
const pathLabel = related ? " | path: " + related : "";
|
|
909
|
+
paramTooltipLine.textContent = label + ": " + value + pathLabel;
|
|
910
|
+
if (description.trim()) {
|
|
911
|
+
paramTooltipDescription.textContent = description;
|
|
912
|
+
paramTooltipDescription.classList.remove("hidden");
|
|
913
|
+
} else {
|
|
914
|
+
paramTooltipDescription.textContent = "";
|
|
915
|
+
paramTooltipDescription.classList.add("hidden");
|
|
916
|
+
}
|
|
917
|
+
paramTooltipRoot.classList.add("is-visible");
|
|
918
|
+
paramTooltipRoot.setAttribute("aria-hidden", "false");
|
|
919
|
+
positionParamTooltip(trigger);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function registerParamTooltipTargets(scope) {
|
|
923
|
+
if (!scope) return;
|
|
924
|
+
const targets = scope.querySelectorAll("[data-param-tooltip-value]");
|
|
925
|
+
for (const target of targets) {
|
|
926
|
+
target.addEventListener("click", (event) => {
|
|
927
|
+
event.preventDefault();
|
|
928
|
+
if (
|
|
929
|
+
activeParamTooltipTrigger === target &&
|
|
930
|
+
paramTooltipRoot &&
|
|
931
|
+
paramTooltipRoot.classList.contains("is-visible")
|
|
932
|
+
) {
|
|
933
|
+
hideParamTooltip();
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
showParamTooltip(target);
|
|
937
|
+
});
|
|
938
|
+
target.addEventListener("mouseenter", () => {
|
|
939
|
+
showParamTooltip(target);
|
|
940
|
+
});
|
|
941
|
+
target.addEventListener("mouseleave", (event) => {
|
|
942
|
+
const related = event.relatedTarget;
|
|
943
|
+
if (paramTooltipRoot && related && paramTooltipRoot.contains(related)) return;
|
|
944
|
+
scheduleParamTooltipHide();
|
|
945
|
+
});
|
|
946
|
+
target.addEventListener("focus", () => {
|
|
947
|
+
showParamTooltip(target);
|
|
948
|
+
});
|
|
949
|
+
target.addEventListener("blur", (event) => {
|
|
950
|
+
const related = event.relatedTarget;
|
|
951
|
+
if (paramTooltipRoot && related && paramTooltipRoot.contains(related)) return;
|
|
952
|
+
scheduleParamTooltipHide();
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
611
957
|
function renderParamSection(title, params) {
|
|
612
958
|
if (!params.length) return "";
|
|
613
959
|
let rows = "";
|
|
614
960
|
for (const p of params) {
|
|
615
|
-
const
|
|
616
|
-
const
|
|
617
|
-
|
|
961
|
+
const schema = resolveSchemaRef(p.schema || {});
|
|
962
|
+
const typeRaw = getSchemaTypeLabel(schema);
|
|
963
|
+
const type = escapeHtml(typeRaw);
|
|
964
|
+
const nameRaw = p.name || "";
|
|
965
|
+
const name = escapeHtml(nameRaw);
|
|
966
|
+
const tooltipName = escapeHtmlAttribute(nameRaw);
|
|
967
|
+
const tooltipType = escapeHtmlAttribute(typeRaw);
|
|
968
|
+
const tooltipDescription = (typeof p.description === "string" && p.description.trim())
|
|
969
|
+
? escapeHtmlAttribute(p.description.trim())
|
|
970
|
+
: (typeof schema.description === "string" && schema.description.trim())
|
|
971
|
+
? escapeHtmlAttribute(schema.description.trim())
|
|
972
|
+
: "";
|
|
973
|
+
const desc = (typeof p.description === "string" && p.description.trim())
|
|
974
|
+
? '<p class="text-xs opacity-60 mt-0.5 leading-snug">' + renderMarkdown(p.description.trim()) + '</p>'
|
|
975
|
+
: "";
|
|
976
|
+
const extra = buildSchemaExtra(schema);
|
|
977
|
+
rows +=
|
|
978
|
+
'<div class="param-row py-2 border-b border-light-border/50 dark:border-dark-border/50">' +
|
|
979
|
+
'<div class="param-row-head">' +
|
|
980
|
+
'<div class="param-row-main">' +
|
|
981
|
+
'<button type="button" class="param-tooltip-trigger param-name-trigger" data-param-tooltip-label="Parameter" data-param-tooltip-value="' +
|
|
982
|
+
tooltipName +
|
|
983
|
+
'" data-param-tooltip-description="' +
|
|
984
|
+
tooltipDescription +
|
|
985
|
+
'" aria-expanded="false">' +
|
|
986
|
+
'<code class="text-sm font-mono param-name-text">' +
|
|
987
|
+
name +
|
|
988
|
+
"</code></button>" +
|
|
989
|
+
'<span class="text-xs text-brand shrink-0">' +
|
|
990
|
+
(p.required ? "required" : "optional") +
|
|
991
|
+
"</span></div>" +
|
|
992
|
+
'<button type="button" class="param-tooltip-trigger param-type-fade text-xs font-mono opacity-60" data-param-tooltip-label="Type" data-param-tooltip-value="' +
|
|
993
|
+
tooltipType +
|
|
994
|
+
'" data-param-tooltip-description="' +
|
|
995
|
+
tooltipDescription +
|
|
996
|
+
'" aria-expanded="false">' +
|
|
997
|
+
type +
|
|
998
|
+
"</button></div>" +
|
|
999
|
+
desc +
|
|
1000
|
+
extra +
|
|
1001
|
+
"</div>";
|
|
618
1002
|
}
|
|
619
1003
|
return '<div><h3 class="text-sm font-semibold mb-3 flex items-center border-b border-light-border dark:border-dark-border pb-2">' + escapeHtml(title) + "</h3>" + rows + "</div>";
|
|
620
1004
|
}
|
|
621
1005
|
|
|
622
1006
|
function getSchemaTypeLabel(schema) {
|
|
623
|
-
|
|
624
|
-
if (
|
|
625
|
-
if (
|
|
626
|
-
if (
|
|
627
|
-
if (
|
|
628
|
-
if (
|
|
629
|
-
if (Array.isArray(
|
|
630
|
-
if (Array.isArray(
|
|
1007
|
+
const resolved = resolveSchemaRef(schema);
|
|
1008
|
+
if (!resolved || typeof resolved !== "object") return "unknown";
|
|
1009
|
+
if (Array.isArray(resolved.type)) return resolved.type.join(" | ");
|
|
1010
|
+
if (resolved.type) return String(resolved.type);
|
|
1011
|
+
if (resolved.properties) return "object";
|
|
1012
|
+
if (resolved.items) return "array";
|
|
1013
|
+
if (Array.isArray(resolved.oneOf)) return "oneOf";
|
|
1014
|
+
if (Array.isArray(resolved.anyOf)) return "anyOf";
|
|
1015
|
+
if (Array.isArray(resolved.allOf)) return "allOf";
|
|
631
1016
|
return "unknown";
|
|
632
1017
|
}
|
|
633
1018
|
|
|
1019
|
+
function buildSchemaExtra(schema) {
|
|
1020
|
+
const resolved = resolveSchemaRef(schema);
|
|
1021
|
+
if (!resolved || typeof resolved !== "object") return "";
|
|
1022
|
+
const chips = [];
|
|
1023
|
+
if (resolved.format) chips.push(escapeHtml(String(resolved.format)));
|
|
1024
|
+
if (Array.isArray(resolved.enum) && resolved.enum.length > 0) {
|
|
1025
|
+
const shown = resolved.enum.slice(0, 5).map(function(v) { return escapeHtml(JSON.stringify(v)); });
|
|
1026
|
+
chips.push(shown.join(" | ") + (resolved.enum.length > 5 ? " …" : ""));
|
|
1027
|
+
}
|
|
1028
|
+
if (resolved.minimum !== undefined) chips.push("min: " + resolved.minimum);
|
|
1029
|
+
if (resolved.maximum !== undefined) chips.push("max: " + resolved.maximum);
|
|
1030
|
+
if (typeof resolved.exclusiveMinimum === "number") chips.push(">" + resolved.exclusiveMinimum);
|
|
1031
|
+
if (typeof resolved.exclusiveMaximum === "number") chips.push("<" + resolved.exclusiveMaximum);
|
|
1032
|
+
if (resolved.minLength !== undefined) chips.push("minLen: " + resolved.minLength);
|
|
1033
|
+
if (resolved.maxLength !== undefined) chips.push("maxLen: " + resolved.maxLength);
|
|
1034
|
+
if (resolved.minItems !== undefined) chips.push("minItems: " + resolved.minItems);
|
|
1035
|
+
if (resolved.maxItems !== undefined) chips.push("maxItems: " + resolved.maxItems);
|
|
1036
|
+
if (resolved.uniqueItems) chips.push("unique");
|
|
1037
|
+
if (resolved.pattern) chips.push("/" + escapeHtml(String(resolved.pattern)) + "/");
|
|
1038
|
+
if (!chips.length) return "";
|
|
1039
|
+
return '<div class="flex flex-wrap gap-1 mt-1.5">' +
|
|
1040
|
+
chips.map(function(c) {
|
|
1041
|
+
return '<span class="text-[10px] px-1.5 py-0.5 rounded bg-black/5 dark:bg-white/5 font-mono opacity-80">' + c + '</span>';
|
|
1042
|
+
}).join("") +
|
|
1043
|
+
'</div>';
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function resolveSchemaRef(schema, visitedRefs) {
|
|
1047
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
1048
|
+
const ref = typeof schema.$ref === "string" ? schema.$ref : "";
|
|
1049
|
+
if (!ref || !ref.startsWith("#/components/schemas/")) {
|
|
1050
|
+
return schema;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const seen = visitedRefs || new Set();
|
|
1054
|
+
if (seen.has(ref)) return schema;
|
|
1055
|
+
seen.add(ref);
|
|
1056
|
+
|
|
1057
|
+
const parts = ref.split("/");
|
|
1058
|
+
const schemaName = parts[parts.length - 1];
|
|
1059
|
+
const referenced = spec && spec.components && spec.components.schemas && spec.components.schemas[schemaName];
|
|
1060
|
+
if (!referenced || typeof referenced !== "object") return schema;
|
|
1061
|
+
|
|
1062
|
+
const merged = Object.assign({}, referenced, schema);
|
|
1063
|
+
delete merged.$ref;
|
|
1064
|
+
return resolveSchemaRef(merged, seen);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
634
1067
|
function buildSchemaChildren(schema) {
|
|
635
|
-
|
|
1068
|
+
const resolved = resolveSchemaRef(schema);
|
|
1069
|
+
if (!resolved || typeof resolved !== "object") return [];
|
|
636
1070
|
|
|
637
1071
|
const children = [];
|
|
638
1072
|
|
|
639
|
-
if (
|
|
1073
|
+
if (resolved.properties && typeof resolved.properties === "object") {
|
|
640
1074
|
const requiredSet = new Set(
|
|
641
|
-
Array.isArray(
|
|
1075
|
+
Array.isArray(resolved.required) ? resolved.required : [],
|
|
642
1076
|
);
|
|
643
|
-
for (const [name, childSchema] of Object.entries(
|
|
1077
|
+
for (const [name, childSchema] of Object.entries(resolved.properties)) {
|
|
1078
|
+
const childDef = childSchema || {};
|
|
1079
|
+
const isArrayType = Array.isArray(childDef.type)
|
|
1080
|
+
? childDef.type.includes("array")
|
|
1081
|
+
: childDef.type === "array";
|
|
1082
|
+
const isArrayLike = isArrayType || childDef.items !== undefined;
|
|
644
1083
|
children.push({
|
|
645
|
-
name,
|
|
646
|
-
schema:
|
|
1084
|
+
name: isArrayLike ? (name + "[]") : name,
|
|
1085
|
+
schema: childDef,
|
|
647
1086
|
required: requiredSet.has(name),
|
|
648
1087
|
});
|
|
649
1088
|
}
|
|
650
1089
|
}
|
|
651
1090
|
|
|
652
|
-
if (
|
|
1091
|
+
if (resolved.items) {
|
|
653
1092
|
children.push({
|
|
654
|
-
name:
|
|
655
|
-
schema:
|
|
1093
|
+
name: getArrayItemNodeName(resolved.items),
|
|
1094
|
+
schema: resolved.items,
|
|
656
1095
|
required: true,
|
|
657
1096
|
});
|
|
658
1097
|
}
|
|
@@ -660,45 +1099,100 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
660
1099
|
return children;
|
|
661
1100
|
}
|
|
662
1101
|
|
|
663
|
-
function
|
|
664
|
-
|
|
665
|
-
const
|
|
1102
|
+
function getArrayItemNodeName(itemSchema) {
|
|
1103
|
+
if (!itemSchema || typeof itemSchema !== "object") return "item";
|
|
1104
|
+
const title =
|
|
1105
|
+
typeof itemSchema.title === "string" && itemSchema.title.trim()
|
|
1106
|
+
? itemSchema.title.trim()
|
|
1107
|
+
: "";
|
|
1108
|
+
if (title) return title;
|
|
1109
|
+
|
|
1110
|
+
const ref =
|
|
1111
|
+
typeof itemSchema.$ref === "string" && itemSchema.$ref.trim()
|
|
1112
|
+
? itemSchema.$ref.trim()
|
|
1113
|
+
: "";
|
|
1114
|
+
if (ref) {
|
|
1115
|
+
const parts = ref.split("/").filter(Boolean);
|
|
1116
|
+
const last = parts[parts.length - 1];
|
|
1117
|
+
if (last) return last;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
const typeLabel = getSchemaTypeLabel(itemSchema);
|
|
1121
|
+
if (typeLabel && typeLabel !== "unknown") return typeLabel;
|
|
1122
|
+
return "type";
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
function renderSchemaFieldNode(field, depth, parentPath) {
|
|
1126
|
+
const schema = resolveSchemaRef(field.schema || {});
|
|
1127
|
+
const nameRaw = field.name || "field";
|
|
1128
|
+
const name = escapeHtml(nameRaw);
|
|
666
1129
|
const requiredLabel = field.required ? "required" : "optional";
|
|
667
|
-
const
|
|
1130
|
+
const typeRaw = getSchemaTypeLabel(schema);
|
|
1131
|
+
const type = escapeHtml(typeRaw);
|
|
1132
|
+
const tooltipName = escapeHtmlAttribute(nameRaw);
|
|
1133
|
+
const tooltipType = escapeHtmlAttribute(typeRaw);
|
|
1134
|
+
const fieldPath = parentPath ? (parentPath + "." + nameRaw) : nameRaw;
|
|
1135
|
+
const tooltipPath = escapeHtmlAttribute(fieldPath);
|
|
1136
|
+
const tooltipDescription = (typeof schema.description === "string" && schema.description.trim())
|
|
1137
|
+
? escapeHtmlAttribute(schema.description.trim())
|
|
1138
|
+
: "";
|
|
668
1139
|
const children = buildSchemaChildren(schema);
|
|
669
1140
|
const padding = depth * 14;
|
|
1141
|
+
const extra = buildSchemaExtra(schema);
|
|
670
1142
|
|
|
671
1143
|
if (!children.length) {
|
|
672
1144
|
return (
|
|
673
|
-
'<div class="py-2 border-b border-light-border/50 dark:border-dark-border/50" style="padding-left:' +
|
|
1145
|
+
'<div class="param-row py-2 border-b border-light-border/50 dark:border-dark-border/50" style="padding-left:' +
|
|
674
1146
|
padding +
|
|
675
|
-
'px"><div class="
|
|
1147
|
+
'px"><div class="param-row-head"><div class="param-row-main">' +
|
|
1148
|
+
'<button type="button" class="param-tooltip-trigger param-name-trigger" data-param-tooltip-label="Field" data-param-tooltip-value="' +
|
|
1149
|
+
tooltipName +
|
|
1150
|
+
'" data-param-tooltip-related="' +
|
|
1151
|
+
tooltipPath +
|
|
1152
|
+
'" data-param-tooltip-description="' +
|
|
1153
|
+
tooltipDescription +
|
|
1154
|
+
'" aria-expanded="false"><code class="text-sm font-mono param-name-text">' +
|
|
676
1155
|
name +
|
|
677
|
-
'</code><span class="text-xs text-brand
|
|
1156
|
+
'</code></button><span class="text-xs text-brand shrink-0">' +
|
|
678
1157
|
requiredLabel +
|
|
679
|
-
'</span></div><
|
|
1158
|
+
'</span></div><button type="button" class="param-tooltip-trigger param-type-fade text-xs font-mono opacity-60" data-param-tooltip-label="Type" data-param-tooltip-value="' +
|
|
1159
|
+
tooltipType +
|
|
1160
|
+
'" data-param-tooltip-description="' +
|
|
1161
|
+
tooltipDescription +
|
|
1162
|
+
'" aria-expanded="false">' +
|
|
680
1163
|
type +
|
|
681
|
-
"</
|
|
1164
|
+
"</button></div>" + extra + "</div>"
|
|
682
1165
|
);
|
|
683
1166
|
}
|
|
684
1167
|
|
|
685
1168
|
let nested = "";
|
|
686
1169
|
for (const child of children) {
|
|
687
|
-
nested += renderSchemaFieldNode(child, depth + 1);
|
|
1170
|
+
nested += renderSchemaFieldNode(child, depth + 1, fieldPath);
|
|
688
1171
|
}
|
|
689
1172
|
|
|
690
1173
|
return (
|
|
691
|
-
'<details
|
|
692
|
-
'<summary class="list-none cursor-pointer py-2
|
|
1174
|
+
'<details open>' +
|
|
1175
|
+
'<summary class="list-none cursor-pointer py-2 border-b border-light-border/50 dark:border-dark-border/50" style="padding-left:' +
|
|
693
1176
|
padding +
|
|
694
|
-
'px"><div class="
|
|
1177
|
+
'px"><div class="param-row-head"><div class="param-row-main"><span class="text-xs opacity-70 shrink-0">▾</span>' +
|
|
1178
|
+
'<button type="button" class="param-tooltip-trigger param-name-trigger" data-param-tooltip-label="Field" data-param-tooltip-value="' +
|
|
1179
|
+
tooltipName +
|
|
1180
|
+
'" data-param-tooltip-related="' +
|
|
1181
|
+
tooltipPath +
|
|
1182
|
+
'" data-param-tooltip-description="' +
|
|
1183
|
+
tooltipDescription +
|
|
1184
|
+
'" aria-expanded="false"><code class="text-sm font-mono param-name-text">' +
|
|
695
1185
|
name +
|
|
696
|
-
'</code><span class="text-xs text-brand">' +
|
|
1186
|
+
'</code></button><span class="text-xs text-brand shrink-0">' +
|
|
697
1187
|
requiredLabel +
|
|
698
|
-
'</span></div><
|
|
1188
|
+
'</span></div><button type="button" class="param-tooltip-trigger param-type-fade text-xs font-mono opacity-60" data-param-tooltip-label="Type" data-param-tooltip-value="' +
|
|
1189
|
+
tooltipType +
|
|
1190
|
+
'" data-param-tooltip-description="' +
|
|
1191
|
+
tooltipDescription +
|
|
1192
|
+
'" aria-expanded="false">' +
|
|
699
1193
|
type +
|
|
700
|
-
"</
|
|
701
|
-
|
|
1194
|
+
"</button></div>" + extra + "</summary>" +
|
|
1195
|
+
"<div>" +
|
|
702
1196
|
nested +
|
|
703
1197
|
"</div></details>"
|
|
704
1198
|
);
|
|
@@ -711,7 +1205,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
711
1205
|
|
|
712
1206
|
let rows = "";
|
|
713
1207
|
for (const child of rootChildren) {
|
|
714
|
-
rows += renderSchemaFieldNode(child, 0);
|
|
1208
|
+
rows += renderSchemaFieldNode(child, 0, "");
|
|
715
1209
|
}
|
|
716
1210
|
|
|
717
1211
|
return (
|
|
@@ -738,27 +1232,40 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
738
1232
|
const responseDef = responses[statusCode];
|
|
739
1233
|
if (!responseDef || typeof responseDef !== "object") continue;
|
|
740
1234
|
|
|
1235
|
+
const responseDesc = (typeof responseDef.description === "string" && responseDef.description.trim())
|
|
1236
|
+
? responseDef.description.trim()
|
|
1237
|
+
: "";
|
|
1238
|
+
|
|
741
1239
|
const jsonSchema =
|
|
742
1240
|
responseDef.content &&
|
|
743
1241
|
responseDef.content["application/json"] &&
|
|
744
1242
|
responseDef.content["application/json"].schema;
|
|
745
1243
|
|
|
746
|
-
if (!jsonSchema || typeof jsonSchema !== "object") continue;
|
|
747
|
-
|
|
748
|
-
const rootChildren = buildSchemaChildren(jsonSchema);
|
|
749
|
-
if (!rootChildren.length) continue;
|
|
750
|
-
|
|
751
1244
|
let rows = "";
|
|
752
|
-
|
|
753
|
-
|
|
1245
|
+
if (jsonSchema && typeof jsonSchema === "object") {
|
|
1246
|
+
const rootChildren = buildSchemaChildren(jsonSchema);
|
|
1247
|
+
for (const child of rootChildren) {
|
|
1248
|
+
rows += renderSchemaFieldNode(child, 0, "");
|
|
1249
|
+
}
|
|
754
1250
|
}
|
|
755
1251
|
|
|
1252
|
+
if (!responseDesc && !rows) continue;
|
|
1253
|
+
|
|
1254
|
+
const descHtml = responseDesc
|
|
1255
|
+
? ' <span class="normal-case font-sans opacity-70 ml-1">— ' + escapeHtml(responseDesc) + '</span>'
|
|
1256
|
+
: "";
|
|
1257
|
+
const contentHtml = rows || '<p class="text-xs opacity-60 mt-1">No schema fields</p>';
|
|
1258
|
+
|
|
756
1259
|
sections +=
|
|
757
|
-
'<
|
|
1260
|
+
'<details class="mb-4">' +
|
|
1261
|
+
'<summary class="list-none cursor-pointer">' +
|
|
1262
|
+
'<h4 class="text-xs font-mono uppercase tracking-wider opacity-70 mb-2">Status ' +
|
|
758
1263
|
escapeHtml(statusCode) +
|
|
1264
|
+
descHtml +
|
|
759
1265
|
"</h4>" +
|
|
760
|
-
|
|
761
|
-
|
|
1266
|
+
"</summary>" +
|
|
1267
|
+
contentHtml +
|
|
1268
|
+
"</details>";
|
|
762
1269
|
}
|
|
763
1270
|
|
|
764
1271
|
if (!sections) return "";
|
|
@@ -850,6 +1357,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
850
1357
|
"w-full text-xs px-2.5 py-2 rounded border border-light-border dark:border-dark-border bg-light-bg dark:bg-dark-bg focus:outline-none focus:border-brand dark:focus:border-brand transition-colors font-mono";
|
|
851
1358
|
keyInput.addEventListener("input", () => {
|
|
852
1359
|
entry.key = keyInput.value;
|
|
1360
|
+
saveHeaders();
|
|
853
1361
|
updateRequestPreview();
|
|
854
1362
|
});
|
|
855
1363
|
|
|
@@ -864,6 +1372,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
864
1372
|
"w-full text-xs px-2.5 py-2 rounded border border-light-border dark:border-dark-border bg-light-bg dark:bg-dark-bg focus:outline-none focus:border-brand dark:focus:border-brand transition-colors font-mono";
|
|
865
1373
|
valueInput.addEventListener("input", () => {
|
|
866
1374
|
entry.value = valueInput.value;
|
|
1375
|
+
saveHeaders();
|
|
867
1376
|
updateRequestPreview();
|
|
868
1377
|
});
|
|
869
1378
|
|
|
@@ -877,6 +1386,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
877
1386
|
'<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 12h12"></path></svg>';
|
|
878
1387
|
removeButton.addEventListener("click", () => {
|
|
879
1388
|
requestHeaders.splice(index, 1);
|
|
1389
|
+
saveHeaders();
|
|
880
1390
|
renderHeaderInputs();
|
|
881
1391
|
updateRequestPreview();
|
|
882
1392
|
});
|
|
@@ -893,15 +1403,32 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
893
1403
|
return Object.keys(headers).some((key) => key.toLowerCase() === target);
|
|
894
1404
|
}
|
|
895
1405
|
|
|
896
|
-
function
|
|
897
|
-
const
|
|
1406
|
+
function buildCookieHeaderValue(cookieValues) {
|
|
1407
|
+
const entries = Object.entries(cookieValues);
|
|
1408
|
+
if (!entries.length) return "";
|
|
1409
|
+
return entries
|
|
1410
|
+
.map(([name, value]) => String(name) + "=" + encodeURIComponent(String(value)))
|
|
1411
|
+
.join("; ");
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
function getRequestHeadersObject(op) {
|
|
1415
|
+
const auth = getAuthHeaders(op);
|
|
1416
|
+
const authCookies = getAuthCookieParams(op);
|
|
1417
|
+
if (Object.keys(authCookies).length > 0) {
|
|
1418
|
+
const cookieHeader = buildCookieHeaderValue(authCookies);
|
|
1419
|
+
if (cookieHeader) {
|
|
1420
|
+
auth["Cookie"] = cookieHeader;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
const manual = {};
|
|
898
1424
|
for (const entry of requestHeaders) {
|
|
899
1425
|
const key = String(entry.key || "").trim();
|
|
900
1426
|
const value = String(entry.value || "").trim();
|
|
901
1427
|
if (!key || !value) continue;
|
|
902
|
-
|
|
1428
|
+
manual[key] = value;
|
|
903
1429
|
}
|
|
904
|
-
|
|
1430
|
+
// Auth provides defaults; manual headers win on conflict
|
|
1431
|
+
return Object.assign({}, auth, manual);
|
|
905
1432
|
}
|
|
906
1433
|
|
|
907
1434
|
function buildCurl(op, headers, body, requestPath) {
|
|
@@ -938,6 +1465,33 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
938
1465
|
.replace(/>/g, ">");
|
|
939
1466
|
}
|
|
940
1467
|
|
|
1468
|
+
function escapeHtmlAttribute(value) {
|
|
1469
|
+
return String(value)
|
|
1470
|
+
.replace(/&/g, "&")
|
|
1471
|
+
.replace(/</g, "<")
|
|
1472
|
+
.replace(/>/g, ">")
|
|
1473
|
+
.replace(/"/g, """)
|
|
1474
|
+
.replace(/'/g, "'");
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
function renderMarkdown(text) {
|
|
1478
|
+
if (!text || typeof text !== "string") return "";
|
|
1479
|
+
var s = escapeHtml(text);
|
|
1480
|
+
// inline code — process first to protect content inside backticks
|
|
1481
|
+
s = s.replace(/\`([^\`\\n]+)\`/g, '<code class="text-xs font-mono bg-black/5 dark:bg-white/5 px-1 py-0.5 rounded">$1</code>');
|
|
1482
|
+
// bold **text**
|
|
1483
|
+
s = s.replace(/\\*\\*([^*\\n]+)\\*\\*/g, '<strong>$1</strong>');
|
|
1484
|
+
// italic *text*
|
|
1485
|
+
s = s.replace(/\\*([^*\\n]+)\\*/g, '<em>$1</em>');
|
|
1486
|
+
// links [text](url)
|
|
1487
|
+
s = s.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, function(m, txt, url) {
|
|
1488
|
+
var lc = url.toLowerCase().replace(/\\s/g, "");
|
|
1489
|
+
if (lc.indexOf("javascript:") === 0 || lc.indexOf("data:") === 0 || lc.indexOf("vbscript:") === 0) return txt;
|
|
1490
|
+
return '<a href="' + url.replace(/"/g, '"') + '" target="_blank" rel="noopener noreferrer" class="text-brand hover:underline">' + txt + '</a>';
|
|
1491
|
+
});
|
|
1492
|
+
return s;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
941
1495
|
function toPrettyJson(value) {
|
|
942
1496
|
const trimmed = (value || "").trim();
|
|
943
1497
|
if (!trimmed) return null;
|
|
@@ -1071,10 +1625,10 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1071
1625
|
|
|
1072
1626
|
const { path, query } = getOperationParameterGroups(selected);
|
|
1073
1627
|
const values = getParameterValues(selected);
|
|
1074
|
-
const requestPath = buildRequestPath(selected, path, query, values);
|
|
1628
|
+
const requestPath = buildRequestPath(selected, path, query, values, getAuthQueryParams(selected));
|
|
1075
1629
|
const bodyInput = document.getElementById("body-input");
|
|
1076
1630
|
const body = bodyInput ? bodyInput.value.trim() : "";
|
|
1077
|
-
const headers = getRequestHeadersObject();
|
|
1631
|
+
const headers = getRequestHeadersObject(selected);
|
|
1078
1632
|
if (body && !hasHeaderName(headers, "Content-Type")) {
|
|
1079
1633
|
headers["Content-Type"] = "application/json";
|
|
1080
1634
|
}
|
|
@@ -1125,8 +1679,13 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1125
1679
|
}
|
|
1126
1680
|
setResponseContent("", "", false);
|
|
1127
1681
|
|
|
1682
|
+
const deprecatedBanner = document.getElementById("deprecated-banner");
|
|
1683
|
+
if (deprecatedBanner) {
|
|
1684
|
+
deprecatedBanner.classList.toggle("hidden", !op.deprecated);
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1128
1687
|
document.getElementById("tag-title").textContent = selected.tag;
|
|
1129
|
-
document.getElementById("tag-description").
|
|
1688
|
+
document.getElementById("tag-description").innerHTML = op.description ? renderMarkdown(op.description) : "Interactive API documentation.";
|
|
1130
1689
|
const methodNode = document.getElementById("endpoint-method");
|
|
1131
1690
|
methodNode.textContent = selected.method;
|
|
1132
1691
|
methodNode.className = "px-2.5 py-0.5 rounded-full text-xs font-mono font-medium " + (methodBadge[selected.method] || methodBadgeDefault);
|
|
@@ -1143,7 +1702,13 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1143
1702
|
|
|
1144
1703
|
html += renderRequestBodySchemaSection(reqSchema);
|
|
1145
1704
|
html += renderResponseSchemasSection(op.responses);
|
|
1146
|
-
document.getElementById("params-column")
|
|
1705
|
+
const paramsColumn = document.getElementById("params-column");
|
|
1706
|
+
if (paramsColumn) {
|
|
1707
|
+
hideParamTooltip();
|
|
1708
|
+
paramsColumn.innerHTML = html || '<div class="text-sm opacity-70">No parameters</div>';
|
|
1709
|
+
registerParamTooltipTargets(paramsColumn);
|
|
1710
|
+
}
|
|
1711
|
+
renderAuthPanel();
|
|
1147
1712
|
renderTryItParameterInputs(path, query);
|
|
1148
1713
|
renderHeaderInputs();
|
|
1149
1714
|
updateRequestPreview();
|
|
@@ -1154,6 +1719,24 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1154
1719
|
document.getElementById("copy-curl").addEventListener("click", async () => {
|
|
1155
1720
|
try { await navigator.clipboard.writeText(document.getElementById("curl-code").textContent || ""); } catch {}
|
|
1156
1721
|
});
|
|
1722
|
+
if (paramTooltipRoot) {
|
|
1723
|
+
paramTooltipRoot.addEventListener("mouseenter", () => {
|
|
1724
|
+
if (paramTooltipHideTimer) {
|
|
1725
|
+
window.clearTimeout(paramTooltipHideTimer);
|
|
1726
|
+
paramTooltipHideTimer = null;
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
paramTooltipRoot.addEventListener("mouseleave", () => {
|
|
1730
|
+
scheduleParamTooltipHide();
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
document.addEventListener("pointerdown", (event) => {
|
|
1734
|
+
if (!paramTooltipRoot) return;
|
|
1735
|
+
const target = event.target;
|
|
1736
|
+
if (target && paramTooltipRoot.contains(target)) return;
|
|
1737
|
+
if (target && target.closest && target.closest("[data-param-tooltip-value]")) return;
|
|
1738
|
+
hideParamTooltip();
|
|
1739
|
+
});
|
|
1157
1740
|
document.getElementById("sidebar-search").addEventListener("input", (event) => {
|
|
1158
1741
|
sidebarSearchQuery = event.currentTarget.value || "";
|
|
1159
1742
|
renderSidebar();
|
|
@@ -1179,7 +1762,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1179
1762
|
return;
|
|
1180
1763
|
}
|
|
1181
1764
|
|
|
1182
|
-
const requestPath = buildRequestPath(selected, path, query, values);
|
|
1765
|
+
const requestPath = buildRequestPath(selected, path, query, values, getAuthQueryParams(selected));
|
|
1183
1766
|
formatBodyJsonInput();
|
|
1184
1767
|
updateBodyJsonPresentation();
|
|
1185
1768
|
const op = selected.operation || {};
|
|
@@ -1188,7 +1771,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1188
1771
|
const bodyInput = document.getElementById("body-input");
|
|
1189
1772
|
const body =
|
|
1190
1773
|
supportsBody && bodyInput ? bodyInput.value.trim() : "";
|
|
1191
|
-
const headers = getRequestHeadersObject();
|
|
1774
|
+
const headers = getRequestHeadersObject(selected);
|
|
1192
1775
|
if (body && !hasHeaderName(headers, "Content-Type")) {
|
|
1193
1776
|
headers["Content-Type"] = "application/json";
|
|
1194
1777
|
}
|
|
@@ -1196,7 +1779,13 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1196
1779
|
setSubmitLoading(true);
|
|
1197
1780
|
try {
|
|
1198
1781
|
const requestStart = performance.now();
|
|
1199
|
-
|
|
1782
|
+
applyAuthCookies(selected);
|
|
1783
|
+
const response = await fetch(requestPath, {
|
|
1784
|
+
method: selected.method,
|
|
1785
|
+
headers,
|
|
1786
|
+
body: body || undefined,
|
|
1787
|
+
credentials: "same-origin",
|
|
1788
|
+
});
|
|
1200
1789
|
const text = await response.text();
|
|
1201
1790
|
const responseTimeMs = Math.round(performance.now() - requestStart);
|
|
1202
1791
|
const contentType = response.headers.get("content-type") || "unknown";
|
|
@@ -1272,6 +1861,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1272
1861
|
|
|
1273
1862
|
document.getElementById("add-header-btn").addEventListener("click", () => {
|
|
1274
1863
|
requestHeaders.push({ key: "", value: "" });
|
|
1864
|
+
saveHeaders();
|
|
1275
1865
|
renderHeaderInputs();
|
|
1276
1866
|
updateRequestPreview();
|
|
1277
1867
|
});
|
|
@@ -1385,12 +1975,21 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1385
1975
|
setMobileSidebarOpen(false);
|
|
1386
1976
|
});
|
|
1387
1977
|
window.addEventListener("resize", () => {
|
|
1978
|
+
if (activeParamTooltipTrigger) {
|
|
1979
|
+
positionParamTooltip(activeParamTooltipTrigger);
|
|
1980
|
+
}
|
|
1388
1981
|
if (window.innerWidth >= 768 && isMobileSidebarOpen) {
|
|
1389
1982
|
setMobileSidebarOpen(false);
|
|
1390
1983
|
}
|
|
1391
1984
|
});
|
|
1985
|
+
window.addEventListener("scroll", () => {
|
|
1986
|
+
if (activeParamTooltipTrigger) {
|
|
1987
|
+
positionParamTooltip(activeParamTooltipTrigger);
|
|
1988
|
+
}
|
|
1989
|
+
}, true);
|
|
1392
1990
|
document.addEventListener("keydown", (event) => {
|
|
1393
1991
|
if (event.key === "Escape") {
|
|
1992
|
+
hideParamTooltip();
|
|
1394
1993
|
if (isMobileSidebarOpen) {
|
|
1395
1994
|
setMobileSidebarOpen(false);
|
|
1396
1995
|
}
|
|
@@ -1415,7 +2014,444 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, log
|
|
|
1415
2014
|
}
|
|
1416
2015
|
});
|
|
1417
2016
|
|
|
2017
|
+
function getOperationSecurityRequirements(op) {
|
|
2018
|
+
const operationSecurity = op && op.operation && Array.isArray(op.operation.security)
|
|
2019
|
+
? op.operation.security
|
|
2020
|
+
: null;
|
|
2021
|
+
if (operationSecurity) {
|
|
2022
|
+
return operationSecurity;
|
|
2023
|
+
}
|
|
2024
|
+
return Array.isArray(spec.security) ? spec.security : [];
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
function getAuthSelectionKeyForOperation(op) {
|
|
2028
|
+
if (!op) return "";
|
|
2029
|
+
return getOperationKey(op);
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
function getAuthSchemeOptionsForOperation(op) {
|
|
2033
|
+
const requirements = getOperationSecurityRequirements(op).filter((requirement) =>
|
|
2034
|
+
requirement && typeof requirement === "object" && !Array.isArray(requirement)
|
|
2035
|
+
);
|
|
2036
|
+
if (!requirements.length) return [];
|
|
2037
|
+
|
|
2038
|
+
const seen = new Set();
|
|
2039
|
+
const options = [];
|
|
2040
|
+
for (const requirement of requirements) {
|
|
2041
|
+
for (const schemeName of Object.keys(requirement)) {
|
|
2042
|
+
if (!Object.prototype.hasOwnProperty.call(authSchemes, schemeName)) continue;
|
|
2043
|
+
if (seen.has(schemeName)) continue;
|
|
2044
|
+
seen.add(schemeName);
|
|
2045
|
+
options.push(schemeName);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
return options;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
function getSelectedAuthSchemeForOperation(op) {
|
|
2052
|
+
const selectionKey = getAuthSelectionKeyForOperation(op);
|
|
2053
|
+
if (!selectionKey) return null;
|
|
2054
|
+
|
|
2055
|
+
const selectedScheme = authSelectionState[selectionKey];
|
|
2056
|
+
if (!selectedScheme || typeof selectedScheme !== "string") return null;
|
|
2057
|
+
|
|
2058
|
+
const options = Object.keys(authSchemes);
|
|
2059
|
+
if (!options.includes(selectedScheme)) {
|
|
2060
|
+
delete authSelectionState[selectionKey];
|
|
2061
|
+
saveAuthSelectionState();
|
|
2062
|
+
return null;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
return selectedScheme;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
function setSelectedAuthSchemeForOperation(op, schemeName) {
|
|
2069
|
+
const selectionKey = getAuthSelectionKeyForOperation(op);
|
|
2070
|
+
if (!selectionKey) return;
|
|
2071
|
+
|
|
2072
|
+
if (!schemeName) {
|
|
2073
|
+
delete authSelectionState[selectionKey];
|
|
2074
|
+
} else {
|
|
2075
|
+
authSelectionState[selectionKey] = schemeName;
|
|
2076
|
+
}
|
|
2077
|
+
saveAuthSelectionState();
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
function hasAuthStateForScheme(schemeName) {
|
|
2081
|
+
const scheme = authSchemes[schemeName];
|
|
2082
|
+
if (!scheme) return false;
|
|
2083
|
+
|
|
2084
|
+
const state = authState[schemeName] || {};
|
|
2085
|
+
const type = (scheme.type || "").toLowerCase();
|
|
2086
|
+
const httpScheme = (scheme.scheme || "").toLowerCase();
|
|
2087
|
+
|
|
2088
|
+
if (type === "http" && httpScheme === "basic") {
|
|
2089
|
+
return Boolean(state.username && state.password);
|
|
2090
|
+
}
|
|
2091
|
+
if (type === "http") {
|
|
2092
|
+
return Boolean(state.token);
|
|
2093
|
+
}
|
|
2094
|
+
if (type === "apikey") {
|
|
2095
|
+
return Boolean(state.value);
|
|
2096
|
+
}
|
|
2097
|
+
if (type === "oauth2" || type === "openidconnect") {
|
|
2098
|
+
return Boolean(state.token);
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
return false;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
function chooseOperationSecurityRequirement(op) {
|
|
2105
|
+
const requirements = getOperationSecurityRequirements(op).filter((requirement) =>
|
|
2106
|
+
requirement && typeof requirement === "object" && !Array.isArray(requirement)
|
|
2107
|
+
);
|
|
2108
|
+
if (!requirements.length) return null;
|
|
2109
|
+
|
|
2110
|
+
const selectedScheme = getSelectedAuthSchemeForOperation(op);
|
|
2111
|
+
if (selectedScheme) {
|
|
2112
|
+
const selectedRequirement = requirements.find((requirement) =>
|
|
2113
|
+
Object.prototype.hasOwnProperty.call(requirement, selectedScheme)
|
|
2114
|
+
);
|
|
2115
|
+
if (selectedRequirement) return selectedRequirement;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
let bestRequirement = null;
|
|
2119
|
+
let bestScore = -1;
|
|
2120
|
+
|
|
2121
|
+
for (const requirement of requirements) {
|
|
2122
|
+
const schemeNames = Object.keys(requirement).filter((schemeName) =>
|
|
2123
|
+
Object.prototype.hasOwnProperty.call(authSchemes, schemeName)
|
|
2124
|
+
);
|
|
2125
|
+
if (!schemeNames.length) continue;
|
|
2126
|
+
|
|
2127
|
+
const providedCount = schemeNames.filter((schemeName) => hasAuthStateForScheme(schemeName)).length;
|
|
2128
|
+
const isComplete = providedCount === schemeNames.length;
|
|
2129
|
+
const score = isComplete ? 1000 + providedCount : providedCount;
|
|
2130
|
+
|
|
2131
|
+
if (score > bestScore) {
|
|
2132
|
+
bestScore = score;
|
|
2133
|
+
bestRequirement = requirement;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
return bestRequirement || requirements[0];
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
function getAuthSchemeNamesForOperation(op) {
|
|
2141
|
+
const schemeNames = Object.keys(authSchemes);
|
|
2142
|
+
if (!schemeNames.length) return [];
|
|
2143
|
+
|
|
2144
|
+
const selectedScheme = getSelectedAuthSchemeForOperation(op);
|
|
2145
|
+
if (selectedScheme) {
|
|
2146
|
+
const requirement = chooseOperationSecurityRequirement(op);
|
|
2147
|
+
if (requirement && Object.prototype.hasOwnProperty.call(requirement, selectedScheme)) {
|
|
2148
|
+
return Object.keys(requirement).filter((schemeName) =>
|
|
2149
|
+
Object.prototype.hasOwnProperty.call(authSchemes, schemeName)
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
return [selectedScheme];
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
const requirement = chooseOperationSecurityRequirement(op);
|
|
2156
|
+
if (!requirement) return [];
|
|
2157
|
+
|
|
2158
|
+
return Object.keys(requirement).filter((schemeName) =>
|
|
2159
|
+
Object.prototype.hasOwnProperty.call(authSchemes, schemeName)
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
function getAuthHeaders(op) {
|
|
2164
|
+
const headers = {};
|
|
2165
|
+
const schemeNames = getAuthSchemeNamesForOperation(op);
|
|
2166
|
+
const allSchemeNames = Object.keys(authSchemes);
|
|
2167
|
+
|
|
2168
|
+
if (!allSchemeNames.length) {
|
|
2169
|
+
const state = authState["__default__"] || {};
|
|
2170
|
+
if (state.token) headers["Authorization"] = "Bearer " + state.token;
|
|
2171
|
+
return headers;
|
|
2172
|
+
}
|
|
2173
|
+
if (!schemeNames.length) return headers;
|
|
2174
|
+
|
|
2175
|
+
for (const schemeName of schemeNames) {
|
|
2176
|
+
const scheme = authSchemes[schemeName];
|
|
2177
|
+
const state = authState[schemeName] || {};
|
|
2178
|
+
const type = (scheme.type || "").toLowerCase();
|
|
2179
|
+
const httpScheme = (scheme.scheme || "").toLowerCase();
|
|
2180
|
+
|
|
2181
|
+
if (type === "http" && httpScheme === "basic") {
|
|
2182
|
+
if (state.username && state.password) {
|
|
2183
|
+
try {
|
|
2184
|
+
headers["Authorization"] = "Basic " + btoa(state.username + ":" + state.password);
|
|
2185
|
+
} catch {}
|
|
2186
|
+
}
|
|
2187
|
+
} else if (type === "http") {
|
|
2188
|
+
if (state.token) headers["Authorization"] = "Bearer " + state.token;
|
|
2189
|
+
} else if (type === "apikey" && (scheme.in || "").toLowerCase() === "header") {
|
|
2190
|
+
if (state.value && scheme.name) headers[scheme.name] = state.value;
|
|
2191
|
+
} else if (type === "oauth2" || type === "openidconnect") {
|
|
2192
|
+
if (state.token) headers["Authorization"] = "Bearer " + state.token;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
return headers;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
function getAuthQueryParams(op) {
|
|
2199
|
+
const params = {};
|
|
2200
|
+
for (const schemeName of getAuthSchemeNamesForOperation(op)) {
|
|
2201
|
+
const scheme = authSchemes[schemeName];
|
|
2202
|
+
if ((scheme.type || "").toLowerCase() === "apikey" && (scheme.in || "").toLowerCase() === "query") {
|
|
2203
|
+
const state = authState[schemeName] || {};
|
|
2204
|
+
if (state.value && scheme.name) params[scheme.name] = state.value;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return params;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
function getAuthCookieParams(op) {
|
|
2211
|
+
const cookies = {};
|
|
2212
|
+
for (const schemeName of getAuthSchemeNamesForOperation(op)) {
|
|
2213
|
+
const scheme = authSchemes[schemeName];
|
|
2214
|
+
if ((scheme.type || "").toLowerCase() !== "apikey") continue;
|
|
2215
|
+
if ((scheme.in || "").toLowerCase() !== "cookie") continue;
|
|
2216
|
+
const state = authState[schemeName] || {};
|
|
2217
|
+
if (state.value && scheme.name) {
|
|
2218
|
+
cookies[scheme.name] = state.value;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
return cookies;
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
function applyAuthCookies(op) {
|
|
2225
|
+
const cookies = getAuthCookieParams(op);
|
|
2226
|
+
for (const [name, value] of Object.entries(cookies)) {
|
|
2227
|
+
try {
|
|
2228
|
+
document.cookie = encodeURIComponent(String(name)) + "=" + encodeURIComponent(String(value)) + "; path=/";
|
|
2229
|
+
} catch {}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
function renderAuthPanel() {
|
|
2234
|
+
const fields = document.getElementById("auth-fields");
|
|
2235
|
+
if (!fields) return;
|
|
2236
|
+
fields.innerHTML = "";
|
|
2237
|
+
|
|
2238
|
+
const schemeNames = Object.keys(authSchemes);
|
|
2239
|
+
const op = selected;
|
|
2240
|
+
const operationSchemeOptions = op ? getAuthSchemeOptionsForOperation(op) : [];
|
|
2241
|
+
const availableSchemeOptions = Object.keys(authSchemes);
|
|
2242
|
+
const selectedScheme = op ? getSelectedAuthSchemeForOperation(op) : null;
|
|
2243
|
+
|
|
2244
|
+
function getAuthSchemeDisplayLabel(schemeName) {
|
|
2245
|
+
const scheme = authSchemes[schemeName] || {};
|
|
2246
|
+
const type = (scheme.type || "").toLowerCase();
|
|
2247
|
+
const httpScheme = (scheme.scheme || "").toLowerCase();
|
|
2248
|
+
const location = (scheme.in || "").toLowerCase();
|
|
2249
|
+
|
|
2250
|
+
if (type === "http" && httpScheme === "basic") return "HTTP Basic";
|
|
2251
|
+
if (type === "http" && httpScheme === "bearer") return "HTTP Bearer";
|
|
2252
|
+
if (type === "http" && httpScheme === "digest") return "HTTP Digest";
|
|
2253
|
+
if (type === "http") return "HTTP " + (httpScheme || "Token");
|
|
2254
|
+
if (type === "apikey") return "API Key" + (location ? " (" + location + ")" : "");
|
|
2255
|
+
if (type === "oauth2") return "OAuth 2.0";
|
|
2256
|
+
if (type === "openidconnect") return "OpenID Connect";
|
|
2257
|
+
if (type === "mutualtls") return "Mutual TLS";
|
|
2258
|
+
return schemeName;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
function makeInput(placeholder, value, onInput, type) {
|
|
2262
|
+
const inp = document.createElement("input");
|
|
2263
|
+
inp.type = type || "text";
|
|
2264
|
+
inp.value = value || "";
|
|
2265
|
+
inp.placeholder = placeholder;
|
|
2266
|
+
inp.className = "w-full text-xs px-2.5 py-2 rounded-md border border-light-border dark:border-dark-border bg-light-bg dark:bg-dark-bg focus:outline-none focus:border-brand dark:focus:border-brand transition-colors font-mono";
|
|
2267
|
+
inp.addEventListener("input", onInput);
|
|
2268
|
+
return inp;
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
function makeLabel(text, small) {
|
|
2272
|
+
const el = document.createElement("p");
|
|
2273
|
+
el.className = small
|
|
2274
|
+
? "text-[10px] font-medium uppercase tracking-wider opacity-55"
|
|
2275
|
+
: "text-[10px] font-semibold uppercase tracking-wider opacity-55";
|
|
2276
|
+
el.textContent = text;
|
|
2277
|
+
return el;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
function makeField(label, input) {
|
|
2281
|
+
const wrapper = document.createElement("div");
|
|
2282
|
+
wrapper.className = "space-y-1";
|
|
2283
|
+
wrapper.appendChild(makeLabel(label, true));
|
|
2284
|
+
wrapper.appendChild(input);
|
|
2285
|
+
return wrapper;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
function makeSchemeCard(title, subtitle) {
|
|
2289
|
+
const card = document.createElement("div");
|
|
2290
|
+
card.className = "space-y-2 rounded-md border border-light-border dark:border-dark-border bg-light-bg/40 dark:bg-dark-bg/40 p-2.5";
|
|
2291
|
+
|
|
2292
|
+
const titleRow = document.createElement("div");
|
|
2293
|
+
titleRow.className = "flex items-center justify-between gap-2";
|
|
2294
|
+
|
|
2295
|
+
const heading = document.createElement("p");
|
|
2296
|
+
heading.className = "text-[11px] font-semibold tracking-wide";
|
|
2297
|
+
heading.textContent = title;
|
|
2298
|
+
titleRow.appendChild(heading);
|
|
2299
|
+
|
|
2300
|
+
if (subtitle) {
|
|
2301
|
+
const note = document.createElement("span");
|
|
2302
|
+
note.className = "text-[10px] opacity-60 font-mono";
|
|
2303
|
+
note.textContent = subtitle;
|
|
2304
|
+
titleRow.appendChild(note);
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
card.appendChild(titleRow);
|
|
2308
|
+
return card;
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
if (!schemeNames.length) {
|
|
2312
|
+
if (!authState["__default__"]) authState["__default__"] = {};
|
|
2313
|
+
const defaultCard = makeSchemeCard("Default Auth", "bearer");
|
|
2314
|
+
defaultCard.appendChild(makeField("Token", makeInput("Enter token…", authState["__default__"].token, function(e) {
|
|
2315
|
+
authState["__default__"].token = e.target.value;
|
|
2316
|
+
saveAuthState();
|
|
2317
|
+
updateRequestPreview();
|
|
2318
|
+
})));
|
|
2319
|
+
fields.appendChild(defaultCard);
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
if (op && availableSchemeOptions.length > 0) {
|
|
2324
|
+
const selectorWrap = document.createElement("div");
|
|
2325
|
+
selectorWrap.className = "space-y-1";
|
|
2326
|
+
selectorWrap.appendChild(makeLabel("Auth Type"));
|
|
2327
|
+
const select = document.createElement("select");
|
|
2328
|
+
select.className = "w-full text-xs px-2.5 py-2 rounded-md border border-light-border dark:border-dark-border bg-light-bg dark:bg-dark-bg focus:outline-none focus:border-brand dark:focus:border-brand transition-colors font-mono";
|
|
2329
|
+
|
|
2330
|
+
const autoOption = document.createElement("option");
|
|
2331
|
+
autoOption.value = "";
|
|
2332
|
+
autoOption.textContent = "Auto";
|
|
2333
|
+
select.appendChild(autoOption);
|
|
2334
|
+
|
|
2335
|
+
for (const schemeName of availableSchemeOptions) {
|
|
2336
|
+
const option = document.createElement("option");
|
|
2337
|
+
option.value = schemeName;
|
|
2338
|
+
const isOperationScheme = operationSchemeOptions.includes(schemeName);
|
|
2339
|
+
const label = getAuthSchemeDisplayLabel(schemeName);
|
|
2340
|
+
option.textContent = isOperationScheme
|
|
2341
|
+
? label
|
|
2342
|
+
: (label + " • override");
|
|
2343
|
+
select.appendChild(option);
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
select.value = selectedScheme || "";
|
|
2347
|
+
select.addEventListener("change", function(e) {
|
|
2348
|
+
setSelectedAuthSchemeForOperation(op, e.target.value || "");
|
|
2349
|
+
renderAuthPanel();
|
|
2350
|
+
updateRequestPreview();
|
|
2351
|
+
});
|
|
2352
|
+
selectorWrap.appendChild(select);
|
|
2353
|
+
fields.appendChild(selectorWrap);
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
const schemesToRender = selectedScheme
|
|
2357
|
+
? [selectedScheme]
|
|
2358
|
+
: (operationSchemeOptions.length ? operationSchemeOptions : schemeNames);
|
|
2359
|
+
|
|
2360
|
+
for (const schemeName of schemesToRender) {
|
|
2361
|
+
const scheme = authSchemes[schemeName];
|
|
2362
|
+
if (!authState[schemeName]) authState[schemeName] = {};
|
|
2363
|
+
const state = authState[schemeName];
|
|
2364
|
+
const type = (scheme.type || "").toLowerCase();
|
|
2365
|
+
const httpScheme = (scheme.scheme || "").toLowerCase();
|
|
2366
|
+
const card = makeSchemeCard(getAuthSchemeDisplayLabel(schemeName), schemeName);
|
|
2367
|
+
|
|
2368
|
+
if (type === "http" && httpScheme === "basic") {
|
|
2369
|
+
card.appendChild(makeField("Username", makeInput("Username", state.username, function(e) {
|
|
2370
|
+
authState[schemeName].username = e.target.value;
|
|
2371
|
+
saveAuthState();
|
|
2372
|
+
updateRequestPreview();
|
|
2373
|
+
})));
|
|
2374
|
+
card.appendChild(makeField("Password", makeInput("Password", state.password, function(e) {
|
|
2375
|
+
authState[schemeName].password = e.target.value;
|
|
2376
|
+
saveAuthState();
|
|
2377
|
+
updateRequestPreview();
|
|
2378
|
+
}, "password")));
|
|
2379
|
+
} else if (type === "apikey") {
|
|
2380
|
+
const paramName = scheme.name || "key";
|
|
2381
|
+
const location = (scheme.in || "header").toLowerCase();
|
|
2382
|
+
card.appendChild(makeField("API Key", makeInput(paramName + " (" + location + ")", state.value, function(e) {
|
|
2383
|
+
authState[schemeName].value = e.target.value;
|
|
2384
|
+
saveAuthState();
|
|
2385
|
+
updateRequestPreview();
|
|
2386
|
+
})));
|
|
2387
|
+
} else if (type === "oauth2") {
|
|
2388
|
+
card.appendChild(makeField("Access Token", makeInput("OAuth2 access token…", state.token, function(e) {
|
|
2389
|
+
authState[schemeName].token = e.target.value;
|
|
2390
|
+
saveAuthState();
|
|
2391
|
+
updateRequestPreview();
|
|
2392
|
+
})));
|
|
2393
|
+
} else if (type === "openidconnect") {
|
|
2394
|
+
card.appendChild(makeField("ID Token / Access Token", makeInput("OpenID Connect token…", state.token, function(e) {
|
|
2395
|
+
authState[schemeName].token = e.target.value;
|
|
2396
|
+
saveAuthState();
|
|
2397
|
+
updateRequestPreview();
|
|
2398
|
+
})));
|
|
2399
|
+
} else if (type === "http" && httpScheme === "digest") {
|
|
2400
|
+
card.appendChild(makeField("Digest Credential", makeInput("Digest token…", state.token, function(e) {
|
|
2401
|
+
authState[schemeName].token = e.target.value;
|
|
2402
|
+
saveAuthState();
|
|
2403
|
+
updateRequestPreview();
|
|
2404
|
+
})));
|
|
2405
|
+
} else if (type === "http" && httpScheme === "bearer") {
|
|
2406
|
+
card.appendChild(makeField("Bearer Token", makeInput("Bearer token…", state.token, function(e) {
|
|
2407
|
+
authState[schemeName].token = e.target.value;
|
|
2408
|
+
saveAuthState();
|
|
2409
|
+
updateRequestPreview();
|
|
2410
|
+
})));
|
|
2411
|
+
} else if (type === "mutualtls") {
|
|
2412
|
+
const hint = document.createElement("p");
|
|
2413
|
+
hint.className = "text-xs opacity-70 leading-relaxed";
|
|
2414
|
+
hint.textContent = "Configured by your client certificate. No token input required.";
|
|
2415
|
+
card.appendChild(hint);
|
|
2416
|
+
} else {
|
|
2417
|
+
card.appendChild(makeField("Token", makeInput("Token…", state.token, function(e) {
|
|
2418
|
+
authState[schemeName].token = e.target.value;
|
|
2419
|
+
saveAuthState();
|
|
2420
|
+
updateRequestPreview();
|
|
2421
|
+
})));
|
|
2422
|
+
}
|
|
2423
|
+
fields.appendChild(card);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
let authPanelOpen = true;
|
|
2428
|
+
document.getElementById("auth-toggle").addEventListener("click", function() {
|
|
2429
|
+
authPanelOpen = !authPanelOpen;
|
|
2430
|
+
const fieldsEl = document.getElementById("auth-fields");
|
|
2431
|
+
const chevron = document.getElementById("auth-chevron");
|
|
2432
|
+
if (fieldsEl) fieldsEl.classList.toggle("hidden", !authPanelOpen);
|
|
2433
|
+
if (chevron) chevron.style.transform = authPanelOpen ? "" : "rotate(-90deg)";
|
|
2434
|
+
});
|
|
2435
|
+
|
|
2436
|
+
// Restore selected operation from URL hash, or set hash for the default selection
|
|
2437
|
+
var initMatch = findOpByHash(window.location.hash);
|
|
2438
|
+
if (initMatch) {
|
|
2439
|
+
selected = initMatch;
|
|
2440
|
+
} else if (selected) {
|
|
2441
|
+
history.replaceState(null, "", getOpHash(selected));
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
window.addEventListener("popstate", function() {
|
|
2445
|
+
var match = findOpByHash(window.location.hash);
|
|
2446
|
+
if (match) {
|
|
2447
|
+
selected = match;
|
|
2448
|
+
renderSidebar();
|
|
2449
|
+
renderEndpoint();
|
|
2450
|
+
}
|
|
2451
|
+
});
|
|
2452
|
+
|
|
1418
2453
|
setMobileSidebarOpen(false);
|
|
2454
|
+
renderAuthPanel();
|
|
1419
2455
|
renderSidebar();
|
|
1420
2456
|
renderEndpoint();
|
|
1421
2457
|
</script>
|