rapidoc 11.0.1 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -31
- package/dist/rapidoc-min.js +1424 -213
- package/dist/rapidoc-static-min.js +13542 -0
- package/package.json +47 -35
- package/src/components/api-request.ts +869 -0
- package/src/components/api-response.ts +509 -0
- package/src/components/json-tree.ts +171 -0
- package/src/components/schema-base-element.ts +315 -0
- package/src/components/schema-table.ts +430 -0
- package/src/components/schema-tree.ts +386 -0
- package/src/components/tag-input.ts +184 -0
- package/src/index.js +6 -0
- package/src/oauth-receiver.ts +29 -0
- package/src/rapidoc-mini.ts +582 -0
- package/src/rapidoc.ts +1808 -0
- package/src/static/index.d.ts +149 -0
- package/src/static/index.js +62 -0
- package/src/static/navigation.js +497 -0
- package/src/static/render.js +490 -0
- package/src/static/serializer.js +216 -0
- package/src/styles/auth-styles.js +318 -0
- package/src/styles/border-styles.js +24 -0
- package/src/styles/css-bundle.js +129 -0
- package/src/styles/custom-styles.js +8 -0
- package/src/styles/dialog-box-styles.js +102 -0
- package/src/styles/endpoint-styles.js +559 -0
- package/src/styles/flex-styles.js +16 -0
- package/src/styles/font-styles.js +432 -0
- package/src/styles/info-styles.js +36 -0
- package/src/styles/input-styles.js +555 -0
- package/src/styles/layout-styles.js +396 -0
- package/src/styles/microlighter-styles.js +99 -0
- package/src/styles/nav-styles.js +609 -0
- package/src/styles/param-list-styles.js +261 -0
- package/src/styles/schema-styles.js +288 -0
- package/src/styles/tab-styles.js +106 -0
- package/src/styles/theme-tokens.js +582 -0
- package/src/templates/advanced-search-dialog-template.js +94 -0
- package/src/templates/api-response-template.js +219 -0
- package/src/templates/callback-template.js +86 -0
- package/src/templates/code-samples-template.js +52 -0
- package/src/templates/components-template.js +86 -0
- package/src/templates/downloads-template.js +85 -0
- package/src/templates/endpoint-template.js +386 -0
- package/src/templates/expanded-endpoint-template.js +266 -0
- package/src/templates/focused-endpoint-template.js +145 -0
- package/src/templates/header-template.js +92 -0
- package/src/templates/logo-template.js +21 -0
- package/src/templates/main-body-template.js +102 -0
- package/src/templates/navbar-template.js +489 -0
- package/src/templates/overview-template.js +104 -0
- package/src/templates/request-body-template.js +651 -0
- package/src/templates/request-params-template.js +474 -0
- package/src/templates/security-scheme-template.js +1263 -0
- package/src/templates/server-template.js +152 -0
- package/src/utils/auto-color-scheme.js +99 -0
- package/src/utils/bind-navbar.js +175 -0
- package/src/utils/color-utils.js +59 -0
- package/src/utils/common-utils.js +191 -0
- package/src/utils/curl-utils.js +123 -0
- package/src/utils/highlighter.js +416 -0
- package/src/utils/markdown-utils.js +110 -0
- package/src/utils/mcp/common.js +467 -0
- package/src/utils/mcp/generators/csharp.js +242 -0
- package/src/utils/mcp/generators/go.js +298 -0
- package/src/utils/mcp/generators/java.js +330 -0
- package/src/utils/mcp/generators/python.js +248 -0
- package/src/utils/mcp/generators/rust.js +281 -0
- package/src/utils/mcp/generators/typescript.js +290 -0
- package/src/utils/mcp-generator.js +143 -0
- package/src/utils/mock-interceptor.js +508 -0
- package/src/utils/oauth-utils.js +87 -0
- package/src/utils/request-utils.js +203 -0
- package/src/utils/response-utils.js +203 -0
- package/src/utils/sanitize.js +57 -0
- package/src/utils/schema-utils.js +1927 -0
- package/src/utils/slugger.js +189 -0
- package/src/utils/spec-parser.js +915 -0
- package/src/utils/url-utils.js +211 -0
- package/src/utils/xml-utils.js +61 -0
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
+
import { guard } from "lit/directives/guard.js";
|
|
4
|
+
import { scheduleHighlight } from "~/utils/highlighter";
|
|
5
|
+
import ParamListStyles from "~/styles/param-list-styles";
|
|
6
|
+
import FlexStyles from "~/styles/flex-styles";
|
|
7
|
+
import InputStyles from "~/styles/input-styles";
|
|
8
|
+
import FontStyles from "~/styles/font-styles";
|
|
9
|
+
import BorderStyles from "~/styles/border-styles";
|
|
10
|
+
import TabStyles from "~/styles/tab-styles";
|
|
11
|
+
import MicrolighterStyles from "~/styles/microlighter-styles";
|
|
12
|
+
import CustomStyles from "~/styles/custom-styles";
|
|
13
|
+
import { constructFetchURL } from "~/utils/url-utils";
|
|
14
|
+
import { generateCurlCommand } from "~/utils/curl-utils";
|
|
15
|
+
import {
|
|
16
|
+
constructFetchHeaders,
|
|
17
|
+
buildUrlEncodedBody,
|
|
18
|
+
buildMultipartFormData,
|
|
19
|
+
} from "~/utils/request-utils";
|
|
20
|
+
import { processFetchResponse } from "../utils/response-utils.js";
|
|
21
|
+
import { inputParametersTemplate } from "~/templates/request-params-template";
|
|
22
|
+
import requestBodyTemplate from "~/templates/request-body-template";
|
|
23
|
+
import apiCallTemplate from "~/templates/api-response-template";
|
|
24
|
+
|
|
25
|
+
@customElement("api-request")
|
|
26
|
+
export default class ApiRequest extends LitElement {
|
|
27
|
+
@property({ type: Object }) config: any;
|
|
28
|
+
@property({ type: String, attribute: "server-url" }) serverUrl = "";
|
|
29
|
+
@property({ type: Array }) servers: any = [];
|
|
30
|
+
@property({ type: String }) method = "";
|
|
31
|
+
@property({ type: String }) path = "";
|
|
32
|
+
@property({ type: Array }) security: any = [];
|
|
33
|
+
@property({ type: Array }) parameters: any = [];
|
|
34
|
+
@property({ type: Object }) request_body: any;
|
|
35
|
+
@property({ type: Array }) api_keys: any = [];
|
|
36
|
+
@property({ type: Object }) parser: any;
|
|
37
|
+
@property({ type: String }) accept = "";
|
|
38
|
+
@property({ type: String }) callback = "";
|
|
39
|
+
@property({ type: String }) webhook = "";
|
|
40
|
+
@property({ type: String, attribute: "fill-request-fields-with-example" })
|
|
41
|
+
fillRequestFieldsWithExample = "";
|
|
42
|
+
@property({ type: String, attribute: "allow-try" }) allowTry = "true";
|
|
43
|
+
@property({ type: String, attribute: "show-curl-before-try" }) showCurlBeforeTry = "false";
|
|
44
|
+
@property({ type: String, attribute: "render-style" }) renderStyle = "view";
|
|
45
|
+
@property({ type: String, attribute: "schema-style" }) schemaStyle = "tree";
|
|
46
|
+
@property({ type: String, attribute: "active-schema-tab" }) activeSchemaTab = "schema";
|
|
47
|
+
@property({
|
|
48
|
+
type: Object,
|
|
49
|
+
converter: {
|
|
50
|
+
fromAttribute: (attr: string) => JSON.parse(attr),
|
|
51
|
+
toAttribute: (prop: any) => JSON.stringify(prop),
|
|
52
|
+
},
|
|
53
|
+
attribute: "active-parameter-schema-tabs",
|
|
54
|
+
})
|
|
55
|
+
activeParameterSchemaTabs: any = {};
|
|
56
|
+
@property({ type: Number, attribute: "schema-expand-level" }) schemaExpandLevel = 99999;
|
|
57
|
+
@property({ type: String, attribute: "schema-description-expanded" }) schemaDescriptionExpanded =
|
|
58
|
+
"false";
|
|
59
|
+
@property({ type: String, attribute: "allow-schema-description-expand-toggle" })
|
|
60
|
+
allowSchemaDescriptionExpandToggle = "false";
|
|
61
|
+
@property({ type: String, attribute: "schema-hide-read-only" }) schemaHideReadOnly = "true";
|
|
62
|
+
@property({ type: String, attribute: "schema-hide-write-only" }) schemaHideWriteOnly = "false";
|
|
63
|
+
@property({ type: String, attribute: "fetch-credentials" }) fetchCredentials = "";
|
|
64
|
+
|
|
65
|
+
// Internal reactive state
|
|
66
|
+
@state() responseMessage: any = "";
|
|
67
|
+
@state() responseText = "";
|
|
68
|
+
@state() responseHeaders: any = "";
|
|
69
|
+
@state() responseStatus = "success";
|
|
70
|
+
@state() responseUrl = "";
|
|
71
|
+
@state() curlSyntax = "";
|
|
72
|
+
@state() responseIsBlob = false;
|
|
73
|
+
@state() responseBlobType = "";
|
|
74
|
+
@state() responseBlobUrl = "";
|
|
75
|
+
@state() respContentDisposition = "";
|
|
76
|
+
@state() activeResponseTab = "response"; // allowed values: response, headers, curl
|
|
77
|
+
@state() selectedRequestBodyType = "";
|
|
78
|
+
@state() selectedRequestBodyExample = "";
|
|
79
|
+
@state() fileInputKeys: any = {};
|
|
80
|
+
@state() loading = false;
|
|
81
|
+
|
|
82
|
+
activeAbortController: any = null;
|
|
83
|
+
|
|
84
|
+
constructor() {
|
|
85
|
+
super();
|
|
86
|
+
this.responseMessage = "";
|
|
87
|
+
this.responseStatus = "success";
|
|
88
|
+
this.responseHeaders = "";
|
|
89
|
+
this.responseText = "";
|
|
90
|
+
this.responseUrl = "";
|
|
91
|
+
this.curlSyntax = "";
|
|
92
|
+
this.responseIsBlob = false;
|
|
93
|
+
this.responseBlobType = "";
|
|
94
|
+
this.responseBlobUrl = "";
|
|
95
|
+
this.respContentDisposition = "";
|
|
96
|
+
this.activeResponseTab = "response";
|
|
97
|
+
this.selectedRequestBodyType = "";
|
|
98
|
+
this.selectedRequestBodyExample = "";
|
|
99
|
+
this.activeParameterSchemaTabs = {};
|
|
100
|
+
this.fileInputKeys = {};
|
|
101
|
+
this.loading = false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static get styles() {
|
|
105
|
+
return [
|
|
106
|
+
ParamListStyles,
|
|
107
|
+
InputStyles,
|
|
108
|
+
FontStyles,
|
|
109
|
+
FlexStyles,
|
|
110
|
+
BorderStyles,
|
|
111
|
+
TabStyles,
|
|
112
|
+
MicrolighterStyles,
|
|
113
|
+
css`
|
|
114
|
+
*,
|
|
115
|
+
*:before,
|
|
116
|
+
*:after {
|
|
117
|
+
box-sizing: border-box;
|
|
118
|
+
}
|
|
119
|
+
:where(button, input[type='checkbox'], [tabindex='0']):focus-visible {
|
|
120
|
+
box-shadow: var(--focus-shadow);
|
|
121
|
+
}
|
|
122
|
+
:where(input[type='text'], input[type='password'], select, textarea):focus-visible {
|
|
123
|
+
border-color: var(--primary);
|
|
124
|
+
}
|
|
125
|
+
tag-input:focus-within {
|
|
126
|
+
outline: 1px solid;
|
|
127
|
+
}
|
|
128
|
+
.top-gap {
|
|
129
|
+
margin-top: 24px;
|
|
130
|
+
}
|
|
131
|
+
/* The first label under the Request heading belongs to it; no section gap */
|
|
132
|
+
.request-panel > div > .top-gap:first-child,
|
|
133
|
+
.request-panel > div > .request-body-container:first-child > .top-gap {
|
|
134
|
+
margin-top: 4px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.textarea {
|
|
138
|
+
min-height: 220px;
|
|
139
|
+
padding: 5px;
|
|
140
|
+
resize: vertical;
|
|
141
|
+
direction: ltr;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.response-message {
|
|
145
|
+
font-weight: bold;
|
|
146
|
+
text-overflow: ellipsis;
|
|
147
|
+
|
|
148
|
+
&.error {
|
|
149
|
+
color: var(--destructive);
|
|
150
|
+
}
|
|
151
|
+
&.success {
|
|
152
|
+
color: var(--blue);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.file-input-container {
|
|
157
|
+
align-items: flex-start;
|
|
158
|
+
width: 100%;
|
|
159
|
+
|
|
160
|
+
/* Each file input spans the full width; its remove button sits inside the box, at the right edge */
|
|
161
|
+
& .input-set {
|
|
162
|
+
position: relative;
|
|
163
|
+
width: 100%;
|
|
164
|
+
display: flex;
|
|
165
|
+
align-items: center;
|
|
166
|
+
|
|
167
|
+
&:first-child .file-input-remove-btn {
|
|
168
|
+
display: none;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
&:not(:first-child) input[type='file'] {
|
|
172
|
+
padding-right: 2.25em;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Inset by the file input's border + padding, so it lines up with Choose File */
|
|
177
|
+
& .file-input-add-btn {
|
|
178
|
+
align-self: flex-start;
|
|
179
|
+
margin: 4px 0 0 0.45em;
|
|
180
|
+
font-weight: 400;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.file-input-remove-btn {
|
|
185
|
+
position: absolute;
|
|
186
|
+
top: 50%;
|
|
187
|
+
right: 0.45em;
|
|
188
|
+
transform: translateY(-50%);
|
|
189
|
+
padding: 0 0.25em;
|
|
190
|
+
font-size: 1.25em;
|
|
191
|
+
line-height: 1;
|
|
192
|
+
color: var(--destructive);
|
|
193
|
+
border: none;
|
|
194
|
+
border-radius: var(--radius);
|
|
195
|
+
background: none;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
|
|
198
|
+
&:hover {
|
|
199
|
+
background: color-mix(in srgb, var(--destructive) 15%, transparent);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
&:focus-visible {
|
|
203
|
+
outline: none;
|
|
204
|
+
box-shadow: var(--focus-shadow);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.v-tab-btn {
|
|
209
|
+
font-size: var(--font-size-small);
|
|
210
|
+
height: 24px;
|
|
211
|
+
border: none;
|
|
212
|
+
background: none;
|
|
213
|
+
opacity: 0.3;
|
|
214
|
+
cursor: pointer;
|
|
215
|
+
padding: 4px 8px;
|
|
216
|
+
|
|
217
|
+
&.active {
|
|
218
|
+
font-weight: bold;
|
|
219
|
+
background: var(--background);
|
|
220
|
+
opacity: 1;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@container (min-width: 768px) {
|
|
225
|
+
.textarea {
|
|
226
|
+
padding: 8px;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/*
|
|
231
|
+
* Try bar: server/auth summary on the left, actions on the right. The summary is a two-row
|
|
232
|
+
* label/value list sized like a parameter label (name over type). The bar is its own container,
|
|
233
|
+
* so it reflows by the width it has, not the viewport.
|
|
234
|
+
*/
|
|
235
|
+
.try-bar {
|
|
236
|
+
container-type: inline-size;
|
|
237
|
+
margin: 16px 0;
|
|
238
|
+
font-size: var(--font-size-small);
|
|
239
|
+
}
|
|
240
|
+
/* Narrow: summary on top, actions below it on the right */
|
|
241
|
+
.try-layout {
|
|
242
|
+
display: flex;
|
|
243
|
+
flex-direction: column;
|
|
244
|
+
gap: 0.75em;
|
|
245
|
+
}
|
|
246
|
+
.try-info {
|
|
247
|
+
display: grid;
|
|
248
|
+
grid-template-columns: max-content minmax(0, 1fr);
|
|
249
|
+
gap: 0.25em 0.75em;
|
|
250
|
+
margin: 0;
|
|
251
|
+
line-height: 1.35;
|
|
252
|
+
|
|
253
|
+
& > dt {
|
|
254
|
+
font-weight: 600;
|
|
255
|
+
color: var(--foreground);
|
|
256
|
+
}
|
|
257
|
+
& > dd {
|
|
258
|
+
margin: 0;
|
|
259
|
+
min-width: 0;
|
|
260
|
+
color: var(--muted-foreground);
|
|
261
|
+
overflow-wrap: anywhere;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
.try-actions {
|
|
265
|
+
display: flex;
|
|
266
|
+
flex-wrap: wrap;
|
|
267
|
+
justify-content: flex-end;
|
|
268
|
+
gap: 0.5em;
|
|
269
|
+
}
|
|
270
|
+
/* Wide: one row; the summary takes the free space and the actions keep their size */
|
|
271
|
+
@container (min-width: 34rem) {
|
|
272
|
+
.try-layout {
|
|
273
|
+
flex-direction: row;
|
|
274
|
+
align-items: center;
|
|
275
|
+
gap: 1.5em;
|
|
276
|
+
}
|
|
277
|
+
.try-info {
|
|
278
|
+
flex: 1;
|
|
279
|
+
min-width: 0;
|
|
280
|
+
}
|
|
281
|
+
.try-actions {
|
|
282
|
+
flex: none;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/* Very narrow: each label above its value */
|
|
286
|
+
@container (max-width: 20rem) {
|
|
287
|
+
.try-info {
|
|
288
|
+
grid-template-columns: minmax(0, 1fr);
|
|
289
|
+
gap: 0;
|
|
290
|
+
|
|
291
|
+
& > dd + dt {
|
|
292
|
+
margin-top: 0.5em;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
`,
|
|
297
|
+
CustomStyles,
|
|
298
|
+
];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Shared schema & display configurations that affect both parameters and request body */
|
|
302
|
+
get commonConfigDeps() {
|
|
303
|
+
return [
|
|
304
|
+
this.schemaStyle,
|
|
305
|
+
this.schemaExpandLevel,
|
|
306
|
+
this.schemaDescriptionExpanded,
|
|
307
|
+
this.allowSchemaDescriptionExpandToggle,
|
|
308
|
+
this.schemaHideReadOnly,
|
|
309
|
+
this.schemaHideWriteOnly,
|
|
310
|
+
this.fillRequestFieldsWithExample,
|
|
311
|
+
this.renderStyle,
|
|
312
|
+
this.allowTry,
|
|
313
|
+
this.callback,
|
|
314
|
+
this.webhook,
|
|
315
|
+
];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Dependencies for query/path/header/cookie parameters */
|
|
319
|
+
get parameterGuardDeps() {
|
|
320
|
+
return [
|
|
321
|
+
this.method,
|
|
322
|
+
this.path,
|
|
323
|
+
this.parameters,
|
|
324
|
+
this.activeParameterSchemaTabs,
|
|
325
|
+
...this.commonConfigDeps,
|
|
326
|
+
];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Dependencies for the request body template */
|
|
330
|
+
get requestBodyGuardDeps() {
|
|
331
|
+
return [
|
|
332
|
+
this.method,
|
|
333
|
+
this.path,
|
|
334
|
+
this.request_body,
|
|
335
|
+
this.selectedRequestBodyType,
|
|
336
|
+
this.selectedRequestBodyExample,
|
|
337
|
+
this.activeSchemaTab,
|
|
338
|
+
this.fileInputKeys,
|
|
339
|
+
...this.commonConfigDeps,
|
|
340
|
+
];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
render() {
|
|
344
|
+
return html`<div
|
|
345
|
+
class="col regular-font request-panel ${
|
|
346
|
+
"read focused".includes(this.renderStyle) || this.callback === "true"
|
|
347
|
+
? "read-mode"
|
|
348
|
+
: "view-mode"
|
|
349
|
+
}"
|
|
350
|
+
>
|
|
351
|
+
${
|
|
352
|
+
this.callback === "true"
|
|
353
|
+
? html`<h6 class="rapidoc-heading label-heading tiny-title" part="section-request-title heading heading-6">Callback request</h6>`
|
|
354
|
+
: html`<h4 class="rapidoc-heading label-heading req-res-title" part="section-request-title heading heading-4">Request</h4>`
|
|
355
|
+
}
|
|
356
|
+
<div>
|
|
357
|
+
${guard(this.parameterGuardDeps, () => inputParametersTemplate.call(this, "path"))}
|
|
358
|
+
${guard(this.parameterGuardDeps, () => inputParametersTemplate.call(this, "query"))}
|
|
359
|
+
${guard(this.requestBodyGuardDeps, () => requestBodyTemplate.call(this))}
|
|
360
|
+
${guard(this.parameterGuardDeps, () => inputParametersTemplate.call(this, "header"))}
|
|
361
|
+
${guard(this.parameterGuardDeps, () => inputParametersTemplate.call(this, "cookie"))}
|
|
362
|
+
${this.allowTry === "false" ? "" : html`${apiCallTemplate.call(this)}`}
|
|
363
|
+
</div>
|
|
364
|
+
</div>`;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async updated(changedProperties) {
|
|
368
|
+
if (this.showCurlBeforeTry === "true") {
|
|
369
|
+
if (
|
|
370
|
+
!changedProperties ||
|
|
371
|
+
!changedProperties.has("curlSyntax") ||
|
|
372
|
+
changedProperties.size > 1
|
|
373
|
+
) {
|
|
374
|
+
this.applyCURLSyntax(this.shadowRoot);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
scheduleHighlight(this.getRootNode()?.host?.shadowRoot || this.shadowRoot);
|
|
378
|
+
|
|
379
|
+
if (this.webhook === "true") {
|
|
380
|
+
this.allowTry = "false";
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// This method is called before navigation change in focused mode
|
|
385
|
+
async beforeNavigationFocusedMode() {}
|
|
386
|
+
|
|
387
|
+
// Called by rapi-doc after navigating to a new endpoint in focused renderStyle.
|
|
388
|
+
// In focused mode, the single <api-request> element instance is reused across operations.
|
|
389
|
+
// We reset selectedRequestBodyType and selectedRequestBodyExample so stale MIME type/example selections
|
|
390
|
+
// from a previous endpoint don't carry over to an endpoint that may not support them.
|
|
391
|
+
// The first valid MIME type will be automatically selected by request-body-template during render.
|
|
392
|
+
async afterNavigationFocusedMode() {
|
|
393
|
+
this.selectedRequestBodyType = "";
|
|
394
|
+
this.selectedRequestBodyExample = "";
|
|
395
|
+
this.clearResponseData();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Request-Body Event Handlers
|
|
399
|
+
onSelectExample(e) {
|
|
400
|
+
this.selectedRequestBodyExample = e.target.value;
|
|
401
|
+
const exampleDropdownEl = e.target;
|
|
402
|
+
window.setTimeout(
|
|
403
|
+
(selectEl) => {
|
|
404
|
+
const readOnlyExampleEl = selectEl
|
|
405
|
+
.closest(".example-panel")
|
|
406
|
+
.querySelector(".request-body-param");
|
|
407
|
+
const userInputExampleTextareaEl = selectEl
|
|
408
|
+
.closest(".example-panel")
|
|
409
|
+
.querySelector(".request-body-param-user-input");
|
|
410
|
+
userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;
|
|
411
|
+
|
|
412
|
+
const requestPanelEl = this.getRequestPanel({ target: selectEl });
|
|
413
|
+
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
414
|
+
},
|
|
415
|
+
0,
|
|
416
|
+
exampleDropdownEl,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
onMimeTypeChange(e) {
|
|
421
|
+
this.selectedRequestBodyType = e.target.value;
|
|
422
|
+
const mimeDropdownEl = e.target;
|
|
423
|
+
this.selectedRequestBodyExample = "";
|
|
424
|
+
window.setTimeout(
|
|
425
|
+
(selectEl) => {
|
|
426
|
+
const readOnlyExampleEl = selectEl
|
|
427
|
+
.closest(".request-body-container")
|
|
428
|
+
.querySelector(".request-body-param");
|
|
429
|
+
if (readOnlyExampleEl) {
|
|
430
|
+
const userInputExampleTextareaEl = selectEl
|
|
431
|
+
.closest(".request-body-container")
|
|
432
|
+
.querySelector(".request-body-param-user-input");
|
|
433
|
+
userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
0,
|
|
437
|
+
mimeDropdownEl,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async onFillRequestData(e) {
|
|
442
|
+
const requestPanelEl = e.target.closest(".request-panel");
|
|
443
|
+
const requestPanelInputEls = [
|
|
444
|
+
...requestPanelEl.querySelectorAll("input, tag-input, textarea:not(.is-hidden)"),
|
|
445
|
+
];
|
|
446
|
+
requestPanelInputEls.forEach((el) => {
|
|
447
|
+
if (el.dataset.example) {
|
|
448
|
+
if (el.tagName.toUpperCase() === "TAG-INPUT") {
|
|
449
|
+
el.value = el.dataset.example.split("~|~");
|
|
450
|
+
} else {
|
|
451
|
+
el.value = el.dataset.example;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async onClearRequestData(e) {
|
|
458
|
+
const requestPanelEl = e.target.closest(".request-panel");
|
|
459
|
+
const requestPanelInputEls = [
|
|
460
|
+
...requestPanelEl.querySelectorAll("input, tag-input, textarea:not(.is-hidden)"),
|
|
461
|
+
];
|
|
462
|
+
requestPanelInputEls.forEach((el) => {
|
|
463
|
+
el.value = "";
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
buildFetchURL(requestPanelEl) {
|
|
468
|
+
const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
|
|
469
|
+
const queryParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='query']")];
|
|
470
|
+
const queryParamObjTypeEls = [
|
|
471
|
+
...requestPanelEl.querySelectorAll("[data-ptype='query-object']"),
|
|
472
|
+
];
|
|
473
|
+
|
|
474
|
+
const pathParams = {};
|
|
475
|
+
pathParamEls.forEach((el) => {
|
|
476
|
+
pathParams[el.dataset.pname] = el.value;
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const queryParams = [];
|
|
480
|
+
queryParamEls.forEach((el) => {
|
|
481
|
+
const isArray = el.dataset.array !== "false";
|
|
482
|
+
const style = el.dataset.paramSerializeStyle || "form";
|
|
483
|
+
const explode =
|
|
484
|
+
el.dataset.paramSerializeExplode !== undefined
|
|
485
|
+
? el.dataset.paramSerializeExplode === "true"
|
|
486
|
+
: style === "form";
|
|
487
|
+
const allowReserved = el.dataset.paramAllowReserved === "true";
|
|
488
|
+
|
|
489
|
+
queryParams.push({
|
|
490
|
+
name: el.dataset.pname,
|
|
491
|
+
value: el.value,
|
|
492
|
+
style,
|
|
493
|
+
explode,
|
|
494
|
+
allowReserved,
|
|
495
|
+
isArray,
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
queryParamObjTypeEls.forEach((el) => {
|
|
500
|
+
const style = el.dataset.paramSerializeStyle || "form";
|
|
501
|
+
const explode =
|
|
502
|
+
el.dataset.paramSerializeExplode !== undefined
|
|
503
|
+
? el.dataset.paramSerializeExplode === "true"
|
|
504
|
+
: style === "form";
|
|
505
|
+
const allowReserved = el.dataset.paramAllowReserved === "true";
|
|
506
|
+
|
|
507
|
+
queryParams.push({
|
|
508
|
+
name: el.dataset.pname,
|
|
509
|
+
value: el.value,
|
|
510
|
+
style,
|
|
511
|
+
explode,
|
|
512
|
+
allowReserved,
|
|
513
|
+
isObject: true,
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
return constructFetchURL({
|
|
518
|
+
serverUrl: this.serverUrl,
|
|
519
|
+
path: this.path,
|
|
520
|
+
pathParams,
|
|
521
|
+
queryParams,
|
|
522
|
+
apiKeys: this.api_keys,
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
buildFetchHeaders(requestPanelEl) {
|
|
527
|
+
const respEl = this.closest(
|
|
528
|
+
".expanded-req-resp-container, .req-resp-container",
|
|
529
|
+
)?.getElementsByTagName("api-response")[0];
|
|
530
|
+
const headerParamEls = [
|
|
531
|
+
...requestPanelEl.querySelectorAll("[data-ptype='header'], [data-ptype='header-object']"),
|
|
532
|
+
];
|
|
533
|
+
const requestBodyContainerEl = requestPanelEl.querySelector(".request-body-container");
|
|
534
|
+
const accept = respEl?.selectedMimeType || this.accept || "";
|
|
535
|
+
const contentType = requestBodyContainerEl?.dataset?.selectedRequestBodyType || "";
|
|
536
|
+
|
|
537
|
+
const headerParams = headerParamEls.map((el) => ({
|
|
538
|
+
name: el.dataset.pname,
|
|
539
|
+
value: el.value,
|
|
540
|
+
explode: el.dataset.paramSerializeExplode === "true",
|
|
541
|
+
isObject: el.dataset.ptype === "header-object",
|
|
542
|
+
}));
|
|
543
|
+
|
|
544
|
+
return constructFetchHeaders({
|
|
545
|
+
accept,
|
|
546
|
+
apiKeys: this.api_keys,
|
|
547
|
+
headerParams,
|
|
548
|
+
contentType,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
buildFetchBodyOptions(requestPanelEl) {
|
|
553
|
+
const requestBodyContainerEl = requestPanelEl.querySelector(".request-body-container");
|
|
554
|
+
const fetchOptions: any = {
|
|
555
|
+
method: this.method.toUpperCase(),
|
|
556
|
+
};
|
|
557
|
+
if (requestBodyContainerEl) {
|
|
558
|
+
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType || "";
|
|
559
|
+
if (requestBodyType.includes("form-urlencoded")) {
|
|
560
|
+
const formUrlDynamicTextAreaEl = requestPanelEl.querySelector(
|
|
561
|
+
"[data-ptype='dynamic-form']",
|
|
562
|
+
);
|
|
563
|
+
if (formUrlDynamicTextAreaEl) {
|
|
564
|
+
fetchOptions.body = buildUrlEncodedBody([], formUrlDynamicTextAreaEl.value);
|
|
565
|
+
} else {
|
|
566
|
+
const formUrlEls = [...requestPanelEl.querySelectorAll("[data-ptype='form-urlencode']")];
|
|
567
|
+
const fields = formUrlEls
|
|
568
|
+
.filter((v: any) => v.type !== "file")
|
|
569
|
+
.map((el: any) => ({
|
|
570
|
+
name: el.dataset.pname,
|
|
571
|
+
value: el.value,
|
|
572
|
+
isArray: el.dataset.array !== "false",
|
|
573
|
+
}));
|
|
574
|
+
fetchOptions.body = buildUrlEncodedBody(fields);
|
|
575
|
+
}
|
|
576
|
+
} else if (requestBodyType.includes("form-data")) {
|
|
577
|
+
const formDataEls = [...requestPanelEl.querySelectorAll("[data-ptype='form-data']")];
|
|
578
|
+
const fields = formDataEls.map((el: any) => ({
|
|
579
|
+
name: el.dataset.pname,
|
|
580
|
+
value: el.value,
|
|
581
|
+
file: el.type === "file" ? el.files?.[0] : undefined,
|
|
582
|
+
isArray: el.dataset.array !== "false",
|
|
583
|
+
}));
|
|
584
|
+
fetchOptions.body = buildMultipartFormData(fields);
|
|
585
|
+
} else if (
|
|
586
|
+
/^audio\/|^image\/|^video\/|^font\/|tar$|zip$|7z$|rtf$|msword$|excel$|\/pdf$|\/octet-stream$/.test(
|
|
587
|
+
requestBodyType,
|
|
588
|
+
)
|
|
589
|
+
) {
|
|
590
|
+
const bodyParamFileEl: any = requestPanelEl.querySelector(".request-body-param-file");
|
|
591
|
+
if (bodyParamFileEl?.files[0]) {
|
|
592
|
+
fetchOptions.body = bodyParamFileEl.files[0];
|
|
593
|
+
}
|
|
594
|
+
} else if (
|
|
595
|
+
requestBodyType.includes("json") ||
|
|
596
|
+
requestBodyType.includes("xml") ||
|
|
597
|
+
requestBodyType.includes("text")
|
|
598
|
+
) {
|
|
599
|
+
const exampleTextAreaEl: any = requestPanelEl.querySelector(
|
|
600
|
+
".request-body-param-user-input",
|
|
601
|
+
);
|
|
602
|
+
if (exampleTextAreaEl?.value) {
|
|
603
|
+
fetchOptions.body = exampleTextAreaEl.value;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return fetchOptions;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async onTryClick(e) {
|
|
612
|
+
const tryBtnEl = e?.target;
|
|
613
|
+
const requestPanelEl = tryBtnEl
|
|
614
|
+
? this.getRequestPanel(e)
|
|
615
|
+
: this.shadowRoot.querySelector(".request-panel");
|
|
616
|
+
const fetchUrl = this.buildFetchURL(requestPanelEl);
|
|
617
|
+
const fetchOptions = this.buildFetchBodyOptions(requestPanelEl);
|
|
618
|
+
const reqHeaders = this.buildFetchHeaders(requestPanelEl);
|
|
619
|
+
this.responseUrl = "";
|
|
620
|
+
this.responseHeaders = [];
|
|
621
|
+
this.curlSyntax = this.generateCURLSyntax(fetchUrl, reqHeaders, fetchOptions, requestPanelEl);
|
|
622
|
+
this.responseStatus = "success";
|
|
623
|
+
this.responseIsBlob = false;
|
|
624
|
+
|
|
625
|
+
this.respContentDisposition = "";
|
|
626
|
+
if (this.responseBlobUrl) {
|
|
627
|
+
URL.revokeObjectURL(this.responseBlobUrl);
|
|
628
|
+
this.responseBlobUrl = "";
|
|
629
|
+
}
|
|
630
|
+
if (this.fetchCredentials) {
|
|
631
|
+
fetchOptions.credentials = this.fetchCredentials;
|
|
632
|
+
}
|
|
633
|
+
const controller = new AbortController();
|
|
634
|
+
this.activeAbortController = controller;
|
|
635
|
+
const { signal } = controller;
|
|
636
|
+
fetchOptions.headers = reqHeaders;
|
|
637
|
+
const tempRequest = { url: fetchUrl, ...fetchOptions };
|
|
638
|
+
this.dispatchEvent(
|
|
639
|
+
new CustomEvent("before-try", {
|
|
640
|
+
bubbles: true,
|
|
641
|
+
composed: true,
|
|
642
|
+
detail: {
|
|
643
|
+
request: tempRequest,
|
|
644
|
+
controller,
|
|
645
|
+
},
|
|
646
|
+
}),
|
|
647
|
+
);
|
|
648
|
+
const updatedFetchOptions = {
|
|
649
|
+
method: tempRequest.method,
|
|
650
|
+
headers: tempRequest.headers,
|
|
651
|
+
credentials: tempRequest.credentials,
|
|
652
|
+
body: tempRequest.body,
|
|
653
|
+
};
|
|
654
|
+
let fetchRequest;
|
|
655
|
+
let fetchResponse;
|
|
656
|
+
let responseClone;
|
|
657
|
+
try {
|
|
658
|
+
this.loading = true;
|
|
659
|
+
this.responseText = "⌛";
|
|
660
|
+
this.responseMessage = "";
|
|
661
|
+
fetchRequest = new Request(tempRequest.url, updatedFetchOptions);
|
|
662
|
+
const startTime = performance.now();
|
|
663
|
+
fetchResponse = await fetch(fetchRequest, { signal });
|
|
664
|
+
const endTime = performance.now();
|
|
665
|
+
responseClone = fetchResponse.clone(); // create a response clone to allow reading response body again (response.json, response.text etc)
|
|
666
|
+
this.responseMessage = html`${fetchResponse.statusText ? `${fetchResponse.statusText}:${fetchResponse.status}` : fetchResponse.status}
|
|
667
|
+
<div style="color:var(--muted-foreground)">Took ${Math.round(endTime - startTime)} milliseconds</div>`;
|
|
668
|
+
this.responseUrl = fetchResponse.url;
|
|
669
|
+
const respHeadersObj = {};
|
|
670
|
+
fetchResponse.headers.forEach((hdrVal, hdr) => {
|
|
671
|
+
respHeadersObj[hdr] = hdrVal;
|
|
672
|
+
this.responseHeaders = `${this.responseHeaders}${hdr}: ${hdrVal}\n`;
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
const processed = await processFetchResponse(fetchResponse);
|
|
676
|
+
this.responseText = processed.responseText;
|
|
677
|
+
this.responseIsBlob = processed.responseIsBlob;
|
|
678
|
+
this.responseBlobType = processed.responseBlobType;
|
|
679
|
+
this.responseBlobUrl = processed.responseBlobUrl;
|
|
680
|
+
this.respContentDisposition = processed.respContentDisposition;
|
|
681
|
+
|
|
682
|
+
this.dispatchEvent(
|
|
683
|
+
new CustomEvent("after-try", {
|
|
684
|
+
bubbles: true,
|
|
685
|
+
composed: true,
|
|
686
|
+
detail: {
|
|
687
|
+
request: fetchRequest,
|
|
688
|
+
response: responseClone,
|
|
689
|
+
responseHeaders: respHeadersObj,
|
|
690
|
+
responseBody: processed.respJson || processed.respText || processed.respBlob,
|
|
691
|
+
responseStatus: responseClone.ok,
|
|
692
|
+
},
|
|
693
|
+
}),
|
|
694
|
+
);
|
|
695
|
+
} catch (err) {
|
|
696
|
+
if (err.name === "AbortError") {
|
|
697
|
+
this.dispatchEvent(
|
|
698
|
+
new CustomEvent("request-aborted", {
|
|
699
|
+
bubbles: true,
|
|
700
|
+
composed: true,
|
|
701
|
+
detail: {
|
|
702
|
+
err,
|
|
703
|
+
request: fetchRequest,
|
|
704
|
+
},
|
|
705
|
+
}),
|
|
706
|
+
);
|
|
707
|
+
this.responseMessage = "Request Aborted";
|
|
708
|
+
this.responseText = "Request Aborted";
|
|
709
|
+
} else {
|
|
710
|
+
this.dispatchEvent(
|
|
711
|
+
new CustomEvent("after-try", {
|
|
712
|
+
bubbles: true,
|
|
713
|
+
composed: true,
|
|
714
|
+
detail: {
|
|
715
|
+
err,
|
|
716
|
+
request: fetchRequest,
|
|
717
|
+
},
|
|
718
|
+
}),
|
|
719
|
+
);
|
|
720
|
+
if (tempRequest.method?.toUpperCase() === "TRACE") {
|
|
721
|
+
this.responseMessage = `${err.message} (Browser security restricts TRACE requests in fetch. Use the cURL snippet below.)`;
|
|
722
|
+
} else {
|
|
723
|
+
this.responseMessage = `${err.message} (CORS or Network Issue)`;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
} finally {
|
|
727
|
+
this.loading = false;
|
|
728
|
+
this.activeAbortController = null;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
liveCURLSyntaxUpdate(requestPanelEl) {
|
|
733
|
+
this.applyCURLSyntax(requestPanelEl);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
onGenerateCURLClick(e) {
|
|
737
|
+
const requestPanelEl = this.getRequestPanel(e);
|
|
738
|
+
this.applyCURLSyntax(requestPanelEl);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
getRequestPanel(e) {
|
|
742
|
+
return e.target.closest(".request-panel");
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
applyCURLSyntax(requestPanelEl) {
|
|
746
|
+
const fetchUrl = this.buildFetchURL(requestPanelEl);
|
|
747
|
+
const fetchOptions = this.buildFetchBodyOptions(requestPanelEl);
|
|
748
|
+
const fetchHeaders = this.buildFetchHeaders(requestPanelEl);
|
|
749
|
+
|
|
750
|
+
const newCurlSyntax = this.generateCURLSyntax(
|
|
751
|
+
fetchUrl,
|
|
752
|
+
fetchHeaders,
|
|
753
|
+
fetchOptions,
|
|
754
|
+
requestPanelEl,
|
|
755
|
+
);
|
|
756
|
+
if (this.curlSyntax !== newCurlSyntax) {
|
|
757
|
+
this.curlSyntax = newCurlSyntax;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
generateCURLSyntax(fetchUrl, fetchHeaders, fetchOptions, requestPanelEl) {
|
|
762
|
+
const requestBodyContainerEl = requestPanelEl?.querySelector(".request-body-container");
|
|
763
|
+
const requestBodyType = requestBodyContainerEl?.dataset?.selectedRequestBodyType || "";
|
|
764
|
+
let body = fetchOptions?.body;
|
|
765
|
+
|
|
766
|
+
if (!body && requestBodyContainerEl) {
|
|
767
|
+
const exampleTextAreaEl: any = requestPanelEl.querySelector(".request-body-param-user-input");
|
|
768
|
+
if (exampleTextAreaEl?.value) {
|
|
769
|
+
body = exampleTextAreaEl.value;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return generateCurlCommand({
|
|
774
|
+
method: this.method,
|
|
775
|
+
url: fetchUrl,
|
|
776
|
+
headers: fetchHeaders,
|
|
777
|
+
body,
|
|
778
|
+
contentType: requestBodyType,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
onAddFileInput(fieldName) {
|
|
783
|
+
const currentKeys = this.fileInputKeys[fieldName] ?? [0];
|
|
784
|
+
const nextKey = currentKeys.length > 0 ? Math.max(...currentKeys) + 1 : 0;
|
|
785
|
+
this.fileInputKeys = {
|
|
786
|
+
...this.fileInputKeys,
|
|
787
|
+
[fieldName]: [...currentKeys, nextKey],
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
onRemoveFileInput(fieldName, keyToRemove) {
|
|
792
|
+
const currentKeys = this.fileInputKeys[fieldName] ?? [0];
|
|
793
|
+
this.fileInputKeys = {
|
|
794
|
+
...this.fileInputKeys,
|
|
795
|
+
[fieldName]: currentKeys.filter((k) => k !== keyToRemove),
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
clearResponseData() {
|
|
800
|
+
this.responseUrl = "";
|
|
801
|
+
this.responseHeaders = "";
|
|
802
|
+
this.responseText = "";
|
|
803
|
+
this.responseStatus = "success";
|
|
804
|
+
this.responseMessage = "";
|
|
805
|
+
this.responseIsBlob = false;
|
|
806
|
+
this.responseBlobType = "";
|
|
807
|
+
this.respContentDisposition = "";
|
|
808
|
+
this.fileInputKeys = {};
|
|
809
|
+
if (this.responseBlobUrl) {
|
|
810
|
+
URL.revokeObjectURL(this.responseBlobUrl);
|
|
811
|
+
this.responseBlobUrl = "";
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
willUpdate(changedProperties) {
|
|
816
|
+
super.willUpdate?.(changedProperties);
|
|
817
|
+
// Sync configuration options passed down from parent <rapi-doc>
|
|
818
|
+
if (this.config && changedProperties.has("config")) {
|
|
819
|
+
if (this.config.renderStyle) this.renderStyle = this.config.renderStyle;
|
|
820
|
+
if (this.config.schemaStyle) this.schemaStyle = this.config.schemaStyle;
|
|
821
|
+
if (this.config.schemaExpandLevel !== undefined)
|
|
822
|
+
this.schemaExpandLevel = this.config.schemaExpandLevel;
|
|
823
|
+
if (this.config.schemaDescriptionExpanded !== undefined)
|
|
824
|
+
this.schemaDescriptionExpanded = this.config.schemaDescriptionExpanded;
|
|
825
|
+
if (this.config.allowSchemaDescriptionExpandToggle !== undefined)
|
|
826
|
+
this.allowSchemaDescriptionExpandToggle = this.config.allowSchemaDescriptionExpandToggle;
|
|
827
|
+
// In incoming requests, readOnly fields are hidden by default ('true') unless configured to 'never'.
|
|
828
|
+
// For incoming webhooks, the direction is inverted (the external caller sends the payload), so readOnly fields are shown ('false').
|
|
829
|
+
if (this.config.schemaHideReadOnly !== undefined)
|
|
830
|
+
this.schemaHideReadOnly =
|
|
831
|
+
this.config.schemaHideReadOnly === "never"
|
|
832
|
+
? "false"
|
|
833
|
+
: this.webhook === "true"
|
|
834
|
+
? "false"
|
|
835
|
+
: "true";
|
|
836
|
+
// In incoming requests, writeOnly fields are shown by default ('false').
|
|
837
|
+
// For webhooks, writeOnly fields are hidden ('true') unless configured to 'never'.
|
|
838
|
+
if (this.config.schemaHideWriteOnly !== undefined)
|
|
839
|
+
this.schemaHideWriteOnly =
|
|
840
|
+
this.config.schemaHideWriteOnly === "never"
|
|
841
|
+
? "false"
|
|
842
|
+
: this.webhook === "true"
|
|
843
|
+
? "true"
|
|
844
|
+
: "false";
|
|
845
|
+
if (this.config.fillRequestFieldsWithExample !== undefined)
|
|
846
|
+
this.fillRequestFieldsWithExample = this.config.fillRequestFieldsWithExample;
|
|
847
|
+
if (this.config.allowTry !== undefined) this.allowTry = this.config.allowTry;
|
|
848
|
+
if (this.config.showCurlBeforeTry !== undefined)
|
|
849
|
+
this.showCurlBeforeTry = this.config.showCurlBeforeTry;
|
|
850
|
+
if (this.config.fetchCredentials !== undefined)
|
|
851
|
+
this.fetchCredentials = this.config.fetchCredentials;
|
|
852
|
+
if (this.config.defaultSchemaTab) this.activeSchemaTab = this.config.defaultSchemaTab;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
disconnectedCallback() {
|
|
857
|
+
this.curlSyntax = "";
|
|
858
|
+
if (this.activeAbortController) {
|
|
859
|
+
this.activeAbortController.abort();
|
|
860
|
+
this.activeAbortController = null;
|
|
861
|
+
}
|
|
862
|
+
// Cleanup ObjectURL for the blob data if this component created one
|
|
863
|
+
if (this.responseBlobUrl) {
|
|
864
|
+
URL.revokeObjectURL(this.responseBlobUrl);
|
|
865
|
+
this.responseBlobUrl = "";
|
|
866
|
+
}
|
|
867
|
+
super.disconnectedCallback();
|
|
868
|
+
}
|
|
869
|
+
}
|