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.
Files changed (189) hide show
  1. package/README.md +18 -6
  2. package/dist/auth/protected.d.ts +4 -4
  3. package/dist/auth/protected.d.ts.map +1 -1
  4. package/dist/auth/protected.js +10 -7
  5. package/dist/auth/protected.js.map +1 -1
  6. package/dist/cache/manager.d.ts +2 -0
  7. package/dist/cache/manager.d.ts.map +1 -1
  8. package/dist/cache/manager.js +21 -4
  9. package/dist/cache/manager.js.map +1 -1
  10. package/dist/checkpoint/artifacts/compressor.d.ts +5 -0
  11. package/dist/checkpoint/artifacts/compressor.d.ts.map +1 -0
  12. package/dist/checkpoint/artifacts/compressor.js +24 -0
  13. package/dist/checkpoint/artifacts/compressor.js.map +1 -0
  14. package/dist/checkpoint/artifacts/decompress-worker.d.ts +2 -0
  15. package/dist/checkpoint/artifacts/decompress-worker.d.ts.map +1 -0
  16. package/dist/checkpoint/artifacts/decompress-worker.js +31 -0
  17. package/dist/checkpoint/artifacts/decompress-worker.js.map +1 -0
  18. package/dist/checkpoint/artifacts/hasher.d.ts +2 -0
  19. package/dist/checkpoint/artifacts/hasher.d.ts.map +1 -0
  20. package/dist/checkpoint/artifacts/hasher.js +7 -0
  21. package/dist/checkpoint/artifacts/hasher.js.map +1 -0
  22. package/dist/checkpoint/artifacts/manifest.d.ts +6 -0
  23. package/dist/checkpoint/artifacts/manifest.d.ts.map +1 -0
  24. package/dist/checkpoint/artifacts/manifest.js +55 -0
  25. package/dist/checkpoint/artifacts/manifest.js.map +1 -0
  26. package/dist/checkpoint/artifacts/materializer.d.ts +16 -0
  27. package/dist/checkpoint/artifacts/materializer.d.ts.map +1 -0
  28. package/dist/checkpoint/artifacts/materializer.js +168 -0
  29. package/dist/checkpoint/artifacts/materializer.js.map +1 -0
  30. package/dist/checkpoint/artifacts/packager.d.ts +12 -0
  31. package/dist/checkpoint/artifacts/packager.d.ts.map +1 -0
  32. package/dist/checkpoint/artifacts/packager.js +82 -0
  33. package/dist/checkpoint/artifacts/packager.js.map +1 -0
  34. package/dist/checkpoint/artifacts/repository.d.ts +11 -0
  35. package/dist/checkpoint/artifacts/repository.d.ts.map +1 -0
  36. package/dist/checkpoint/artifacts/repository.js +29 -0
  37. package/dist/checkpoint/artifacts/repository.js.map +1 -0
  38. package/dist/checkpoint/artifacts/store.d.ts +13 -0
  39. package/dist/checkpoint/artifacts/store.d.ts.map +1 -0
  40. package/dist/checkpoint/artifacts/store.js +85 -0
  41. package/dist/checkpoint/artifacts/store.js.map +1 -0
  42. package/dist/checkpoint/artifacts/types.d.ts +21 -0
  43. package/dist/checkpoint/artifacts/types.d.ts.map +1 -0
  44. package/dist/checkpoint/artifacts/types.js +2 -0
  45. package/dist/checkpoint/artifacts/types.js.map +1 -0
  46. package/dist/checkpoint/artifacts/worker-decompressor.d.ts +17 -0
  47. package/dist/checkpoint/artifacts/worker-decompressor.d.ts.map +1 -0
  48. package/dist/checkpoint/artifacts/worker-decompressor.js +148 -0
  49. package/dist/checkpoint/artifacts/worker-decompressor.js.map +1 -0
  50. package/dist/checkpoint/asset-store.d.ts +10 -0
  51. package/dist/checkpoint/asset-store.d.ts.map +1 -0
  52. package/dist/checkpoint/asset-store.js +46 -0
  53. package/dist/checkpoint/asset-store.js.map +1 -0
  54. package/dist/checkpoint/bundler.d.ts +15 -0
  55. package/dist/checkpoint/bundler.d.ts.map +1 -0
  56. package/dist/checkpoint/bundler.js +45 -0
  57. package/dist/checkpoint/bundler.js.map +1 -0
  58. package/dist/checkpoint/cli.d.ts +2 -0
  59. package/dist/checkpoint/cli.d.ts.map +1 -0
  60. package/dist/checkpoint/cli.js +157 -0
  61. package/dist/checkpoint/cli.js.map +1 -0
  62. package/dist/checkpoint/entrypoint-generator.d.ts +17 -0
  63. package/dist/checkpoint/entrypoint-generator.d.ts.map +1 -0
  64. package/dist/checkpoint/entrypoint-generator.js +251 -0
  65. package/dist/checkpoint/entrypoint-generator.js.map +1 -0
  66. package/dist/checkpoint/forwarder.d.ts +6 -0
  67. package/dist/checkpoint/forwarder.d.ts.map +1 -0
  68. package/dist/checkpoint/forwarder.js +74 -0
  69. package/dist/checkpoint/forwarder.js.map +1 -0
  70. package/dist/checkpoint/gateway.d.ts +11 -0
  71. package/dist/checkpoint/gateway.d.ts.map +1 -0
  72. package/dist/checkpoint/gateway.js +30 -0
  73. package/dist/checkpoint/gateway.js.map +1 -0
  74. package/dist/checkpoint/ipc.d.ts +12 -0
  75. package/dist/checkpoint/ipc.d.ts.map +1 -0
  76. package/dist/checkpoint/ipc.js +96 -0
  77. package/dist/checkpoint/ipc.js.map +1 -0
  78. package/dist/checkpoint/manager.d.ts +20 -0
  79. package/dist/checkpoint/manager.d.ts.map +1 -0
  80. package/dist/checkpoint/manager.js +214 -0
  81. package/dist/checkpoint/manager.js.map +1 -0
  82. package/dist/checkpoint/process-manager.d.ts +35 -0
  83. package/dist/checkpoint/process-manager.d.ts.map +1 -0
  84. package/dist/checkpoint/process-manager.js +203 -0
  85. package/dist/checkpoint/process-manager.js.map +1 -0
  86. package/dist/checkpoint/resolver.d.ts +25 -0
  87. package/dist/checkpoint/resolver.d.ts.map +1 -0
  88. package/dist/checkpoint/resolver.js +95 -0
  89. package/dist/checkpoint/resolver.js.map +1 -0
  90. package/dist/checkpoint/socket-path.d.ts +2 -0
  91. package/dist/checkpoint/socket-path.d.ts.map +1 -0
  92. package/dist/checkpoint/socket-path.js +51 -0
  93. package/dist/checkpoint/socket-path.js.map +1 -0
  94. package/dist/checkpoint/types.d.ts +54 -0
  95. package/dist/checkpoint/types.d.ts.map +1 -0
  96. package/dist/checkpoint/types.js +2 -0
  97. package/dist/checkpoint/types.js.map +1 -0
  98. package/dist/cli/index.js +10 -2
  99. package/dist/cli/index.js.map +1 -1
  100. package/dist/cli/option-resolution.d.ts +1 -1
  101. package/dist/cli/option-resolution.d.ts.map +1 -1
  102. package/dist/cli/option-resolution.js.map +1 -1
  103. package/dist/cli.js +3709 -328
  104. package/dist/core/config-loader.d.ts +1 -0
  105. package/dist/core/config-loader.d.ts.map +1 -1
  106. package/dist/core/config-loader.js +10 -2
  107. package/dist/core/config-loader.js.map +1 -1
  108. package/dist/core/router.d.ts +24 -3
  109. package/dist/core/router.d.ts.map +1 -1
  110. package/dist/core/router.js +398 -249
  111. package/dist/core/router.js.map +1 -1
  112. package/dist/core/server.d.ts +2 -0
  113. package/dist/core/server.d.ts.map +1 -1
  114. package/dist/core/server.js +22 -8
  115. package/dist/core/server.js.map +1 -1
  116. package/dist/core/vector.d.ts +3 -0
  117. package/dist/core/vector.d.ts.map +1 -1
  118. package/dist/core/vector.js +51 -1
  119. package/dist/core/vector.js.map +1 -1
  120. package/dist/dev/route-scanner.d.ts.map +1 -1
  121. package/dist/dev/route-scanner.js +2 -1
  122. package/dist/dev/route-scanner.js.map +1 -1
  123. package/dist/http.d.ts +32 -7
  124. package/dist/http.d.ts.map +1 -1
  125. package/dist/http.js +144 -13
  126. package/dist/http.js.map +1 -1
  127. package/dist/index.cjs +1297 -74
  128. package/dist/index.d.ts +3 -2
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +2 -2
  131. package/dist/index.js.map +1 -1
  132. package/dist/index.mjs +1296 -73
  133. package/dist/middleware/manager.d.ts +3 -3
  134. package/dist/middleware/manager.d.ts.map +1 -1
  135. package/dist/middleware/manager.js +9 -8
  136. package/dist/middleware/manager.js.map +1 -1
  137. package/dist/openapi/docs-ui.d.ts.map +1 -1
  138. package/dist/openapi/docs-ui.js +1097 -61
  139. package/dist/openapi/docs-ui.js.map +1 -1
  140. package/dist/openapi/generator.d.ts +2 -1
  141. package/dist/openapi/generator.d.ts.map +1 -1
  142. package/dist/openapi/generator.js +240 -7
  143. package/dist/openapi/generator.js.map +1 -1
  144. package/dist/types/index.d.ts +71 -28
  145. package/dist/types/index.d.ts.map +1 -1
  146. package/dist/types/index.js +24 -1
  147. package/dist/types/index.js.map +1 -1
  148. package/dist/utils/validation.d.ts.map +1 -1
  149. package/dist/utils/validation.js +3 -2
  150. package/dist/utils/validation.js.map +1 -1
  151. package/package.json +2 -1
  152. package/src/auth/protected.ts +11 -8
  153. package/src/cache/manager.ts +23 -4
  154. package/src/checkpoint/artifacts/compressor.ts +30 -0
  155. package/src/checkpoint/artifacts/decompress-worker.ts +49 -0
  156. package/src/checkpoint/artifacts/hasher.ts +6 -0
  157. package/src/checkpoint/artifacts/manifest.ts +72 -0
  158. package/src/checkpoint/artifacts/materializer.ts +211 -0
  159. package/src/checkpoint/artifacts/packager.ts +100 -0
  160. package/src/checkpoint/artifacts/repository.ts +36 -0
  161. package/src/checkpoint/artifacts/store.ts +102 -0
  162. package/src/checkpoint/artifacts/types.ts +24 -0
  163. package/src/checkpoint/artifacts/worker-decompressor.ts +192 -0
  164. package/src/checkpoint/asset-store.ts +61 -0
  165. package/src/checkpoint/bundler.ts +64 -0
  166. package/src/checkpoint/cli.ts +177 -0
  167. package/src/checkpoint/entrypoint-generator.ts +275 -0
  168. package/src/checkpoint/forwarder.ts +84 -0
  169. package/src/checkpoint/gateway.ts +40 -0
  170. package/src/checkpoint/ipc.ts +107 -0
  171. package/src/checkpoint/manager.ts +254 -0
  172. package/src/checkpoint/process-manager.ts +250 -0
  173. package/src/checkpoint/resolver.ts +124 -0
  174. package/src/checkpoint/socket-path.ts +61 -0
  175. package/src/checkpoint/types.ts +63 -0
  176. package/src/cli/index.ts +11 -2
  177. package/src/cli/option-resolution.ts +5 -1
  178. package/src/core/config-loader.ts +11 -2
  179. package/src/core/router.ts +505 -264
  180. package/src/core/server.ts +36 -9
  181. package/src/core/vector.ts +60 -1
  182. package/src/dev/route-scanner.ts +2 -1
  183. package/src/http.ts +219 -19
  184. package/src/index.ts +3 -2
  185. package/src/middleware/manager.ts +10 -10
  186. package/src/openapi/docs-ui.ts +1097 -61
  187. package/src/openapi/generator.ts +265 -6
  188. package/src/types/index.ts +83 -30
  189. package/src/utils/validation.ts +5 -3
@@ -144,6 +144,124 @@ export function renderOpenAPIDocsHtml(
144
144
  .dark .json-number { color: #7dc9ff; }
145
145
  .dark .json-boolean { color: #93a4bf; }
146
146
  .dark .json-null { color: #7c8ba3; }
147
+ .param-row {
148
+ --param-row-bg-rgb: 255 255 255;
149
+ }
150
+ .dark .param-row {
151
+ --param-row-bg-rgb: 10 10 10;
152
+ }
153
+ .param-row-head {
154
+ display: grid;
155
+ grid-template-columns: minmax(0, 1fr) auto;
156
+ align-items: center;
157
+ gap: 0.5rem;
158
+ min-width: 0;
159
+ width: 100%;
160
+ }
161
+ .param-row-main {
162
+ min-width: 0;
163
+ display: flex;
164
+ align-items: center;
165
+ gap: 0.4rem;
166
+ overflow: hidden;
167
+ }
168
+ .param-tooltip-trigger {
169
+ border: 0;
170
+ margin: 0;
171
+ padding: 0;
172
+ background: transparent;
173
+ color: inherit;
174
+ cursor: pointer;
175
+ font: inherit;
176
+ text-align: left;
177
+ min-width: 0;
178
+ }
179
+ .param-tooltip-trigger:focus-visible {
180
+ outline: 2px solid rgba(0, 161, 255, 0.65);
181
+ outline-offset: 2px;
182
+ border-radius: 0.375rem;
183
+ }
184
+ .param-name-trigger {
185
+ min-width: 0;
186
+ flex: 1 1 auto;
187
+ overflow: hidden;
188
+ }
189
+ .param-name-text {
190
+ display: block;
191
+ max-width: 100%;
192
+ overflow: hidden;
193
+ text-overflow: ellipsis;
194
+ white-space: nowrap;
195
+ mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 18px), transparent 100%);
196
+ -webkit-mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 18px), transparent 100%);
197
+ }
198
+ .param-type-fade {
199
+ position: relative;
200
+ z-index: 1;
201
+ display: block;
202
+ max-width: none;
203
+ overflow: visible;
204
+ text-overflow: clip;
205
+ padding-left: 1.25rem;
206
+ white-space: nowrap;
207
+ text-align: left;
208
+ justify-self: end;
209
+ background: linear-gradient(
210
+ 90deg,
211
+ rgba(var(--param-row-bg-rgb), 0) 0%,
212
+ rgba(var(--param-row-bg-rgb), 0.76) 36%,
213
+ rgba(var(--param-row-bg-rgb), 0.94) 68%,
214
+ rgba(var(--param-row-bg-rgb), 1) 100%
215
+ );
216
+ }
217
+ #param-value-tooltip {
218
+ position: fixed;
219
+ top: 0;
220
+ left: 0;
221
+ z-index: 70;
222
+ width: min(42rem, calc(100vw - 0.75rem));
223
+ border-radius: 0.5rem;
224
+ border: 1px solid rgba(15, 23, 42, 0.12);
225
+ background: rgba(255, 255, 255, 0.92);
226
+ color: #111111;
227
+ box-shadow: 0 10px 20px rgba(15, 23, 42, 0.14);
228
+ backdrop-filter: blur(12px) saturate(145%);
229
+ -webkit-backdrop-filter: blur(12px) saturate(145%);
230
+ padding: 0.4rem 0.6rem;
231
+ opacity: 0;
232
+ pointer-events: none;
233
+ transform: translateY(6px) scale(0.98);
234
+ transition:
235
+ opacity var(--motion-fast) var(--motion-ease),
236
+ transform var(--motion-fast) var(--motion-ease);
237
+ }
238
+ #param-value-tooltip.is-visible {
239
+ opacity: 1;
240
+ pointer-events: auto;
241
+ transform: translateY(0) scale(1);
242
+ }
243
+ .dark #param-value-tooltip {
244
+ border-color: rgba(148, 163, 184, 0.24);
245
+ background: rgba(17, 17, 17, 0.9);
246
+ color: #ededed;
247
+ box-shadow: 0 14px 30px rgba(0, 0, 0, 0.45);
248
+ }
249
+ #param-tooltip-line {
250
+ margin: 0;
251
+ font-size: 11px;
252
+ line-height: 1.3;
253
+ font-family: "JetBrains Mono", monospace;
254
+ white-space: normal;
255
+ word-break: break-word;
256
+ }
257
+ #param-tooltip-description {
258
+ margin: 0.2rem 0 0;
259
+ font-size: 11px;
260
+ line-height: 1.3;
261
+ opacity: 0.8;
262
+ white-space: normal;
263
+ word-break: break-word;
264
+ }
147
265
  </style>
148
266
  </head>
149
267
  <body class="bg-light-bg text-light-text dark:bg-dark-bg dark:text-dark-text font-sans antialiased flex h-screen overflow-hidden">
@@ -173,6 +291,16 @@ export function renderOpenAPIDocsHtml(
173
291
  />
174
292
  </div>
175
293
  </div>
294
+ <div id="auth-panel" class="border-b border-light-border dark:border-dark-border">
295
+ <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">
296
+ <span class="flex items-center gap-1.5">
297
+ <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>
298
+ Auth
299
+ </span>
300
+ <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>
301
+ </button>
302
+ <div id="auth-fields" class="px-4 pb-3 space-y-2"></div>
303
+ </div>
176
304
  <nav class="flex-1 overflow-y-auto px-3 py-2 space-y-6 text-sm" id="sidebar-nav"></nav>
177
305
  </aside>
178
306
 
@@ -206,6 +334,9 @@ export function renderOpenAPIDocsHtml(
206
334
  <span id="endpoint-method" class="px-2.5 py-0.5 rounded-full text-xs font-mono font-medium"></span>
207
335
  <h2 class="text-xl font-semibold tracking-tight" id="endpoint-title">Operation</h2>
208
336
  </div>
337
+ <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">
338
+ ! This operation is deprecated
339
+ </div>
209
340
  <p class="text-sm opacity-80 mb-8 font-mono" id="endpoint-path">/</p>
210
341
  <div class="grid grid-cols-1 lg:grid-cols-12 gap-10">
211
342
  <div class="lg:col-span-5 space-y-8" id="params-column"></div>
@@ -301,6 +432,10 @@ export function renderOpenAPIDocsHtml(
301
432
  <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>
302
433
  </div>
303
434
  </div>
435
+ <div id="param-value-tooltip" aria-hidden="true" role="tooltip">
436
+ <p id="param-tooltip-line"></p>
437
+ <p id="param-tooltip-description" class="hidden"></p>
438
+ </div>
304
439
 
305
440
  <script>
306
441
  const spec = ${specJson};
@@ -371,14 +506,71 @@ export function renderOpenAPIDocsHtml(
371
506
  return ops;
372
507
  }
373
508
 
509
+ const AUTH_STATE_KEY = "vector-docs-auth-v1";
510
+ const AUTH_SELECTION_KEY = "vector-docs-auth-selection-v1";
511
+ const HEADERS_STATE_KEY = "vector-docs-headers-v1";
512
+
513
+ function loadSavedHeaders() {
514
+ try {
515
+ const raw = localStorage.getItem(HEADERS_STATE_KEY);
516
+ if (raw) {
517
+ const parsed = JSON.parse(raw);
518
+ if (Array.isArray(parsed) && parsed.length > 0) return parsed;
519
+ }
520
+ } catch {}
521
+ return [{ key: "", value: "" }];
522
+ }
523
+
524
+ function saveHeaders() {
525
+ try { localStorage.setItem(HEADERS_STATE_KEY, JSON.stringify(requestHeaders)); } catch {}
526
+ }
527
+
528
+ function loadAuthState() {
529
+ try {
530
+ const raw = localStorage.getItem(AUTH_STATE_KEY);
531
+ if (raw) return JSON.parse(raw);
532
+ } catch {}
533
+ return {};
534
+ }
535
+
536
+ function saveAuthState() {
537
+ try { localStorage.setItem(AUTH_STATE_KEY, JSON.stringify(authState)); } catch {}
538
+ }
539
+
540
+ function loadAuthSelectionState() {
541
+ try {
542
+ const raw = localStorage.getItem(AUTH_SELECTION_KEY);
543
+ if (raw) {
544
+ const parsed = JSON.parse(raw);
545
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
546
+ return parsed;
547
+ }
548
+ }
549
+ } catch {}
550
+ return {};
551
+ }
552
+
553
+ function saveAuthSelectionState() {
554
+ try { localStorage.setItem(AUTH_SELECTION_KEY, JSON.stringify(authSelectionState)); } catch {}
555
+ }
556
+
557
+ const authSchemes = (spec.components && spec.components.securitySchemes) || {};
558
+ let authState = loadAuthState();
559
+ let authSelectionState = loadAuthSelectionState();
560
+
374
561
  const operations = getOperations();
375
562
  let selected = operations[0] || null;
376
563
  const operationParamValues = new Map();
377
564
  const operationBodyDrafts = new Map();
378
- const requestHeaders = [{ key: "Authorization", value: "" }];
565
+ const requestHeaders = loadSavedHeaders();
379
566
  let expandModalMode = null;
380
567
  let isMobileSidebarOpen = false;
381
568
  let sidebarSearchQuery = "";
569
+ const paramTooltipRoot = document.getElementById("param-value-tooltip");
570
+ const paramTooltipLine = document.getElementById("param-tooltip-line");
571
+ const paramTooltipDescription = document.getElementById("param-tooltip-description");
572
+ let activeParamTooltipTrigger = null;
573
+ let paramTooltipHideTimer = null;
382
574
 
383
575
  function setMobileSidebarOpen(open) {
384
576
  const sidebar = document.getElementById("docs-sidebar");
@@ -398,6 +590,29 @@ export function renderOpenAPIDocsHtml(
398
590
  return op.method + " " + op.path;
399
591
  }
400
592
 
593
+ function getOpHash(op) {
594
+ var tag = op.tag || "default";
595
+ var id = (op.operation && op.operation.operationId)
596
+ ? op.operation.operationId
597
+ : op.method.toLowerCase() + "_" + op.path.split("/").filter(Boolean).join("_").replace(/[{}]/g, "");
598
+ return "#/" + encodeURIComponent(tag) + "/" + encodeURIComponent(id);
599
+ }
600
+
601
+ function findOpByHash(hash) {
602
+ if (!hash || hash.length <= 1) return null;
603
+ var parts = hash.slice(1).split("/").filter(Boolean);
604
+ if (parts.length < 2) return null;
605
+ var hashTag = decodeURIComponent(parts[0]);
606
+ var hashId = decodeURIComponent(parts[1]);
607
+ return operations.find(function(op) {
608
+ if (op.tag !== hashTag) return false;
609
+ var id = (op.operation && op.operation.operationId)
610
+ ? op.operation.operationId
611
+ : op.method.toLowerCase() + "_" + op.path.split("/").filter(Boolean).join("_").replace(/[{}]/g, "");
612
+ return id === hashId;
613
+ }) || null;
614
+ }
615
+
401
616
  function getOperationParameterGroups(op) {
402
617
  const params =
403
618
  op &&
@@ -447,7 +662,7 @@ export function renderOpenAPIDocsHtml(
447
662
  return resolved;
448
663
  }
449
664
 
450
- function buildRequestPath(op, pathParams, queryParams, values) {
665
+ function buildRequestPath(op, pathParams, queryParams, values, extraQuery) {
451
666
  const resolvedPath = resolvePath(op.path, pathParams, values);
452
667
  const query = new URLSearchParams();
453
668
 
@@ -459,6 +674,13 @@ export function renderOpenAPIDocsHtml(
459
674
  query.append(param.name, String(rawValue));
460
675
  }
461
676
 
677
+ if (extraQuery) {
678
+ for (const key of Object.keys(extraQuery)) {
679
+ const val = extraQuery[key];
680
+ if (val) query.set(key, String(val));
681
+ }
682
+ }
683
+
462
684
  const queryString = query.toString();
463
685
  return queryString ? resolvedPath + "?" + queryString : resolvedPath;
464
686
  }
@@ -598,14 +820,25 @@ export function renderOpenAPIDocsHtml(
598
820
 
599
821
  const name = document.createElement("span");
600
822
  name.textContent = op.name;
823
+ if (op.operation && op.operation.deprecated) {
824
+ name.style.textDecoration = "line-through";
825
+ name.style.opacity = "0.5";
826
+ }
601
827
 
602
828
  row.appendChild(method);
603
829
  row.appendChild(name);
830
+ if (op.operation && op.operation.deprecated) {
831
+ const badge = document.createElement("span");
832
+ 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";
833
+ badge.textContent = "deprecated";
834
+ row.appendChild(badge);
835
+ }
604
836
  a.appendChild(row);
605
837
 
606
838
  a.onclick = (e) => {
607
839
  e.preventDefault();
608
840
  selected = op;
841
+ history.pushState(null, "", getOpHash(op));
609
842
  renderSidebar();
610
843
  renderEndpoint();
611
844
  if (window.innerWidth < 768) {
@@ -619,51 +852,257 @@ export function renderOpenAPIDocsHtml(
619
852
  }
620
853
  }
621
854
 
855
+ function hideParamTooltip() {
856
+ if (!paramTooltipRoot) return;
857
+ if (paramTooltipHideTimer) {
858
+ window.clearTimeout(paramTooltipHideTimer);
859
+ paramTooltipHideTimer = null;
860
+ }
861
+ paramTooltipRoot.classList.remove("is-visible");
862
+ paramTooltipRoot.setAttribute("aria-hidden", "true");
863
+ if (activeParamTooltipTrigger) {
864
+ activeParamTooltipTrigger.setAttribute("aria-expanded", "false");
865
+ }
866
+ activeParamTooltipTrigger = null;
867
+ }
868
+
869
+ function scheduleParamTooltipHide() {
870
+ if (paramTooltipHideTimer) {
871
+ window.clearTimeout(paramTooltipHideTimer);
872
+ }
873
+ paramTooltipHideTimer = window.setTimeout(() => {
874
+ hideParamTooltip();
875
+ }, 95);
876
+ }
877
+
878
+ function positionParamTooltip(trigger) {
879
+ if (!paramTooltipRoot || !trigger) return;
880
+ const viewportPadding = 8;
881
+ const spacing = 10;
882
+ const triggerRect = trigger.getBoundingClientRect();
883
+ const tooltipRect = paramTooltipRoot.getBoundingClientRect();
884
+ let left = triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2);
885
+ left = Math.max(viewportPadding, Math.min(left, window.innerWidth - tooltipRect.width - viewportPadding));
886
+ let top = triggerRect.top - tooltipRect.height - spacing;
887
+ if (top < viewportPadding) {
888
+ top = triggerRect.bottom + spacing;
889
+ }
890
+ if (top + tooltipRect.height > window.innerHeight - viewportPadding) {
891
+ top = window.innerHeight - tooltipRect.height - viewportPadding;
892
+ }
893
+ paramTooltipRoot.style.left = Math.round(left) + "px";
894
+ paramTooltipRoot.style.top = Math.round(top) + "px";
895
+ }
896
+
897
+ function showParamTooltip(trigger) {
898
+ if (
899
+ !paramTooltipRoot ||
900
+ !paramTooltipLine ||
901
+ !paramTooltipDescription ||
902
+ !trigger
903
+ ) {
904
+ return;
905
+ }
906
+ if (paramTooltipHideTimer) {
907
+ window.clearTimeout(paramTooltipHideTimer);
908
+ paramTooltipHideTimer = null;
909
+ }
910
+ const label = trigger.getAttribute("data-param-tooltip-label") || "Value";
911
+ const value = trigger.getAttribute("data-param-tooltip-value") || "";
912
+ const related = trigger.getAttribute("data-param-tooltip-related") || "";
913
+ const description = trigger.getAttribute("data-param-tooltip-description") || "";
914
+ if (activeParamTooltipTrigger && activeParamTooltipTrigger !== trigger) {
915
+ activeParamTooltipTrigger.setAttribute("aria-expanded", "false");
916
+ }
917
+ activeParamTooltipTrigger = trigger;
918
+ activeParamTooltipTrigger.setAttribute("aria-expanded", "true");
919
+ const pathLabel = related ? " | path: " + related : "";
920
+ paramTooltipLine.textContent = label + ": " + value + pathLabel;
921
+ if (description.trim()) {
922
+ paramTooltipDescription.textContent = description;
923
+ paramTooltipDescription.classList.remove("hidden");
924
+ } else {
925
+ paramTooltipDescription.textContent = "";
926
+ paramTooltipDescription.classList.add("hidden");
927
+ }
928
+ paramTooltipRoot.classList.add("is-visible");
929
+ paramTooltipRoot.setAttribute("aria-hidden", "false");
930
+ positionParamTooltip(trigger);
931
+ }
932
+
933
+ function registerParamTooltipTargets(scope) {
934
+ if (!scope) return;
935
+ const targets = scope.querySelectorAll("[data-param-tooltip-value]");
936
+ for (const target of targets) {
937
+ target.addEventListener("click", (event) => {
938
+ event.preventDefault();
939
+ if (
940
+ activeParamTooltipTrigger === target &&
941
+ paramTooltipRoot &&
942
+ paramTooltipRoot.classList.contains("is-visible")
943
+ ) {
944
+ hideParamTooltip();
945
+ return;
946
+ }
947
+ showParamTooltip(target);
948
+ });
949
+ target.addEventListener("mouseenter", () => {
950
+ showParamTooltip(target);
951
+ });
952
+ target.addEventListener("mouseleave", (event) => {
953
+ const related = event.relatedTarget;
954
+ if (paramTooltipRoot && related && paramTooltipRoot.contains(related)) return;
955
+ scheduleParamTooltipHide();
956
+ });
957
+ target.addEventListener("focus", () => {
958
+ showParamTooltip(target);
959
+ });
960
+ target.addEventListener("blur", (event) => {
961
+ const related = event.relatedTarget;
962
+ if (paramTooltipRoot && related && paramTooltipRoot.contains(related)) return;
963
+ scheduleParamTooltipHide();
964
+ });
965
+ }
966
+ }
967
+
622
968
  function renderParamSection(title, params) {
623
969
  if (!params.length) return "";
624
970
  let rows = "";
625
971
  for (const p of params) {
626
- const type = escapeHtml((p.schema && p.schema.type) || "unknown");
627
- const name = escapeHtml(p.name || "");
628
- rows += '<div class="py-2 flex justify-between border-b border-light-border/50 dark:border-dark-border/50"><div><code class="text-sm font-mono">' + name + '</code><span class="text-xs text-brand ml-2">' + (p.required ? "required" : "optional") + '</span></div><span class="text-xs font-mono opacity-60">' + type + '</span></div>';
972
+ const schema = resolveSchemaRef(p.schema || {});
973
+ const typeRaw = getSchemaTypeLabel(schema);
974
+ const type = escapeHtml(typeRaw);
975
+ const nameRaw = p.name || "";
976
+ const name = escapeHtml(nameRaw);
977
+ const tooltipName = escapeHtmlAttribute(nameRaw);
978
+ const tooltipType = escapeHtmlAttribute(typeRaw);
979
+ const tooltipDescription = (typeof p.description === "string" && p.description.trim())
980
+ ? escapeHtmlAttribute(p.description.trim())
981
+ : (typeof schema.description === "string" && schema.description.trim())
982
+ ? escapeHtmlAttribute(schema.description.trim())
983
+ : "";
984
+ const desc = (typeof p.description === "string" && p.description.trim())
985
+ ? '<p class="text-xs opacity-60 mt-0.5 leading-snug">' + renderMarkdown(p.description.trim()) + '</p>'
986
+ : "";
987
+ const extra = buildSchemaExtra(schema);
988
+ rows +=
989
+ '<div class="param-row py-2 border-b border-light-border/50 dark:border-dark-border/50">' +
990
+ '<div class="param-row-head">' +
991
+ '<div class="param-row-main">' +
992
+ '<button type="button" class="param-tooltip-trigger param-name-trigger" data-param-tooltip-label="Parameter" data-param-tooltip-value="' +
993
+ tooltipName +
994
+ '" data-param-tooltip-description="' +
995
+ tooltipDescription +
996
+ '" aria-expanded="false">' +
997
+ '<code class="text-sm font-mono param-name-text">' +
998
+ name +
999
+ "</code></button>" +
1000
+ '<span class="text-xs text-brand shrink-0">' +
1001
+ (p.required ? "required" : "optional") +
1002
+ "</span></div>" +
1003
+ '<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="' +
1004
+ tooltipType +
1005
+ '" data-param-tooltip-description="' +
1006
+ tooltipDescription +
1007
+ '" aria-expanded="false">' +
1008
+ type +
1009
+ "</button></div>" +
1010
+ desc +
1011
+ extra +
1012
+ "</div>";
629
1013
  }
630
1014
  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>";
631
1015
  }
632
1016
 
633
1017
  function getSchemaTypeLabel(schema) {
634
- if (!schema || typeof schema !== "object") return "unknown";
635
- if (Array.isArray(schema.type)) return schema.type.join(" | ");
636
- if (schema.type) return String(schema.type);
637
- if (schema.properties) return "object";
638
- if (schema.items) return "array";
639
- if (Array.isArray(schema.oneOf)) return "oneOf";
640
- if (Array.isArray(schema.anyOf)) return "anyOf";
641
- if (Array.isArray(schema.allOf)) return "allOf";
1018
+ const resolved = resolveSchemaRef(schema);
1019
+ if (!resolved || typeof resolved !== "object") return "unknown";
1020
+ if (Array.isArray(resolved.type)) return resolved.type.join(" | ");
1021
+ if (resolved.type) return String(resolved.type);
1022
+ if (resolved.properties) return "object";
1023
+ if (resolved.items) return "array";
1024
+ if (Array.isArray(resolved.oneOf)) return "oneOf";
1025
+ if (Array.isArray(resolved.anyOf)) return "anyOf";
1026
+ if (Array.isArray(resolved.allOf)) return "allOf";
642
1027
  return "unknown";
643
1028
  }
644
1029
 
1030
+ function buildSchemaExtra(schema) {
1031
+ const resolved = resolveSchemaRef(schema);
1032
+ if (!resolved || typeof resolved !== "object") return "";
1033
+ const chips = [];
1034
+ if (resolved.format) chips.push(escapeHtml(String(resolved.format)));
1035
+ if (Array.isArray(resolved.enum) && resolved.enum.length > 0) {
1036
+ const shown = resolved.enum.slice(0, 5).map(function(v) { return escapeHtml(JSON.stringify(v)); });
1037
+ chips.push(shown.join(" | ") + (resolved.enum.length > 5 ? " …" : ""));
1038
+ }
1039
+ if (resolved.minimum !== undefined) chips.push("min: " + resolved.minimum);
1040
+ if (resolved.maximum !== undefined) chips.push("max: " + resolved.maximum);
1041
+ if (typeof resolved.exclusiveMinimum === "number") chips.push("&gt;" + resolved.exclusiveMinimum);
1042
+ if (typeof resolved.exclusiveMaximum === "number") chips.push("&lt;" + resolved.exclusiveMaximum);
1043
+ if (resolved.minLength !== undefined) chips.push("minLen: " + resolved.minLength);
1044
+ if (resolved.maxLength !== undefined) chips.push("maxLen: " + resolved.maxLength);
1045
+ if (resolved.minItems !== undefined) chips.push("minItems: " + resolved.minItems);
1046
+ if (resolved.maxItems !== undefined) chips.push("maxItems: " + resolved.maxItems);
1047
+ if (resolved.uniqueItems) chips.push("unique");
1048
+ if (resolved.pattern) chips.push("/" + escapeHtml(String(resolved.pattern)) + "/");
1049
+ if (!chips.length) return "";
1050
+ return '<div class="flex flex-wrap gap-1 mt-1.5">' +
1051
+ chips.map(function(c) {
1052
+ 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>';
1053
+ }).join("") +
1054
+ '</div>';
1055
+ }
1056
+
1057
+ function resolveSchemaRef(schema, visitedRefs) {
1058
+ if (!schema || typeof schema !== "object") return schema;
1059
+ const ref = typeof schema.$ref === "string" ? schema.$ref : "";
1060
+ if (!ref || !ref.startsWith("#/components/schemas/")) {
1061
+ return schema;
1062
+ }
1063
+
1064
+ const seen = visitedRefs || new Set();
1065
+ if (seen.has(ref)) return schema;
1066
+ seen.add(ref);
1067
+
1068
+ const parts = ref.split("/");
1069
+ const schemaName = parts[parts.length - 1];
1070
+ const referenced = spec && spec.components && spec.components.schemas && spec.components.schemas[schemaName];
1071
+ if (!referenced || typeof referenced !== "object") return schema;
1072
+
1073
+ const merged = Object.assign({}, referenced, schema);
1074
+ delete merged.$ref;
1075
+ return resolveSchemaRef(merged, seen);
1076
+ }
1077
+
645
1078
  function buildSchemaChildren(schema) {
646
- if (!schema || typeof schema !== "object") return [];
1079
+ const resolved = resolveSchemaRef(schema);
1080
+ if (!resolved || typeof resolved !== "object") return [];
647
1081
 
648
1082
  const children = [];
649
1083
 
650
- if (schema.properties && typeof schema.properties === "object") {
1084
+ if (resolved.properties && typeof resolved.properties === "object") {
651
1085
  const requiredSet = new Set(
652
- Array.isArray(schema.required) ? schema.required : [],
1086
+ Array.isArray(resolved.required) ? resolved.required : [],
653
1087
  );
654
- for (const [name, childSchema] of Object.entries(schema.properties)) {
1088
+ for (const [name, childSchema] of Object.entries(resolved.properties)) {
1089
+ const childDef = childSchema || {};
1090
+ const isArrayType = Array.isArray(childDef.type)
1091
+ ? childDef.type.includes("array")
1092
+ : childDef.type === "array";
1093
+ const isArrayLike = isArrayType || childDef.items !== undefined;
655
1094
  children.push({
656
- name,
657
- schema: childSchema || {},
1095
+ name: isArrayLike ? (name + "[]") : name,
1096
+ schema: childDef,
658
1097
  required: requiredSet.has(name),
659
1098
  });
660
1099
  }
661
1100
  }
662
1101
 
663
- if (schema.items) {
1102
+ if (resolved.items) {
664
1103
  children.push({
665
- name: "items[]",
666
- schema: schema.items,
1104
+ name: getArrayItemNodeName(resolved.items),
1105
+ schema: resolved.items,
667
1106
  required: true,
668
1107
  });
669
1108
  }
@@ -671,45 +1110,100 @@ export function renderOpenAPIDocsHtml(
671
1110
  return children;
672
1111
  }
673
1112
 
674
- function renderSchemaFieldNode(field, depth) {
675
- const schema = field.schema || {};
676
- const name = escapeHtml(field.name || "field");
1113
+ function getArrayItemNodeName(itemSchema) {
1114
+ if (!itemSchema || typeof itemSchema !== "object") return "item";
1115
+ const title =
1116
+ typeof itemSchema.title === "string" && itemSchema.title.trim()
1117
+ ? itemSchema.title.trim()
1118
+ : "";
1119
+ if (title) return title;
1120
+
1121
+ const ref =
1122
+ typeof itemSchema.$ref === "string" && itemSchema.$ref.trim()
1123
+ ? itemSchema.$ref.trim()
1124
+ : "";
1125
+ if (ref) {
1126
+ const parts = ref.split("/").filter(Boolean);
1127
+ const last = parts[parts.length - 1];
1128
+ if (last) return last;
1129
+ }
1130
+
1131
+ const typeLabel = getSchemaTypeLabel(itemSchema);
1132
+ if (typeLabel && typeLabel !== "unknown") return typeLabel;
1133
+ return "type";
1134
+ }
1135
+
1136
+ function renderSchemaFieldNode(field, depth, parentPath) {
1137
+ const schema = resolveSchemaRef(field.schema || {});
1138
+ const nameRaw = field.name || "field";
1139
+ const name = escapeHtml(nameRaw);
677
1140
  const requiredLabel = field.required ? "required" : "optional";
678
- const type = escapeHtml(getSchemaTypeLabel(schema));
1141
+ const typeRaw = getSchemaTypeLabel(schema);
1142
+ const type = escapeHtml(typeRaw);
1143
+ const tooltipName = escapeHtmlAttribute(nameRaw);
1144
+ const tooltipType = escapeHtmlAttribute(typeRaw);
1145
+ const fieldPath = parentPath ? (parentPath + "." + nameRaw) : nameRaw;
1146
+ const tooltipPath = escapeHtmlAttribute(fieldPath);
1147
+ const tooltipDescription = (typeof schema.description === "string" && schema.description.trim())
1148
+ ? escapeHtmlAttribute(schema.description.trim())
1149
+ : "";
679
1150
  const children = buildSchemaChildren(schema);
680
1151
  const padding = depth * 14;
1152
+ const extra = buildSchemaExtra(schema);
681
1153
 
682
1154
  if (!children.length) {
683
1155
  return (
684
- '<div class="py-2 border-b border-light-border/50 dark:border-dark-border/50" style="padding-left:' +
1156
+ '<div class="param-row py-2 border-b border-light-border/50 dark:border-dark-border/50" style="padding-left:' +
685
1157
  padding +
686
- 'px"><div class="flex justify-between"><div><code class="text-sm font-mono">' +
1158
+ 'px"><div class="param-row-head"><div class="param-row-main">' +
1159
+ '<button type="button" class="param-tooltip-trigger param-name-trigger" data-param-tooltip-label="Field" data-param-tooltip-value="' +
1160
+ tooltipName +
1161
+ '" data-param-tooltip-related="' +
1162
+ tooltipPath +
1163
+ '" data-param-tooltip-description="' +
1164
+ tooltipDescription +
1165
+ '" aria-expanded="false"><code class="text-sm font-mono param-name-text">' +
687
1166
  name +
688
- '</code><span class="text-xs text-brand ml-2">' +
1167
+ '</code></button><span class="text-xs text-brand shrink-0">' +
689
1168
  requiredLabel +
690
- '</span></div><span class="text-xs font-mono opacity-60">' +
1169
+ '</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="' +
1170
+ tooltipType +
1171
+ '" data-param-tooltip-description="' +
1172
+ tooltipDescription +
1173
+ '" aria-expanded="false">' +
691
1174
  type +
692
- "</span></div></div>"
1175
+ "</button></div>" + extra + "</div>"
693
1176
  );
694
1177
  }
695
1178
 
696
1179
  let nested = "";
697
1180
  for (const child of children) {
698
- nested += renderSchemaFieldNode(child, depth + 1);
1181
+ nested += renderSchemaFieldNode(child, depth + 1, fieldPath);
699
1182
  }
700
1183
 
701
1184
  return (
702
- '<details class="border-b border-light-border/50 dark:border-dark-border/50" open>' +
703
- '<summary class="list-none cursor-pointer py-2 flex justify-between items-center" style="padding-left:' +
1185
+ '<details open>' +
1186
+ '<summary class="list-none cursor-pointer py-2 border-b border-light-border/50 dark:border-dark-border/50" style="padding-left:' +
704
1187
  padding +
705
- 'px"><div class="flex items-center gap-2"><span class="text-xs opacity-70">▾</span><code class="text-sm font-mono">' +
1188
+ 'px"><div class="param-row-head"><div class="param-row-main"><span class="text-xs opacity-70 shrink-0">▾</span>' +
1189
+ '<button type="button" class="param-tooltip-trigger param-name-trigger" data-param-tooltip-label="Field" data-param-tooltip-value="' +
1190
+ tooltipName +
1191
+ '" data-param-tooltip-related="' +
1192
+ tooltipPath +
1193
+ '" data-param-tooltip-description="' +
1194
+ tooltipDescription +
1195
+ '" aria-expanded="false"><code class="text-sm font-mono param-name-text">' +
706
1196
  name +
707
- '</code><span class="text-xs text-brand">' +
1197
+ '</code></button><span class="text-xs text-brand shrink-0">' +
708
1198
  requiredLabel +
709
- '</span></div><span class="text-xs font-mono opacity-60">' +
1199
+ '</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="' +
1200
+ tooltipType +
1201
+ '" data-param-tooltip-description="' +
1202
+ tooltipDescription +
1203
+ '" aria-expanded="false">' +
710
1204
  type +
711
- "</span></summary>" +
712
- '<div class="pb-1">' +
1205
+ "</button></div>" + extra + "</summary>" +
1206
+ "<div>" +
713
1207
  nested +
714
1208
  "</div></details>"
715
1209
  );
@@ -722,7 +1216,7 @@ export function renderOpenAPIDocsHtml(
722
1216
 
723
1217
  let rows = "";
724
1218
  for (const child of rootChildren) {
725
- rows += renderSchemaFieldNode(child, 0);
1219
+ rows += renderSchemaFieldNode(child, 0, "");
726
1220
  }
727
1221
 
728
1222
  return (
@@ -749,27 +1243,40 @@ export function renderOpenAPIDocsHtml(
749
1243
  const responseDef = responses[statusCode];
750
1244
  if (!responseDef || typeof responseDef !== "object") continue;
751
1245
 
1246
+ const responseDesc = (typeof responseDef.description === "string" && responseDef.description.trim())
1247
+ ? responseDef.description.trim()
1248
+ : "";
1249
+
752
1250
  const jsonSchema =
753
1251
  responseDef.content &&
754
1252
  responseDef.content["application/json"] &&
755
1253
  responseDef.content["application/json"].schema;
756
1254
 
757
- if (!jsonSchema || typeof jsonSchema !== "object") continue;
758
-
759
- const rootChildren = buildSchemaChildren(jsonSchema);
760
- if (!rootChildren.length) continue;
761
-
762
1255
  let rows = "";
763
- for (const child of rootChildren) {
764
- rows += renderSchemaFieldNode(child, 0);
1256
+ if (jsonSchema && typeof jsonSchema === "object") {
1257
+ const rootChildren = buildSchemaChildren(jsonSchema);
1258
+ for (const child of rootChildren) {
1259
+ rows += renderSchemaFieldNode(child, 0, "");
1260
+ }
765
1261
  }
766
1262
 
1263
+ if (!responseDesc && !rows) continue;
1264
+
1265
+ const descHtml = responseDesc
1266
+ ? ' <span class="normal-case font-sans opacity-70 ml-1">— ' + escapeHtml(responseDesc) + '</span>'
1267
+ : "";
1268
+ const contentHtml = rows || '<p class="text-xs opacity-60 mt-1">No schema fields</p>';
1269
+
767
1270
  sections +=
768
- '<div class="mb-4"><h4 class="text-xs font-mono uppercase tracking-wider opacity-70 mb-2">Status ' +
1271
+ '<details class="mb-4">' +
1272
+ '<summary class="list-none cursor-pointer">' +
1273
+ '<h4 class="text-xs font-mono uppercase tracking-wider opacity-70 mb-2">Status ' +
769
1274
  escapeHtml(statusCode) +
1275
+ descHtml +
770
1276
  "</h4>" +
771
- rows +
772
- "</div>";
1277
+ "</summary>" +
1278
+ contentHtml +
1279
+ "</details>";
773
1280
  }
774
1281
 
775
1282
  if (!sections) return "";
@@ -861,6 +1368,7 @@ export function renderOpenAPIDocsHtml(
861
1368
  "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";
862
1369
  keyInput.addEventListener("input", () => {
863
1370
  entry.key = keyInput.value;
1371
+ saveHeaders();
864
1372
  updateRequestPreview();
865
1373
  });
866
1374
 
@@ -875,6 +1383,7 @@ export function renderOpenAPIDocsHtml(
875
1383
  "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";
876
1384
  valueInput.addEventListener("input", () => {
877
1385
  entry.value = valueInput.value;
1386
+ saveHeaders();
878
1387
  updateRequestPreview();
879
1388
  });
880
1389
 
@@ -888,6 +1397,7 @@ export function renderOpenAPIDocsHtml(
888
1397
  '<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>';
889
1398
  removeButton.addEventListener("click", () => {
890
1399
  requestHeaders.splice(index, 1);
1400
+ saveHeaders();
891
1401
  renderHeaderInputs();
892
1402
  updateRequestPreview();
893
1403
  });
@@ -904,15 +1414,32 @@ export function renderOpenAPIDocsHtml(
904
1414
  return Object.keys(headers).some((key) => key.toLowerCase() === target);
905
1415
  }
906
1416
 
907
- function getRequestHeadersObject() {
908
- const headers = {};
1417
+ function buildCookieHeaderValue(cookieValues) {
1418
+ const entries = Object.entries(cookieValues);
1419
+ if (!entries.length) return "";
1420
+ return entries
1421
+ .map(([name, value]) => String(name) + "=" + encodeURIComponent(String(value)))
1422
+ .join("; ");
1423
+ }
1424
+
1425
+ function getRequestHeadersObject(op) {
1426
+ const auth = getAuthHeaders(op);
1427
+ const authCookies = getAuthCookieParams(op);
1428
+ if (Object.keys(authCookies).length > 0) {
1429
+ const cookieHeader = buildCookieHeaderValue(authCookies);
1430
+ if (cookieHeader) {
1431
+ auth["Cookie"] = cookieHeader;
1432
+ }
1433
+ }
1434
+ const manual = {};
909
1435
  for (const entry of requestHeaders) {
910
1436
  const key = String(entry.key || "").trim();
911
1437
  const value = String(entry.value || "").trim();
912
1438
  if (!key || !value) continue;
913
- headers[key] = value;
1439
+ manual[key] = value;
914
1440
  }
915
- return headers;
1441
+ // Auth provides defaults; manual headers win on conflict
1442
+ return Object.assign({}, auth, manual);
916
1443
  }
917
1444
 
918
1445
  function buildCurl(op, headers, body, requestPath) {
@@ -949,6 +1476,33 @@ export function renderOpenAPIDocsHtml(
949
1476
  .replace(/>/g, "&gt;");
950
1477
  }
951
1478
 
1479
+ function escapeHtmlAttribute(value) {
1480
+ return String(value)
1481
+ .replace(/&/g, "&amp;")
1482
+ .replace(/</g, "&lt;")
1483
+ .replace(/>/g, "&gt;")
1484
+ .replace(/"/g, "&quot;")
1485
+ .replace(/'/g, "&#39;");
1486
+ }
1487
+
1488
+ function renderMarkdown(text) {
1489
+ if (!text || typeof text !== "string") return "";
1490
+ var s = escapeHtml(text);
1491
+ // inline code — process first to protect content inside backticks
1492
+ 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>');
1493
+ // bold **text**
1494
+ s = s.replace(/\\*\\*([^*\\n]+)\\*\\*/g, '<strong>$1</strong>');
1495
+ // italic *text*
1496
+ s = s.replace(/\\*([^*\\n]+)\\*/g, '<em>$1</em>');
1497
+ // links [text](url)
1498
+ s = s.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, function(m, txt, url) {
1499
+ var lc = url.toLowerCase().replace(/\\s/g, "");
1500
+ if (lc.indexOf("javascript:") === 0 || lc.indexOf("data:") === 0 || lc.indexOf("vbscript:") === 0) return txt;
1501
+ return '<a href="' + url.replace(/"/g, '&quot;') + '" target="_blank" rel="noopener noreferrer" class="text-brand hover:underline">' + txt + '</a>';
1502
+ });
1503
+ return s;
1504
+ }
1505
+
952
1506
  function toPrettyJson(value) {
953
1507
  const trimmed = (value || "").trim();
954
1508
  if (!trimmed) return null;
@@ -1082,10 +1636,10 @@ export function renderOpenAPIDocsHtml(
1082
1636
 
1083
1637
  const { path, query } = getOperationParameterGroups(selected);
1084
1638
  const values = getParameterValues(selected);
1085
- const requestPath = buildRequestPath(selected, path, query, values);
1639
+ const requestPath = buildRequestPath(selected, path, query, values, getAuthQueryParams(selected));
1086
1640
  const bodyInput = document.getElementById("body-input");
1087
1641
  const body = bodyInput ? bodyInput.value.trim() : "";
1088
- const headers = getRequestHeadersObject();
1642
+ const headers = getRequestHeadersObject(selected);
1089
1643
  if (body && !hasHeaderName(headers, "Content-Type")) {
1090
1644
  headers["Content-Type"] = "application/json";
1091
1645
  }
@@ -1136,8 +1690,13 @@ export function renderOpenAPIDocsHtml(
1136
1690
  }
1137
1691
  setResponseContent("", "", false);
1138
1692
 
1693
+ const deprecatedBanner = document.getElementById("deprecated-banner");
1694
+ if (deprecatedBanner) {
1695
+ deprecatedBanner.classList.toggle("hidden", !op.deprecated);
1696
+ }
1697
+
1139
1698
  document.getElementById("tag-title").textContent = selected.tag;
1140
- document.getElementById("tag-description").textContent = op.description || "Interactive API documentation.";
1699
+ document.getElementById("tag-description").innerHTML = op.description ? renderMarkdown(op.description) : "Interactive API documentation.";
1141
1700
  const methodNode = document.getElementById("endpoint-method");
1142
1701
  methodNode.textContent = selected.method;
1143
1702
  methodNode.className = "px-2.5 py-0.5 rounded-full text-xs font-mono font-medium " + (methodBadge[selected.method] || methodBadgeDefault);
@@ -1154,7 +1713,13 @@ export function renderOpenAPIDocsHtml(
1154
1713
 
1155
1714
  html += renderRequestBodySchemaSection(reqSchema);
1156
1715
  html += renderResponseSchemasSection(op.responses);
1157
- document.getElementById("params-column").innerHTML = html || '<div class="text-sm opacity-70">No parameters</div>';
1716
+ const paramsColumn = document.getElementById("params-column");
1717
+ if (paramsColumn) {
1718
+ hideParamTooltip();
1719
+ paramsColumn.innerHTML = html || '<div class="text-sm opacity-70">No parameters</div>';
1720
+ registerParamTooltipTargets(paramsColumn);
1721
+ }
1722
+ renderAuthPanel();
1158
1723
  renderTryItParameterInputs(path, query);
1159
1724
  renderHeaderInputs();
1160
1725
  updateRequestPreview();
@@ -1165,6 +1730,24 @@ export function renderOpenAPIDocsHtml(
1165
1730
  document.getElementById("copy-curl").addEventListener("click", async () => {
1166
1731
  try { await navigator.clipboard.writeText(document.getElementById("curl-code").textContent || ""); } catch {}
1167
1732
  });
1733
+ if (paramTooltipRoot) {
1734
+ paramTooltipRoot.addEventListener("mouseenter", () => {
1735
+ if (paramTooltipHideTimer) {
1736
+ window.clearTimeout(paramTooltipHideTimer);
1737
+ paramTooltipHideTimer = null;
1738
+ }
1739
+ });
1740
+ paramTooltipRoot.addEventListener("mouseleave", () => {
1741
+ scheduleParamTooltipHide();
1742
+ });
1743
+ }
1744
+ document.addEventListener("pointerdown", (event) => {
1745
+ if (!paramTooltipRoot) return;
1746
+ const target = event.target;
1747
+ if (target && paramTooltipRoot.contains(target)) return;
1748
+ if (target && target.closest && target.closest("[data-param-tooltip-value]")) return;
1749
+ hideParamTooltip();
1750
+ });
1168
1751
  document.getElementById("sidebar-search").addEventListener("input", (event) => {
1169
1752
  sidebarSearchQuery = event.currentTarget.value || "";
1170
1753
  renderSidebar();
@@ -1190,7 +1773,7 @@ export function renderOpenAPIDocsHtml(
1190
1773
  return;
1191
1774
  }
1192
1775
 
1193
- const requestPath = buildRequestPath(selected, path, query, values);
1776
+ const requestPath = buildRequestPath(selected, path, query, values, getAuthQueryParams(selected));
1194
1777
  formatBodyJsonInput();
1195
1778
  updateBodyJsonPresentation();
1196
1779
  const op = selected.operation || {};
@@ -1199,7 +1782,7 @@ export function renderOpenAPIDocsHtml(
1199
1782
  const bodyInput = document.getElementById("body-input");
1200
1783
  const body =
1201
1784
  supportsBody && bodyInput ? bodyInput.value.trim() : "";
1202
- const headers = getRequestHeadersObject();
1785
+ const headers = getRequestHeadersObject(selected);
1203
1786
  if (body && !hasHeaderName(headers, "Content-Type")) {
1204
1787
  headers["Content-Type"] = "application/json";
1205
1788
  }
@@ -1207,7 +1790,13 @@ export function renderOpenAPIDocsHtml(
1207
1790
  setSubmitLoading(true);
1208
1791
  try {
1209
1792
  const requestStart = performance.now();
1210
- const response = await fetch(requestPath, { method: selected.method, headers, body: body || undefined });
1793
+ applyAuthCookies(selected);
1794
+ const response = await fetch(requestPath, {
1795
+ method: selected.method,
1796
+ headers,
1797
+ body: body || undefined,
1798
+ credentials: "same-origin",
1799
+ });
1211
1800
  const text = await response.text();
1212
1801
  const responseTimeMs = Math.round(performance.now() - requestStart);
1213
1802
  const contentType = response.headers.get("content-type") || "unknown";
@@ -1283,6 +1872,7 @@ export function renderOpenAPIDocsHtml(
1283
1872
 
1284
1873
  document.getElementById("add-header-btn").addEventListener("click", () => {
1285
1874
  requestHeaders.push({ key: "", value: "" });
1875
+ saveHeaders();
1286
1876
  renderHeaderInputs();
1287
1877
  updateRequestPreview();
1288
1878
  });
@@ -1396,12 +1986,21 @@ export function renderOpenAPIDocsHtml(
1396
1986
  setMobileSidebarOpen(false);
1397
1987
  });
1398
1988
  window.addEventListener("resize", () => {
1989
+ if (activeParamTooltipTrigger) {
1990
+ positionParamTooltip(activeParamTooltipTrigger);
1991
+ }
1399
1992
  if (window.innerWidth >= 768 && isMobileSidebarOpen) {
1400
1993
  setMobileSidebarOpen(false);
1401
1994
  }
1402
1995
  });
1996
+ window.addEventListener("scroll", () => {
1997
+ if (activeParamTooltipTrigger) {
1998
+ positionParamTooltip(activeParamTooltipTrigger);
1999
+ }
2000
+ }, true);
1403
2001
  document.addEventListener("keydown", (event) => {
1404
2002
  if (event.key === "Escape") {
2003
+ hideParamTooltip();
1405
2004
  if (isMobileSidebarOpen) {
1406
2005
  setMobileSidebarOpen(false);
1407
2006
  }
@@ -1426,7 +2025,444 @@ export function renderOpenAPIDocsHtml(
1426
2025
  }
1427
2026
  });
1428
2027
 
2028
+ function getOperationSecurityRequirements(op) {
2029
+ const operationSecurity = op && op.operation && Array.isArray(op.operation.security)
2030
+ ? op.operation.security
2031
+ : null;
2032
+ if (operationSecurity) {
2033
+ return operationSecurity;
2034
+ }
2035
+ return Array.isArray(spec.security) ? spec.security : [];
2036
+ }
2037
+
2038
+ function getAuthSelectionKeyForOperation(op) {
2039
+ if (!op) return "";
2040
+ return getOperationKey(op);
2041
+ }
2042
+
2043
+ function getAuthSchemeOptionsForOperation(op) {
2044
+ const requirements = getOperationSecurityRequirements(op).filter((requirement) =>
2045
+ requirement && typeof requirement === "object" && !Array.isArray(requirement)
2046
+ );
2047
+ if (!requirements.length) return [];
2048
+
2049
+ const seen = new Set();
2050
+ const options = [];
2051
+ for (const requirement of requirements) {
2052
+ for (const schemeName of Object.keys(requirement)) {
2053
+ if (!Object.prototype.hasOwnProperty.call(authSchemes, schemeName)) continue;
2054
+ if (seen.has(schemeName)) continue;
2055
+ seen.add(schemeName);
2056
+ options.push(schemeName);
2057
+ }
2058
+ }
2059
+ return options;
2060
+ }
2061
+
2062
+ function getSelectedAuthSchemeForOperation(op) {
2063
+ const selectionKey = getAuthSelectionKeyForOperation(op);
2064
+ if (!selectionKey) return null;
2065
+
2066
+ const selectedScheme = authSelectionState[selectionKey];
2067
+ if (!selectedScheme || typeof selectedScheme !== "string") return null;
2068
+
2069
+ const options = Object.keys(authSchemes);
2070
+ if (!options.includes(selectedScheme)) {
2071
+ delete authSelectionState[selectionKey];
2072
+ saveAuthSelectionState();
2073
+ return null;
2074
+ }
2075
+
2076
+ return selectedScheme;
2077
+ }
2078
+
2079
+ function setSelectedAuthSchemeForOperation(op, schemeName) {
2080
+ const selectionKey = getAuthSelectionKeyForOperation(op);
2081
+ if (!selectionKey) return;
2082
+
2083
+ if (!schemeName) {
2084
+ delete authSelectionState[selectionKey];
2085
+ } else {
2086
+ authSelectionState[selectionKey] = schemeName;
2087
+ }
2088
+ saveAuthSelectionState();
2089
+ }
2090
+
2091
+ function hasAuthStateForScheme(schemeName) {
2092
+ const scheme = authSchemes[schemeName];
2093
+ if (!scheme) return false;
2094
+
2095
+ const state = authState[schemeName] || {};
2096
+ const type = (scheme.type || "").toLowerCase();
2097
+ const httpScheme = (scheme.scheme || "").toLowerCase();
2098
+
2099
+ if (type === "http" && httpScheme === "basic") {
2100
+ return Boolean(state.username && state.password);
2101
+ }
2102
+ if (type === "http") {
2103
+ return Boolean(state.token);
2104
+ }
2105
+ if (type === "apikey") {
2106
+ return Boolean(state.value);
2107
+ }
2108
+ if (type === "oauth2" || type === "openidconnect") {
2109
+ return Boolean(state.token);
2110
+ }
2111
+
2112
+ return false;
2113
+ }
2114
+
2115
+ function chooseOperationSecurityRequirement(op) {
2116
+ const requirements = getOperationSecurityRequirements(op).filter((requirement) =>
2117
+ requirement && typeof requirement === "object" && !Array.isArray(requirement)
2118
+ );
2119
+ if (!requirements.length) return null;
2120
+
2121
+ const selectedScheme = getSelectedAuthSchemeForOperation(op);
2122
+ if (selectedScheme) {
2123
+ const selectedRequirement = requirements.find((requirement) =>
2124
+ Object.prototype.hasOwnProperty.call(requirement, selectedScheme)
2125
+ );
2126
+ if (selectedRequirement) return selectedRequirement;
2127
+ }
2128
+
2129
+ let bestRequirement = null;
2130
+ let bestScore = -1;
2131
+
2132
+ for (const requirement of requirements) {
2133
+ const schemeNames = Object.keys(requirement).filter((schemeName) =>
2134
+ Object.prototype.hasOwnProperty.call(authSchemes, schemeName)
2135
+ );
2136
+ if (!schemeNames.length) continue;
2137
+
2138
+ const providedCount = schemeNames.filter((schemeName) => hasAuthStateForScheme(schemeName)).length;
2139
+ const isComplete = providedCount === schemeNames.length;
2140
+ const score = isComplete ? 1000 + providedCount : providedCount;
2141
+
2142
+ if (score > bestScore) {
2143
+ bestScore = score;
2144
+ bestRequirement = requirement;
2145
+ }
2146
+ }
2147
+
2148
+ return bestRequirement || requirements[0];
2149
+ }
2150
+
2151
+ function getAuthSchemeNamesForOperation(op) {
2152
+ const schemeNames = Object.keys(authSchemes);
2153
+ if (!schemeNames.length) return [];
2154
+
2155
+ const selectedScheme = getSelectedAuthSchemeForOperation(op);
2156
+ if (selectedScheme) {
2157
+ const requirement = chooseOperationSecurityRequirement(op);
2158
+ if (requirement && Object.prototype.hasOwnProperty.call(requirement, selectedScheme)) {
2159
+ return Object.keys(requirement).filter((schemeName) =>
2160
+ Object.prototype.hasOwnProperty.call(authSchemes, schemeName)
2161
+ );
2162
+ }
2163
+ return [selectedScheme];
2164
+ }
2165
+
2166
+ const requirement = chooseOperationSecurityRequirement(op);
2167
+ if (!requirement) return [];
2168
+
2169
+ return Object.keys(requirement).filter((schemeName) =>
2170
+ Object.prototype.hasOwnProperty.call(authSchemes, schemeName)
2171
+ );
2172
+ }
2173
+
2174
+ function getAuthHeaders(op) {
2175
+ const headers = {};
2176
+ const schemeNames = getAuthSchemeNamesForOperation(op);
2177
+ const allSchemeNames = Object.keys(authSchemes);
2178
+
2179
+ if (!allSchemeNames.length) {
2180
+ const state = authState["__default__"] || {};
2181
+ if (state.token) headers["Authorization"] = "Bearer " + state.token;
2182
+ return headers;
2183
+ }
2184
+ if (!schemeNames.length) return headers;
2185
+
2186
+ for (const schemeName of schemeNames) {
2187
+ const scheme = authSchemes[schemeName];
2188
+ const state = authState[schemeName] || {};
2189
+ const type = (scheme.type || "").toLowerCase();
2190
+ const httpScheme = (scheme.scheme || "").toLowerCase();
2191
+
2192
+ if (type === "http" && httpScheme === "basic") {
2193
+ if (state.username && state.password) {
2194
+ try {
2195
+ headers["Authorization"] = "Basic " + btoa(state.username + ":" + state.password);
2196
+ } catch {}
2197
+ }
2198
+ } else if (type === "http") {
2199
+ if (state.token) headers["Authorization"] = "Bearer " + state.token;
2200
+ } else if (type === "apikey" && (scheme.in || "").toLowerCase() === "header") {
2201
+ if (state.value && scheme.name) headers[scheme.name] = state.value;
2202
+ } else if (type === "oauth2" || type === "openidconnect") {
2203
+ if (state.token) headers["Authorization"] = "Bearer " + state.token;
2204
+ }
2205
+ }
2206
+ return headers;
2207
+ }
2208
+
2209
+ function getAuthQueryParams(op) {
2210
+ const params = {};
2211
+ for (const schemeName of getAuthSchemeNamesForOperation(op)) {
2212
+ const scheme = authSchemes[schemeName];
2213
+ if ((scheme.type || "").toLowerCase() === "apikey" && (scheme.in || "").toLowerCase() === "query") {
2214
+ const state = authState[schemeName] || {};
2215
+ if (state.value && scheme.name) params[scheme.name] = state.value;
2216
+ }
2217
+ }
2218
+ return params;
2219
+ }
2220
+
2221
+ function getAuthCookieParams(op) {
2222
+ const cookies = {};
2223
+ for (const schemeName of getAuthSchemeNamesForOperation(op)) {
2224
+ const scheme = authSchemes[schemeName];
2225
+ if ((scheme.type || "").toLowerCase() !== "apikey") continue;
2226
+ if ((scheme.in || "").toLowerCase() !== "cookie") continue;
2227
+ const state = authState[schemeName] || {};
2228
+ if (state.value && scheme.name) {
2229
+ cookies[scheme.name] = state.value;
2230
+ }
2231
+ }
2232
+ return cookies;
2233
+ }
2234
+
2235
+ function applyAuthCookies(op) {
2236
+ const cookies = getAuthCookieParams(op);
2237
+ for (const [name, value] of Object.entries(cookies)) {
2238
+ try {
2239
+ document.cookie = encodeURIComponent(String(name)) + "=" + encodeURIComponent(String(value)) + "; path=/";
2240
+ } catch {}
2241
+ }
2242
+ }
2243
+
2244
+ function renderAuthPanel() {
2245
+ const fields = document.getElementById("auth-fields");
2246
+ if (!fields) return;
2247
+ fields.innerHTML = "";
2248
+
2249
+ const schemeNames = Object.keys(authSchemes);
2250
+ const op = selected;
2251
+ const operationSchemeOptions = op ? getAuthSchemeOptionsForOperation(op) : [];
2252
+ const availableSchemeOptions = Object.keys(authSchemes);
2253
+ const selectedScheme = op ? getSelectedAuthSchemeForOperation(op) : null;
2254
+
2255
+ function getAuthSchemeDisplayLabel(schemeName) {
2256
+ const scheme = authSchemes[schemeName] || {};
2257
+ const type = (scheme.type || "").toLowerCase();
2258
+ const httpScheme = (scheme.scheme || "").toLowerCase();
2259
+ const location = (scheme.in || "").toLowerCase();
2260
+
2261
+ if (type === "http" && httpScheme === "basic") return "HTTP Basic";
2262
+ if (type === "http" && httpScheme === "bearer") return "HTTP Bearer";
2263
+ if (type === "http" && httpScheme === "digest") return "HTTP Digest";
2264
+ if (type === "http") return "HTTP " + (httpScheme || "Token");
2265
+ if (type === "apikey") return "API Key" + (location ? " (" + location + ")" : "");
2266
+ if (type === "oauth2") return "OAuth 2.0";
2267
+ if (type === "openidconnect") return "OpenID Connect";
2268
+ if (type === "mutualtls") return "Mutual TLS";
2269
+ return schemeName;
2270
+ }
2271
+
2272
+ function makeInput(placeholder, value, onInput, type) {
2273
+ const inp = document.createElement("input");
2274
+ inp.type = type || "text";
2275
+ inp.value = value || "";
2276
+ inp.placeholder = placeholder;
2277
+ 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";
2278
+ inp.addEventListener("input", onInput);
2279
+ return inp;
2280
+ }
2281
+
2282
+ function makeLabel(text, small) {
2283
+ const el = document.createElement("p");
2284
+ el.className = small
2285
+ ? "text-[10px] font-medium uppercase tracking-wider opacity-55"
2286
+ : "text-[10px] font-semibold uppercase tracking-wider opacity-55";
2287
+ el.textContent = text;
2288
+ return el;
2289
+ }
2290
+
2291
+ function makeField(label, input) {
2292
+ const wrapper = document.createElement("div");
2293
+ wrapper.className = "space-y-1";
2294
+ wrapper.appendChild(makeLabel(label, true));
2295
+ wrapper.appendChild(input);
2296
+ return wrapper;
2297
+ }
2298
+
2299
+ function makeSchemeCard(title, subtitle) {
2300
+ const card = document.createElement("div");
2301
+ 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";
2302
+
2303
+ const titleRow = document.createElement("div");
2304
+ titleRow.className = "flex items-center justify-between gap-2";
2305
+
2306
+ const heading = document.createElement("p");
2307
+ heading.className = "text-[11px] font-semibold tracking-wide";
2308
+ heading.textContent = title;
2309
+ titleRow.appendChild(heading);
2310
+
2311
+ if (subtitle) {
2312
+ const note = document.createElement("span");
2313
+ note.className = "text-[10px] opacity-60 font-mono";
2314
+ note.textContent = subtitle;
2315
+ titleRow.appendChild(note);
2316
+ }
2317
+
2318
+ card.appendChild(titleRow);
2319
+ return card;
2320
+ }
2321
+
2322
+ if (!schemeNames.length) {
2323
+ if (!authState["__default__"]) authState["__default__"] = {};
2324
+ const defaultCard = makeSchemeCard("Default Auth", "bearer");
2325
+ defaultCard.appendChild(makeField("Token", makeInput("Enter token…", authState["__default__"].token, function(e) {
2326
+ authState["__default__"].token = e.target.value;
2327
+ saveAuthState();
2328
+ updateRequestPreview();
2329
+ })));
2330
+ fields.appendChild(defaultCard);
2331
+ return;
2332
+ }
2333
+
2334
+ if (op && availableSchemeOptions.length > 0) {
2335
+ const selectorWrap = document.createElement("div");
2336
+ selectorWrap.className = "space-y-1";
2337
+ selectorWrap.appendChild(makeLabel("Auth Type"));
2338
+ const select = document.createElement("select");
2339
+ 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";
2340
+
2341
+ const autoOption = document.createElement("option");
2342
+ autoOption.value = "";
2343
+ autoOption.textContent = "Auto";
2344
+ select.appendChild(autoOption);
2345
+
2346
+ for (const schemeName of availableSchemeOptions) {
2347
+ const option = document.createElement("option");
2348
+ option.value = schemeName;
2349
+ const isOperationScheme = operationSchemeOptions.includes(schemeName);
2350
+ const label = getAuthSchemeDisplayLabel(schemeName);
2351
+ option.textContent = isOperationScheme
2352
+ ? label
2353
+ : (label + " • override");
2354
+ select.appendChild(option);
2355
+ }
2356
+
2357
+ select.value = selectedScheme || "";
2358
+ select.addEventListener("change", function(e) {
2359
+ setSelectedAuthSchemeForOperation(op, e.target.value || "");
2360
+ renderAuthPanel();
2361
+ updateRequestPreview();
2362
+ });
2363
+ selectorWrap.appendChild(select);
2364
+ fields.appendChild(selectorWrap);
2365
+ }
2366
+
2367
+ const schemesToRender = selectedScheme
2368
+ ? [selectedScheme]
2369
+ : (operationSchemeOptions.length ? operationSchemeOptions : schemeNames);
2370
+
2371
+ for (const schemeName of schemesToRender) {
2372
+ const scheme = authSchemes[schemeName];
2373
+ if (!authState[schemeName]) authState[schemeName] = {};
2374
+ const state = authState[schemeName];
2375
+ const type = (scheme.type || "").toLowerCase();
2376
+ const httpScheme = (scheme.scheme || "").toLowerCase();
2377
+ const card = makeSchemeCard(getAuthSchemeDisplayLabel(schemeName), schemeName);
2378
+
2379
+ if (type === "http" && httpScheme === "basic") {
2380
+ card.appendChild(makeField("Username", makeInput("Username", state.username, function(e) {
2381
+ authState[schemeName].username = e.target.value;
2382
+ saveAuthState();
2383
+ updateRequestPreview();
2384
+ })));
2385
+ card.appendChild(makeField("Password", makeInput("Password", state.password, function(e) {
2386
+ authState[schemeName].password = e.target.value;
2387
+ saveAuthState();
2388
+ updateRequestPreview();
2389
+ }, "password")));
2390
+ } else if (type === "apikey") {
2391
+ const paramName = scheme.name || "key";
2392
+ const location = (scheme.in || "header").toLowerCase();
2393
+ card.appendChild(makeField("API Key", makeInput(paramName + " (" + location + ")", state.value, function(e) {
2394
+ authState[schemeName].value = e.target.value;
2395
+ saveAuthState();
2396
+ updateRequestPreview();
2397
+ })));
2398
+ } else if (type === "oauth2") {
2399
+ card.appendChild(makeField("Access Token", makeInput("OAuth2 access token…", state.token, function(e) {
2400
+ authState[schemeName].token = e.target.value;
2401
+ saveAuthState();
2402
+ updateRequestPreview();
2403
+ })));
2404
+ } else if (type === "openidconnect") {
2405
+ card.appendChild(makeField("ID Token / Access Token", makeInput("OpenID Connect token…", state.token, function(e) {
2406
+ authState[schemeName].token = e.target.value;
2407
+ saveAuthState();
2408
+ updateRequestPreview();
2409
+ })));
2410
+ } else if (type === "http" && httpScheme === "digest") {
2411
+ card.appendChild(makeField("Digest Credential", makeInput("Digest token…", state.token, function(e) {
2412
+ authState[schemeName].token = e.target.value;
2413
+ saveAuthState();
2414
+ updateRequestPreview();
2415
+ })));
2416
+ } else if (type === "http" && httpScheme === "bearer") {
2417
+ card.appendChild(makeField("Bearer Token", makeInput("Bearer token…", state.token, function(e) {
2418
+ authState[schemeName].token = e.target.value;
2419
+ saveAuthState();
2420
+ updateRequestPreview();
2421
+ })));
2422
+ } else if (type === "mutualtls") {
2423
+ const hint = document.createElement("p");
2424
+ hint.className = "text-xs opacity-70 leading-relaxed";
2425
+ hint.textContent = "Configured by your client certificate. No token input required.";
2426
+ card.appendChild(hint);
2427
+ } else {
2428
+ card.appendChild(makeField("Token", makeInput("Token…", state.token, function(e) {
2429
+ authState[schemeName].token = e.target.value;
2430
+ saveAuthState();
2431
+ updateRequestPreview();
2432
+ })));
2433
+ }
2434
+ fields.appendChild(card);
2435
+ }
2436
+ }
2437
+
2438
+ let authPanelOpen = true;
2439
+ document.getElementById("auth-toggle").addEventListener("click", function() {
2440
+ authPanelOpen = !authPanelOpen;
2441
+ const fieldsEl = document.getElementById("auth-fields");
2442
+ const chevron = document.getElementById("auth-chevron");
2443
+ if (fieldsEl) fieldsEl.classList.toggle("hidden", !authPanelOpen);
2444
+ if (chevron) chevron.style.transform = authPanelOpen ? "" : "rotate(-90deg)";
2445
+ });
2446
+
2447
+ // Restore selected operation from URL hash, or set hash for the default selection
2448
+ var initMatch = findOpByHash(window.location.hash);
2449
+ if (initMatch) {
2450
+ selected = initMatch;
2451
+ } else if (selected) {
2452
+ history.replaceState(null, "", getOpHash(selected));
2453
+ }
2454
+
2455
+ window.addEventListener("popstate", function() {
2456
+ var match = findOpByHash(window.location.hash);
2457
+ if (match) {
2458
+ selected = match;
2459
+ renderSidebar();
2460
+ renderEndpoint();
2461
+ }
2462
+ });
2463
+
1429
2464
  setMobileSidebarOpen(false);
2465
+ renderAuthPanel();
1430
2466
  renderSidebar();
1431
2467
  renderEndpoint();
1432
2468
  </script>