vector-framework 1.2.0 → 1.2.1
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 +19 -0
- package/dist/auth/protected.d.ts +1 -0
- package/dist/auth/protected.d.ts.map +1 -1
- package/dist/auth/protected.js +3 -0
- package/dist/auth/protected.js.map +1 -1
- package/dist/cache/manager.d.ts +1 -0
- package/dist/cache/manager.d.ts.map +1 -1
- package/dist/cache/manager.js +3 -0
- package/dist/cache/manager.js.map +1 -1
- package/dist/cli/graceful-shutdown.d.ts +15 -0
- package/dist/cli/graceful-shutdown.d.ts.map +1 -0
- package/dist/cli/graceful-shutdown.js +42 -0
- package/dist/cli/graceful-shutdown.js.map +1 -0
- package/dist/cli/index.js +37 -43
- package/dist/cli/index.js.map +1 -1
- package/dist/cli.js +877 -218
- package/dist/core/config-loader.d.ts.map +1 -1
- package/dist/core/config-loader.js +5 -2
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/server.d.ts +3 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +227 -7
- package/dist/core/server.js.map +1 -1
- package/dist/core/vector.d.ts +4 -2
- package/dist/core/vector.d.ts.map +1 -1
- package/dist/core/vector.js +32 -2
- package/dist/core/vector.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +147 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +147 -41
- package/dist/openapi/docs-ui.d.ts +1 -1
- package/dist/openapi/docs-ui.d.ts.map +1 -1
- package/dist/openapi/docs-ui.js +147 -35
- package/dist/openapi/docs-ui.js.map +1 -1
- package/dist/openapi/generator.d.ts.map +1 -1
- package/dist/openapi/generator.js +233 -4
- package/dist/openapi/generator.js.map +1 -1
- package/dist/start-vector.d.ts +3 -0
- package/dist/start-vector.d.ts.map +1 -0
- package/dist/start-vector.js +38 -0
- package/dist/start-vector.js.map +1 -0
- package/dist/types/index.d.ts +25 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/logger.js +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +2 -0
- package/dist/utils/validation.js.map +1 -1
- package/package.json +3 -1
- package/src/auth/protected.ts +4 -0
- package/src/cache/manager.ts +4 -0
- package/src/cli/graceful-shutdown.ts +60 -0
- package/src/cli/index.ts +42 -49
- package/src/core/config-loader.ts +5 -2
- package/src/core/server.ts +289 -7
- package/src/core/vector.ts +38 -4
- package/src/index.ts +4 -3
- package/src/openapi/assets/favicon/android-chrome-192x192.png +0 -0
- package/src/openapi/assets/favicon/android-chrome-512x512.png +0 -0
- package/src/openapi/assets/favicon/apple-touch-icon.png +0 -0
- package/src/openapi/assets/favicon/favicon-16x16.png +0 -0
- package/src/openapi/assets/favicon/favicon-32x32.png +0 -0
- package/src/openapi/assets/favicon/favicon.ico +0 -0
- package/src/openapi/assets/favicon/site.webmanifest +11 -0
- package/src/openapi/assets/logo.svg +12 -0
- package/src/openapi/assets/logo_dark.svg +6 -0
- package/src/openapi/assets/logo_icon.png +0 -0
- package/src/openapi/assets/logo_white.svg +6 -0
- package/src/openapi/docs-ui.ts +153 -35
- package/src/openapi/generator.ts +231 -4
- package/src/start-vector.ts +50 -0
- package/src/types/index.ts +34 -0
- package/src/utils/logger.ts +1 -1
- package/src/utils/validation.ts +2 -0
package/dist/openapi/docs-ui.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
1
|
+
export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath, logoDarkPath, logoWhitePath, appleTouchIconPath, favicon32Path, favicon16Path, webManifestPath) {
|
|
2
2
|
const specJson = JSON.stringify(spec).replace(/<\/script/gi, '<\\/script');
|
|
3
3
|
const openapiPathJson = JSON.stringify(openapiPath);
|
|
4
4
|
const tailwindScriptPathJson = JSON.stringify(tailwindScriptPath);
|
|
5
|
+
const logoDarkPathJson = JSON.stringify(logoDarkPath);
|
|
6
|
+
const logoWhitePathJson = JSON.stringify(logoWhitePath);
|
|
7
|
+
const appleTouchIconPathJson = JSON.stringify(appleTouchIconPath);
|
|
8
|
+
const favicon32PathJson = JSON.stringify(favicon32Path);
|
|
9
|
+
const favicon16PathJson = JSON.stringify(favicon16Path);
|
|
10
|
+
const webManifestPathJson = JSON.stringify(webManifestPath);
|
|
5
11
|
return `<!DOCTYPE html>
|
|
6
12
|
<html lang="en">
|
|
7
13
|
<head>
|
|
8
14
|
<meta charset="UTF-8">
|
|
9
15
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
10
16
|
<title>Vector API Documentation</title>
|
|
17
|
+
<link rel="apple-touch-icon" sizes="180x180" href=${appleTouchIconPathJson}>
|
|
18
|
+
<link rel="icon" type="image/png" sizes="32x32" href=${favicon32PathJson}>
|
|
19
|
+
<link rel="icon" type="image/png" sizes="16x16" href=${favicon16PathJson}>
|
|
20
|
+
<link rel="manifest" href=${webManifestPathJson}>
|
|
11
21
|
<script>
|
|
12
22
|
if (localStorage.getItem('theme') === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
13
23
|
document.documentElement.classList.add('dark');
|
|
@@ -23,7 +33,12 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
23
33
|
theme: {
|
|
24
34
|
extend: {
|
|
25
35
|
colors: {
|
|
26
|
-
brand:
|
|
36
|
+
brand: {
|
|
37
|
+
DEFAULT: '#00A1FF',
|
|
38
|
+
mint: '#00FF8F',
|
|
39
|
+
soft: '#E4F5FF',
|
|
40
|
+
deep: '#007BC5',
|
|
41
|
+
},
|
|
27
42
|
dark: { bg: '#0A0A0A', surface: '#111111', border: '#1F1F1F', text: '#EDEDED' },
|
|
28
43
|
light: { bg: '#FFFFFF', surface: '#F9F9F9', border: '#E5E5E5', text: '#111111' }
|
|
29
44
|
},
|
|
@@ -90,32 +105,44 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
90
105
|
from { opacity: 0; transform: translateX(-6px); }
|
|
91
106
|
to { opacity: 1; transform: translateX(0); }
|
|
92
107
|
}
|
|
108
|
+
@keyframes spin {
|
|
109
|
+
to { transform: rotate(360deg); }
|
|
110
|
+
}
|
|
111
|
+
.button-spinner {
|
|
112
|
+
display: inline-block;
|
|
113
|
+
width: 0.875rem;
|
|
114
|
+
height: 0.875rem;
|
|
115
|
+
border: 2px solid currentColor;
|
|
116
|
+
border-right-color: transparent;
|
|
117
|
+
border-radius: 9999px;
|
|
118
|
+
animation: spin 700ms linear infinite;
|
|
119
|
+
}
|
|
93
120
|
@media (prefers-reduced-motion: reduce) {
|
|
94
121
|
*, *::before, *::after {
|
|
95
122
|
animation: none !important;
|
|
96
123
|
transition: none !important;
|
|
97
124
|
}
|
|
98
125
|
}
|
|
99
|
-
.json-key { color: #
|
|
100
|
-
.json-string { color: #
|
|
101
|
-
.json-number { color: #
|
|
102
|
-
.json-boolean { color: #
|
|
103
|
-
.json-null { color: #
|
|
104
|
-
.dark .json-key { color: #
|
|
105
|
-
.dark .json-string { color: #
|
|
106
|
-
.dark .json-number { color: #
|
|
107
|
-
.dark .json-boolean { color: #
|
|
108
|
-
.dark .json-null { color: #
|
|
126
|
+
.json-key { color: #007bc5; }
|
|
127
|
+
.json-string { color: #334155; }
|
|
128
|
+
.json-number { color: #00a1ff; }
|
|
129
|
+
.json-boolean { color: #475569; }
|
|
130
|
+
.json-null { color: #64748b; }
|
|
131
|
+
.dark .json-key { color: #7dc9ff; }
|
|
132
|
+
.dark .json-string { color: #d1d9e6; }
|
|
133
|
+
.dark .json-number { color: #7dc9ff; }
|
|
134
|
+
.dark .json-boolean { color: #93a4bf; }
|
|
135
|
+
.dark .json-null { color: #7c8ba3; }
|
|
109
136
|
</style>
|
|
110
137
|
</head>
|
|
111
138
|
<body class="bg-light-bg text-light-text dark:bg-dark-bg dark:text-dark-text font-sans antialiased flex h-screen overflow-hidden">
|
|
112
139
|
<div id="mobile-backdrop" class="fixed inset-0 z-30 bg-black/40 opacity-0 pointer-events-none transition-opacity duration-300 md:hidden"></div>
|
|
113
140
|
<aside id="docs-sidebar" class="fixed inset-y-0 left-0 z-40 w-72 md:w-64 border-r border-light-border dark:border-dark-border bg-light-surface dark:bg-dark-surface flex flex-col flex-shrink-0 transition-transform duration-300 ease-out -translate-x-full md:translate-x-0 md:static md:z-auto transition-colors duration-150">
|
|
114
141
|
<div class="h-14 flex items-center px-5 border-b border-light-border dark:border-dark-border">
|
|
115
|
-
<
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
142
|
+
<div class="flex items-center">
|
|
143
|
+
<img src=${logoDarkPathJson} alt="Vector" class="h-6 w-auto block dark:hidden" />
|
|
144
|
+
<img src=${logoWhitePathJson} alt="Vector" class="h-6 w-auto hidden dark:block" />
|
|
145
|
+
</div>
|
|
119
146
|
<button id="sidebar-close" class="ml-auto p-1.5 rounded-full border border-light-border dark:border-dark-border bg-light-bg/90 dark:bg-dark-bg/90 opacity-90 hover:opacity-100 transition md:hidden" aria-label="Close Menu" title="Close Menu">
|
|
120
147
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
121
148
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
@@ -146,8 +173,8 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
146
173
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
|
147
174
|
</svg>
|
|
148
175
|
</button>
|
|
149
|
-
<
|
|
150
|
-
<
|
|
176
|
+
<img src=${logoDarkPathJson} alt="Vector" class="h-5 w-auto block dark:hidden" />
|
|
177
|
+
<img src=${logoWhitePathJson} alt="Vector" class="h-5 w-auto hidden dark:block" />
|
|
151
178
|
</div>
|
|
152
179
|
<div class="flex-1"></div>
|
|
153
180
|
<button id="theme-toggle" class="p-2 rounded-md hover:bg-black/5 dark:hover:bg-white/5 transition-colors" aria-label="Toggle Dark Mode">
|
|
@@ -172,17 +199,22 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
172
199
|
<div class="grid grid-cols-1 lg:grid-cols-12 gap-10">
|
|
173
200
|
<div class="lg:col-span-5 space-y-8" id="params-column"></div>
|
|
174
201
|
<div class="lg:col-span-7">
|
|
175
|
-
<div class="rounded-lg border border-light-border dark:border-
|
|
176
|
-
<div class="flex items-center justify-between px-4 py-2 border-b border-light-border dark:border-
|
|
177
|
-
<span class="text-xs font-mono text-light-text/70 dark:text-
|
|
178
|
-
<button class="text-xs text-light-text/50 hover:text-light-text dark:text-
|
|
202
|
+
<div class="rounded-lg border border-light-border dark:border-dark-border bg-light-bg dark:bg-dark-bg overflow-hidden group">
|
|
203
|
+
<div class="flex items-center justify-between px-4 py-2 border-b border-light-border dark:border-dark-border bg-light-surface dark:bg-dark-surface">
|
|
204
|
+
<span class="text-xs font-mono text-light-text/70 dark:text-dark-text/70">cURL</span>
|
|
205
|
+
<button class="text-xs text-light-text/50 hover:text-light-text dark:text-dark-text/50 dark:hover:text-dark-text transition-colors" id="copy-curl">Copy</button>
|
|
179
206
|
</div>
|
|
180
|
-
<pre class="p-4 text-sm font-mono text-light-text dark:text-
|
|
207
|
+
<pre class="p-4 text-sm font-mono text-light-text dark:text-dark-text overflow-x-auto leading-relaxed"><code id="curl-code"></code></pre>
|
|
181
208
|
</div>
|
|
182
209
|
<div class="mt-4 p-4 rounded-lg border border-light-border dark:border-dark-border bg-light-surface dark:bg-dark-surface">
|
|
183
210
|
<div class="flex items-center justify-between mb-3">
|
|
184
211
|
<h4 class="text-sm font-medium">Try it out</h4>
|
|
185
|
-
<button id="send-btn" class="px-4 py-1.5 bg-
|
|
212
|
+
<button id="send-btn" class="px-4 py-1.5 bg-brand text-white text-sm font-semibold rounded hover:bg-brand-deep transition-colors">
|
|
213
|
+
<span class="inline-flex items-center gap-2">
|
|
214
|
+
<span id="send-btn-spinner" class="button-spinner hidden" aria-hidden="true"></span>
|
|
215
|
+
<span id="send-btn-label">Submit</span>
|
|
216
|
+
</span>
|
|
217
|
+
</button>
|
|
186
218
|
</div>
|
|
187
219
|
<div class="space-y-4">
|
|
188
220
|
<div>
|
|
@@ -216,7 +248,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
216
248
|
</div>
|
|
217
249
|
</div>
|
|
218
250
|
|
|
219
|
-
<div>
|
|
251
|
+
<div id="response-section">
|
|
220
252
|
<div class="flex items-center justify-between mb-2">
|
|
221
253
|
<p class="text-xs font-semibold uppercase tracking-wider opacity-60">Response</p>
|
|
222
254
|
</div>
|
|
@@ -243,7 +275,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
243
275
|
<div class="flex items-center justify-between mb-3">
|
|
244
276
|
<h3 id="expand-modal-title" class="text-sm font-semibold">Expanded View</h3>
|
|
245
277
|
<div class="flex items-center gap-2">
|
|
246
|
-
<button id="expand-apply" class="hidden text-sm px-3 py-1.5 rounded bg-
|
|
278
|
+
<button id="expand-apply" class="hidden text-sm px-3 py-1.5 rounded bg-brand text-white font-semibold hover:bg-brand-deep transition-colors">Apply</button>
|
|
247
279
|
<button id="expand-close" class="p-1.5 rounded-full border border-light-border dark:border-dark-border bg-light-bg/90 dark:bg-dark-bg/90 opacity-90 hover:opacity-100 hover:border-brand/60 transition-colors" aria-label="Close Modal" title="Close Modal">
|
|
248
280
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
249
281
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
@@ -262,12 +294,13 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
262
294
|
<script>
|
|
263
295
|
const spec = ${specJson};
|
|
264
296
|
const openapiPath = ${openapiPathJson};
|
|
297
|
+
const methodBadgeDefault = "bg-black/5 text-light-text/80 dark:bg-white/10 dark:text-dark-text/80";
|
|
265
298
|
const methodBadge = {
|
|
266
|
-
GET: "bg-
|
|
267
|
-
POST: "bg-
|
|
268
|
-
PUT: "bg-
|
|
269
|
-
PATCH: "bg-
|
|
270
|
-
DELETE: "bg-
|
|
299
|
+
GET: "bg-brand-soft text-brand-deep dark:bg-brand/20 dark:text-brand",
|
|
300
|
+
POST: "bg-brand-soft text-brand-deep dark:bg-brand/20 dark:text-brand",
|
|
301
|
+
PUT: "bg-brand-soft text-brand-deep dark:bg-brand/20 dark:text-brand",
|
|
302
|
+
PATCH: "bg-brand-soft text-brand-deep dark:bg-brand/20 dark:text-brand",
|
|
303
|
+
DELETE: "bg-brand-soft text-brand-deep dark:bg-brand/20 dark:text-brand",
|
|
271
304
|
};
|
|
272
305
|
|
|
273
306
|
function getOperations() {
|
|
@@ -521,6 +554,16 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
521
554
|
return;
|
|
522
555
|
}
|
|
523
556
|
for (const [tag, ops] of groups.entries()) {
|
|
557
|
+
ops.sort((a, b) => {
|
|
558
|
+
const byName = a.name.localeCompare(b.name, undefined, { sensitivity: "base" });
|
|
559
|
+
if (byName !== 0) return byName;
|
|
560
|
+
|
|
561
|
+
const byPath = a.path.localeCompare(b.path, undefined, { sensitivity: "base" });
|
|
562
|
+
if (byPath !== 0) return byPath;
|
|
563
|
+
|
|
564
|
+
return a.method.localeCompare(b.method, undefined, { sensitivity: "base" });
|
|
565
|
+
});
|
|
566
|
+
|
|
524
567
|
const block = document.createElement("div");
|
|
525
568
|
block.innerHTML = '<h3 class="px-2 mb-2 font-semibold text-xs uppercase tracking-wider opacity-50"></h3><ul class="space-y-0.5"></ul>';
|
|
526
569
|
block.querySelector("h3").textContent = tag;
|
|
@@ -532,14 +575,14 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
532
575
|
const a = document.createElement("a");
|
|
533
576
|
a.href = "#";
|
|
534
577
|
a.className = op === selected
|
|
535
|
-
? "block px-2 py-1.5 rounded-md bg-
|
|
578
|
+
? "block px-2 py-1.5 rounded-md bg-brand-soft/70 dark:bg-brand/20 text-brand-deep dark:text-brand font-medium transition-colors"
|
|
536
579
|
: "block px-2 py-1.5 rounded-md hover:bg-black/5 dark:hover:bg-white/5 transition-colors";
|
|
537
580
|
|
|
538
581
|
const row = document.createElement("span");
|
|
539
582
|
row.className = "flex items-center gap-2";
|
|
540
583
|
|
|
541
584
|
const method = document.createElement("span");
|
|
542
|
-
method.className = "px-1.5 py-0.5 rounded text-[10px] font-mono font-semibold " + (methodBadge[op.method] ||
|
|
585
|
+
method.className = "px-1.5 py-0.5 rounded text-[10px] font-mono font-semibold " + (methodBadge[op.method] || methodBadgeDefault);
|
|
543
586
|
method.textContent = op.method;
|
|
544
587
|
|
|
545
588
|
const name = document.createElement("span");
|
|
@@ -678,6 +721,55 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
678
721
|
);
|
|
679
722
|
}
|
|
680
723
|
|
|
724
|
+
function renderResponseSchemasSection(responses) {
|
|
725
|
+
if (!responses || typeof responses !== "object") return "";
|
|
726
|
+
|
|
727
|
+
const statusCodes = Object.keys(responses).sort((a, b) => {
|
|
728
|
+
const aNum = Number(a);
|
|
729
|
+
const bNum = Number(b);
|
|
730
|
+
if (Number.isInteger(aNum) && Number.isInteger(bNum)) return aNum - bNum;
|
|
731
|
+
if (Number.isInteger(aNum)) return -1;
|
|
732
|
+
if (Number.isInteger(bNum)) return 1;
|
|
733
|
+
return a.localeCompare(b);
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
let sections = "";
|
|
737
|
+
for (const statusCode of statusCodes) {
|
|
738
|
+
const responseDef = responses[statusCode];
|
|
739
|
+
if (!responseDef || typeof responseDef !== "object") continue;
|
|
740
|
+
|
|
741
|
+
const jsonSchema =
|
|
742
|
+
responseDef.content &&
|
|
743
|
+
responseDef.content["application/json"] &&
|
|
744
|
+
responseDef.content["application/json"].schema;
|
|
745
|
+
|
|
746
|
+
if (!jsonSchema || typeof jsonSchema !== "object") continue;
|
|
747
|
+
|
|
748
|
+
const rootChildren = buildSchemaChildren(jsonSchema);
|
|
749
|
+
if (!rootChildren.length) continue;
|
|
750
|
+
|
|
751
|
+
let rows = "";
|
|
752
|
+
for (const child of rootChildren) {
|
|
753
|
+
rows += renderSchemaFieldNode(child, 0);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
sections +=
|
|
757
|
+
'<div class="mb-4"><h4 class="text-xs font-mono uppercase tracking-wider opacity-70 mb-2">Status ' +
|
|
758
|
+
escapeHtml(statusCode) +
|
|
759
|
+
"</h4>" +
|
|
760
|
+
rows +
|
|
761
|
+
"</div>";
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (!sections) return "";
|
|
765
|
+
|
|
766
|
+
return (
|
|
767
|
+
'<div><h3 class="text-sm font-semibold mb-3 flex items-center border-b border-light-border dark:border-dark-border pb-2">Response Schemas</h3>' +
|
|
768
|
+
sections +
|
|
769
|
+
"</div>"
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
|
|
681
773
|
function renderTryItParameterInputs(pathParams, queryParams) {
|
|
682
774
|
const container = document.getElementById("request-param-inputs");
|
|
683
775
|
if (!container || !selected) return;
|
|
@@ -961,6 +1053,19 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
961
1053
|
}
|
|
962
1054
|
}
|
|
963
1055
|
|
|
1056
|
+
function setSubmitLoading(isLoading) {
|
|
1057
|
+
const sendButton = document.getElementById("send-btn");
|
|
1058
|
+
const spinner = document.getElementById("send-btn-spinner");
|
|
1059
|
+
const label = document.getElementById("send-btn-label");
|
|
1060
|
+
if (!sendButton) return;
|
|
1061
|
+
|
|
1062
|
+
sendButton.disabled = isLoading;
|
|
1063
|
+
sendButton.classList.toggle("opacity-80", isLoading);
|
|
1064
|
+
sendButton.classList.toggle("cursor-wait", isLoading);
|
|
1065
|
+
if (spinner) spinner.classList.toggle("hidden", !isLoading);
|
|
1066
|
+
if (label) label.textContent = isLoading ? "Sending..." : "Submit";
|
|
1067
|
+
}
|
|
1068
|
+
|
|
964
1069
|
function updateRequestPreview() {
|
|
965
1070
|
if (!selected) return;
|
|
966
1071
|
|
|
@@ -1024,7 +1129,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
1024
1129
|
document.getElementById("tag-description").textContent = op.description || "Interactive API documentation.";
|
|
1025
1130
|
const methodNode = document.getElementById("endpoint-method");
|
|
1026
1131
|
methodNode.textContent = selected.method;
|
|
1027
|
-
methodNode.className = "px-2.5 py-0.5 rounded-full text-xs font-mono font-medium " + (methodBadge[selected.method] ||
|
|
1132
|
+
methodNode.className = "px-2.5 py-0.5 rounded-full text-xs font-mono font-medium " + (methodBadge[selected.method] || methodBadgeDefault);
|
|
1028
1133
|
document.getElementById("endpoint-title").textContent = selected.name;
|
|
1029
1134
|
document.getElementById("endpoint-path").textContent = selected.path;
|
|
1030
1135
|
|
|
@@ -1037,6 +1142,7 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
1037
1142
|
html += renderParamSection("Header Parameters", headers);
|
|
1038
1143
|
|
|
1039
1144
|
html += renderRequestBodySchemaSection(reqSchema);
|
|
1145
|
+
html += renderResponseSchemasSection(op.responses);
|
|
1040
1146
|
document.getElementById("params-column").innerHTML = html || '<div class="text-sm opacity-70">No parameters</div>';
|
|
1041
1147
|
renderTryItParameterInputs(path, query);
|
|
1042
1148
|
renderHeaderInputs();
|
|
@@ -1087,14 +1193,18 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
1087
1193
|
headers["Content-Type"] = "application/json";
|
|
1088
1194
|
}
|
|
1089
1195
|
|
|
1196
|
+
setSubmitLoading(true);
|
|
1090
1197
|
try {
|
|
1198
|
+
const requestStart = performance.now();
|
|
1091
1199
|
const response = await fetch(requestPath, { method: selected.method, headers, body: body || undefined });
|
|
1092
1200
|
const text = await response.text();
|
|
1201
|
+
const responseTimeMs = Math.round(performance.now() - requestStart);
|
|
1093
1202
|
const contentType = response.headers.get("content-type") || "unknown";
|
|
1094
1203
|
const formattedResponse = formatResponseText(text);
|
|
1095
1204
|
const headerText =
|
|
1096
1205
|
"Status: " + response.status + " " + response.statusText + "\\n" +
|
|
1097
|
-
"Content-Type: " + contentType + "\\n
|
|
1206
|
+
"Content-Type: " + contentType + "\\n" +
|
|
1207
|
+
"Response Time: " + responseTimeMs + " ms\\n\\n";
|
|
1098
1208
|
setResponseContent(
|
|
1099
1209
|
headerText,
|
|
1100
1210
|
formattedResponse.text,
|
|
@@ -1102,6 +1212,8 @@ export function renderOpenAPIDocsHtml(spec, openapiPath, tailwindScriptPath) {
|
|
|
1102
1212
|
);
|
|
1103
1213
|
} catch (error) {
|
|
1104
1214
|
setResponseContent("", "Request failed: " + String(error), false);
|
|
1215
|
+
} finally {
|
|
1216
|
+
setSubmitLoading(false);
|
|
1105
1217
|
}
|
|
1106
1218
|
});
|
|
1107
1219
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs-ui.js","sourceRoot":"","sources":["../../src/openapi/docs-ui.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,qBAAqB,CACnC,IAA6B,EAC7B,WAAmB,EACnB,kBAA0B;
|
|
1
|
+
{"version":3,"file":"docs-ui.js","sourceRoot":"","sources":["../../src/openapi/docs-ui.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,qBAAqB,CACnC,IAA6B,EAC7B,WAAmB,EACnB,kBAA0B,EAC1B,YAAoB,EACpB,aAAqB,EACrB,kBAA0B,EAC1B,aAAqB,EACrB,aAAqB,EACrB,eAAuB;IAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC3E,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,sBAAsB,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAClE,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACxD,MAAM,sBAAsB,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAClE,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACxD,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACxD,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAE5D,OAAO;;;;;;sDAM6C,sBAAsB;yDACnB,iBAAiB;yDACjB,iBAAiB;8BAC5C,mBAAmB;;;;;;;;gBAQjC,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAmHnB,gBAAgB;mBAChB,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAgCjB,gBAAgB;mBAChB,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAsHjB,QAAQ;0BACD,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAumCjC,CAAC;AACT,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/openapi/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAoD,MAAM,UAAU,CAAC;AAIrG,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/openapi/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAoD,MAAM,UAAU,CAAC;AAIrG,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA2gBD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,yBAAyB,EAAE,EACnC,OAAO,EAAE,wBAAwB,GAChC,uBAAuB,CA8CzB"}
|
|
@@ -100,8 +100,9 @@ function convertInputSchema(routePath, inputSchema, target, warnings) {
|
|
|
100
100
|
return inputSchema['~standard'].jsonSchema.input({ target });
|
|
101
101
|
}
|
|
102
102
|
catch (error) {
|
|
103
|
-
warnings.push(`[OpenAPI] Failed input schema conversion for ${routePath}: ${error instanceof Error ? error.message : String(error)}
|
|
104
|
-
|
|
103
|
+
warnings.push(`[OpenAPI] Failed input schema conversion for ${routePath}: ${error instanceof Error ? error.message : String(error)}. Falling back to a permissive JSON Schema.`);
|
|
104
|
+
const fallback = buildFallbackJSONSchema(inputSchema);
|
|
105
|
+
return isEmptyObjectSchema(fallback) ? null : fallback;
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
function convertOutputSchema(routePath, statusCode, outputSchema, target, warnings) {
|
|
@@ -112,13 +113,241 @@ function convertOutputSchema(routePath, statusCode, outputSchema, target, warnin
|
|
|
112
113
|
return outputSchema['~standard'].jsonSchema.output({ target });
|
|
113
114
|
}
|
|
114
115
|
catch (error) {
|
|
115
|
-
warnings.push(`[OpenAPI] Failed output schema conversion for ${routePath} (${statusCode}): ${error instanceof Error ? error.message : String(error)}
|
|
116
|
-
return
|
|
116
|
+
warnings.push(`[OpenAPI] Failed output schema conversion for ${routePath} (${statusCode}): ${error instanceof Error ? error.message : String(error)}. Falling back to a permissive JSON Schema.`);
|
|
117
|
+
return buildFallbackJSONSchema(outputSchema);
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
function isRecord(value) {
|
|
120
121
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
121
122
|
}
|
|
123
|
+
function isEmptyObjectSchema(value) {
|
|
124
|
+
return isRecord(value) && Object.keys(value).length === 0;
|
|
125
|
+
}
|
|
126
|
+
// Best-effort extraction of internal schema definition metadata from common
|
|
127
|
+
// standards-compatible validators. If unavailable, callers should fall back to {}.
|
|
128
|
+
function getValidatorSchemaDef(schema) {
|
|
129
|
+
if (!schema || typeof schema !== 'object')
|
|
130
|
+
return null;
|
|
131
|
+
const value = schema;
|
|
132
|
+
if (isRecord(value._def))
|
|
133
|
+
return value._def;
|
|
134
|
+
if (isRecord(value._zod) && isRecord(value._zod.def)) {
|
|
135
|
+
return value._zod.def;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
function getSchemaKind(def) {
|
|
140
|
+
if (!def)
|
|
141
|
+
return null;
|
|
142
|
+
const typeName = def.typeName;
|
|
143
|
+
if (typeof typeName === 'string')
|
|
144
|
+
return typeName;
|
|
145
|
+
const type = def.type;
|
|
146
|
+
if (typeof type === 'string')
|
|
147
|
+
return type;
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function pickSchemaChild(def) {
|
|
151
|
+
const candidates = ['innerType', 'schema', 'type', 'out', 'in', 'left', 'right'];
|
|
152
|
+
for (const key of candidates) {
|
|
153
|
+
if (key in def)
|
|
154
|
+
return def[key];
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
function pickSchemaObjectCandidate(def, keys) {
|
|
159
|
+
for (const key of keys) {
|
|
160
|
+
const value = def[key];
|
|
161
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
function isOptionalWrapperKind(kind) {
|
|
168
|
+
if (!kind)
|
|
169
|
+
return false;
|
|
170
|
+
const lower = kind.toLowerCase();
|
|
171
|
+
return lower.includes('optional') || lower.includes('default') || lower.includes('catch');
|
|
172
|
+
}
|
|
173
|
+
function unwrapOptionalForRequired(schema) {
|
|
174
|
+
let current = schema;
|
|
175
|
+
let optional = false;
|
|
176
|
+
let guard = 0;
|
|
177
|
+
while (guard < 8) {
|
|
178
|
+
guard += 1;
|
|
179
|
+
const def = getValidatorSchemaDef(current);
|
|
180
|
+
const kind = getSchemaKind(def);
|
|
181
|
+
if (!def || !isOptionalWrapperKind(kind))
|
|
182
|
+
break;
|
|
183
|
+
optional = true;
|
|
184
|
+
const inner = pickSchemaChild(def);
|
|
185
|
+
if (!inner)
|
|
186
|
+
break;
|
|
187
|
+
current = inner;
|
|
188
|
+
}
|
|
189
|
+
return { schema: current, optional };
|
|
190
|
+
}
|
|
191
|
+
function getObjectShape(def) {
|
|
192
|
+
const rawShape = def.shape;
|
|
193
|
+
if (typeof rawShape === 'function') {
|
|
194
|
+
try {
|
|
195
|
+
const resolved = rawShape();
|
|
196
|
+
return isRecord(resolved) ? resolved : {};
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return isRecord(rawShape) ? rawShape : {};
|
|
203
|
+
}
|
|
204
|
+
function mapPrimitiveKind(kind) {
|
|
205
|
+
const lower = kind.toLowerCase();
|
|
206
|
+
if (lower.includes('string'))
|
|
207
|
+
return { type: 'string' };
|
|
208
|
+
if (lower.includes('number'))
|
|
209
|
+
return { type: 'number' };
|
|
210
|
+
if (lower.includes('boolean'))
|
|
211
|
+
return { type: 'boolean' };
|
|
212
|
+
if (lower.includes('bigint'))
|
|
213
|
+
return { type: 'string' };
|
|
214
|
+
if (lower.includes('null'))
|
|
215
|
+
return { type: 'null' };
|
|
216
|
+
if (lower.includes('any') || lower.includes('unknown') || lower.includes('never'))
|
|
217
|
+
return {};
|
|
218
|
+
if (lower.includes('date'))
|
|
219
|
+
return { type: 'string', format: 'date-time' };
|
|
220
|
+
if (lower.includes('custom'))
|
|
221
|
+
return { type: 'object', additionalProperties: true };
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
// Universal fallback schema builder used when converter functions throw.
|
|
225
|
+
// This keeps docs generation resilient and preserves routes in OpenAPI output.
|
|
226
|
+
function buildIntrospectedFallbackJSONSchema(schema, seen = new WeakSet()) {
|
|
227
|
+
if (!schema || typeof schema !== 'object')
|
|
228
|
+
return {};
|
|
229
|
+
if (seen.has(schema))
|
|
230
|
+
return {};
|
|
231
|
+
seen.add(schema);
|
|
232
|
+
const def = getValidatorSchemaDef(schema);
|
|
233
|
+
const kind = getSchemaKind(def);
|
|
234
|
+
if (!def || !kind)
|
|
235
|
+
return {};
|
|
236
|
+
const primitive = mapPrimitiveKind(kind);
|
|
237
|
+
if (primitive)
|
|
238
|
+
return primitive;
|
|
239
|
+
const lower = kind.toLowerCase();
|
|
240
|
+
if (lower.includes('object')) {
|
|
241
|
+
const shape = getObjectShape(def);
|
|
242
|
+
const properties = {};
|
|
243
|
+
const required = [];
|
|
244
|
+
for (const [key, child] of Object.entries(shape)) {
|
|
245
|
+
const unwrapped = unwrapOptionalForRequired(child);
|
|
246
|
+
properties[key] = buildIntrospectedFallbackJSONSchema(unwrapped.schema, seen);
|
|
247
|
+
if (!unwrapped.optional)
|
|
248
|
+
required.push(key);
|
|
249
|
+
}
|
|
250
|
+
const out = {
|
|
251
|
+
type: 'object',
|
|
252
|
+
properties,
|
|
253
|
+
additionalProperties: true,
|
|
254
|
+
};
|
|
255
|
+
if (required.length > 0) {
|
|
256
|
+
out.required = required;
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
260
|
+
if (lower.includes('array')) {
|
|
261
|
+
const itemSchema = pickSchemaObjectCandidate(def, ['element', 'items', 'innerType', 'type']) ?? {};
|
|
262
|
+
return {
|
|
263
|
+
type: 'array',
|
|
264
|
+
items: buildIntrospectedFallbackJSONSchema(itemSchema, seen),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
if (lower.includes('record')) {
|
|
268
|
+
const valueType = def.valueType ?? def.valueSchema;
|
|
269
|
+
return {
|
|
270
|
+
type: 'object',
|
|
271
|
+
additionalProperties: valueType ? buildIntrospectedFallbackJSONSchema(valueType, seen) : true,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (lower.includes('tuple')) {
|
|
275
|
+
const items = Array.isArray(def.items)
|
|
276
|
+
? def.items
|
|
277
|
+
: [];
|
|
278
|
+
const prefixItems = items.map((item) => buildIntrospectedFallbackJSONSchema(item, seen));
|
|
279
|
+
return {
|
|
280
|
+
type: 'array',
|
|
281
|
+
prefixItems,
|
|
282
|
+
minItems: prefixItems.length,
|
|
283
|
+
maxItems: prefixItems.length,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (lower.includes('union')) {
|
|
287
|
+
const options = def.options ?? def.schemas ?? [];
|
|
288
|
+
if (!Array.isArray(options) || options.length === 0)
|
|
289
|
+
return {};
|
|
290
|
+
return {
|
|
291
|
+
anyOf: options.map((option) => buildIntrospectedFallbackJSONSchema(option, seen)),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (lower.includes('intersection')) {
|
|
295
|
+
const left = def.left;
|
|
296
|
+
const right = def.right;
|
|
297
|
+
if (!left || !right)
|
|
298
|
+
return {};
|
|
299
|
+
return {
|
|
300
|
+
allOf: [buildIntrospectedFallbackJSONSchema(left, seen), buildIntrospectedFallbackJSONSchema(right, seen)],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (lower.includes('enum')) {
|
|
304
|
+
const values = def.values;
|
|
305
|
+
if (Array.isArray(values))
|
|
306
|
+
return { enum: values };
|
|
307
|
+
if (values && typeof values === 'object')
|
|
308
|
+
return { enum: Object.values(values) };
|
|
309
|
+
return {};
|
|
310
|
+
}
|
|
311
|
+
if (lower.includes('literal')) {
|
|
312
|
+
const value = def.value;
|
|
313
|
+
if (value === undefined)
|
|
314
|
+
return {};
|
|
315
|
+
const valueType = value === null ? 'null' : typeof value;
|
|
316
|
+
if (valueType === 'string' || valueType === 'number' || valueType === 'boolean' || valueType === 'null') {
|
|
317
|
+
return { type: valueType, const: value };
|
|
318
|
+
}
|
|
319
|
+
return { const: value };
|
|
320
|
+
}
|
|
321
|
+
if (lower.includes('nullable')) {
|
|
322
|
+
const inner = pickSchemaChild(def);
|
|
323
|
+
if (!inner)
|
|
324
|
+
return {};
|
|
325
|
+
return {
|
|
326
|
+
anyOf: [buildIntrospectedFallbackJSONSchema(inner, seen), { type: 'null' }],
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (lower.includes('lazy')) {
|
|
330
|
+
const getter = def.getter;
|
|
331
|
+
if (typeof getter !== 'function')
|
|
332
|
+
return {};
|
|
333
|
+
try {
|
|
334
|
+
return buildIntrospectedFallbackJSONSchema(getter(), seen);
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
return {};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const child = pickSchemaChild(def);
|
|
341
|
+
if (child)
|
|
342
|
+
return buildIntrospectedFallbackJSONSchema(child, seen);
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
function buildFallbackJSONSchema(schema) {
|
|
346
|
+
const def = getValidatorSchemaDef(schema);
|
|
347
|
+
if (!def)
|
|
348
|
+
return {};
|
|
349
|
+
return buildIntrospectedFallbackJSONSchema(schema);
|
|
350
|
+
}
|
|
122
351
|
function addStructuredInputToOperation(operation, inputJSONSchema) {
|
|
123
352
|
if (!isRecord(inputJSONSchema))
|
|
124
353
|
return;
|