@scalar/api-client 3.16.3 → 3.18.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/CHANGELOG.md +21 -0
- package/README.md +18 -0
- package/dist/style.css +25 -36
- package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts +2 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +38 -10
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.d.ts +4 -0
- package/dist/v2/blocks/operation-block/components/Header.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.js +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.script.js +14 -9
- package/dist/v2/blocks/operation-block/components/Header.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts +2 -0
- package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.js +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js +3 -0
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts +6 -0
- package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.js +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js +4 -3
- package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js +3 -2
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts +4 -0
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js +61 -24
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/modal/Modal.vue.d.ts +2 -0
- package/dist/v2/features/modal/Modal.vue.d.ts.map +1 -1
- package/dist/v2/features/modal/Modal.vue.js.map +1 -1
- package/dist/v2/features/modal/Modal.vue.script.js +3 -0
- package/dist/v2/features/modal/Modal.vue.script.js.map +1 -1
- package/dist/v2/features/modal/helpers/create-api-client-modal.d.ts.map +1 -1
- package/dist/v2/features/modal/helpers/create-api-client-modal.js +5 -1
- package/dist/v2/features/modal/helpers/create-api-client-modal.js.map +1 -1
- package/dist/v2/features/modal/helpers/resolve-route-parameters.d.ts +12 -3
- package/dist/v2/features/modal/helpers/resolve-route-parameters.d.ts.map +1 -1
- package/dist/v2/features/modal/helpers/resolve-route-parameters.js +13 -9
- package/dist/v2/features/modal/helpers/resolve-route-parameters.js.map +1 -1
- package/dist/v2/features/modal/hooks/use-modal-sidebar.d.ts +3 -1
- package/dist/v2/features/modal/hooks/use-modal-sidebar.d.ts.map +1 -1
- package/dist/v2/features/modal/hooks/use-modal-sidebar.js +28 -7
- package/dist/v2/features/modal/hooks/use-modal-sidebar.js.map +1 -1
- package/dist/v2/features/modal/modal-events.d.ts.map +1 -1
- package/dist/v2/features/modal/modal-events.js +2 -1
- package/dist/v2/features/modal/modal-events.js.map +1 -1
- package/dist/v2/features/operation/Operation.vue.d.ts +2 -0
- package/dist/v2/features/operation/Operation.vue.d.ts.map +1 -1
- package/dist/v2/features/operation/Operation.vue.js.map +1 -1
- package/dist/v2/features/operation/Operation.vue.script.js +8 -1
- package/dist/v2/features/operation/Operation.vue.script.js.map +1 -1
- package/dist/v2/helpers/generate-location-id.d.ts +3 -1
- package/dist/v2/helpers/generate-location-id.d.ts.map +1 -1
- package/dist/v2/helpers/generate-location-id.js +2 -1
- package/dist/v2/helpers/generate-location-id.js.map +1 -1
- package/dist/vue-styles.css +24 -24
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @scalar/api-client
|
|
2
2
|
|
|
3
|
+
## 3.18.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#9937](https://github.com/scalar/scalar/pull/9937): Add a way to open the request body editor in the Form view by default. Set the `defaultRequestBodyView: 'form'` config option, or the `x-scalar-default-request-body-view` extension in your OpenAPI document (which also works per source). Defaults to `raw`, and falls back to `raw` when a body cannot be shown as a form.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#10046](https://github.com/scalar/scalar/pull/10046): Stop sending optional form-body properties by default. Optional `multipart/form-data` and `application/x-www-form-urlencoded` properties now start unchecked and are left out of the request unless you enable them, matching how optional parameters already behave. Required properties are unaffected.
|
|
12
|
+
|
|
13
|
+
## 3.17.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- feat: test OpenAPI webhooks from the API reference and API client
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- [#9983](https://github.com/scalar/scalar/pull/9983): Bump the `zod` catalog to `^4.4.3` so the standalone bundle ships a single `zod` instead of two (`4.3.5` from `@scalar/types` plus `4.4.3` from the `ai` / `@ai-sdk` peer). This makes `standalone.js` ~68KB raw / ~18KB gzip smaller.
|
|
22
|
+
- [#10024](https://github.com/scalar/scalar/pull/10024): Fix "Test Request" discarding an edited request body when the operation it opens is already the one on screen. Reopening the entry the modal already shows does not route anywhere, so the request body kept its edited value while the composition selection was replaced with whatever the reference page had selected. The request body read that as a manual `oneOf`/`anyOf` branch switch and regenerated itself from the schema. An open modal now keeps the selection it is already showing, while opening a different entry still re-establishes it.
|
|
23
|
+
|
|
3
24
|
## 3.16.3
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -175,9 +175,27 @@ The `createApiClientModal` call returns:
|
|
|
175
175
|
method: HttpMethod
|
|
176
176
|
example?: string
|
|
177
177
|
documentSlug?: string // For multiple documents
|
|
178
|
+
isWebhook?: boolean // Resolve path as an OpenAPI webhook name
|
|
178
179
|
}
|
|
179
180
|
```
|
|
180
181
|
|
|
182
|
+
### OpenAPI webhooks
|
|
183
|
+
|
|
184
|
+
Set `isWebhook` to open a webhook from an OpenAPI 3.1 document. Pass the
|
|
185
|
+
webhook map key as `path`:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
modal.open({
|
|
189
|
+
path: 'delivery.created',
|
|
190
|
+
method: 'post',
|
|
191
|
+
isWebhook: true,
|
|
192
|
+
})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The modal prompts for the destination URL. Destination edits apply only to
|
|
196
|
+
the request and do not change the OpenAPI document. In the API reference, the
|
|
197
|
+
**Test Request** button opens webhook entries this way automatically.
|
|
198
|
+
|
|
181
199
|
|
|
182
200
|
## Plugins
|
|
183
201
|
|
package/dist/style.css
CHANGED
|
@@ -491,6 +491,7 @@
|
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
:where(.scalar-app) input:-webkit-autofill {
|
|
494
|
+
-webkit-text-fill-color: var(--scalar-color-1);
|
|
494
495
|
-webkit-background-clip: text !important;
|
|
495
496
|
background-clip: text !important;
|
|
496
497
|
}
|
|
@@ -3034,10 +3035,6 @@
|
|
|
3034
3035
|
min-width: 100%;
|
|
3035
3036
|
}
|
|
3036
3037
|
|
|
3037
|
-
.scalar-app .min-w-min {
|
|
3038
|
-
min-width: min-content;
|
|
3039
|
-
}
|
|
3040
|
-
|
|
3041
3038
|
.scalar-app .flex-1 {
|
|
3042
3039
|
flex: 1;
|
|
3043
3040
|
}
|
|
@@ -4696,10 +4693,6 @@
|
|
|
4696
4693
|
min-width: 20px;
|
|
4697
4694
|
}
|
|
4698
4695
|
|
|
4699
|
-
:is(.scalar-app .\*\:flex-1 > *) {
|
|
4700
|
-
flex: 1;
|
|
4701
|
-
}
|
|
4702
|
-
|
|
4703
4696
|
:is(.scalar-app .\*\:cursor-pointer > *) {
|
|
4704
4697
|
cursor: pointer;
|
|
4705
4698
|
}
|
|
@@ -4712,10 +4705,6 @@
|
|
|
4712
4705
|
justify-content: center;
|
|
4713
4706
|
}
|
|
4714
4707
|
|
|
4715
|
-
:is(.scalar-app .\*\:gap-1 > *) {
|
|
4716
|
-
gap: 4px;
|
|
4717
|
-
}
|
|
4718
|
-
|
|
4719
4708
|
:is(.scalar-app .\*\:rounded > *) {
|
|
4720
4709
|
border-radius: var(--scalar-radius-md);
|
|
4721
4710
|
}
|
|
@@ -6418,7 +6407,7 @@ to {
|
|
|
6418
6407
|
margin-left: 0.5px;
|
|
6419
6408
|
}
|
|
6420
6409
|
|
|
6421
|
-
[data-v-
|
|
6410
|
+
[data-v-928dfb0c] .cm-content {
|
|
6422
6411
|
font-size: var(--scalar-small);
|
|
6423
6412
|
}
|
|
6424
6413
|
|
|
@@ -6470,20 +6459,20 @@ input[data-v-a6f4752a]::placeholder {
|
|
|
6470
6459
|
scrollbar-width: none;
|
|
6471
6460
|
}
|
|
6472
6461
|
|
|
6473
|
-
.request-section-content[data-v-
|
|
6462
|
+
.request-section-content[data-v-bfd0db87] {
|
|
6474
6463
|
--scalar-border-width: 0.5px;
|
|
6475
6464
|
}
|
|
6476
|
-
.request-section-content-filter[data-v-
|
|
6465
|
+
.request-section-content-filter[data-v-bfd0db87] {
|
|
6477
6466
|
box-shadow: 0 -10px 0 10px var(--scalar-background-1);
|
|
6478
6467
|
}
|
|
6479
|
-
.request-item:focus-within .request-meta-buttons[data-v-
|
|
6468
|
+
.request-item:focus-within .request-meta-buttons[data-v-bfd0db87] {
|
|
6480
6469
|
opacity: 1;
|
|
6481
6470
|
}
|
|
6482
|
-
.group-hover-input[data-v-
|
|
6471
|
+
.group-hover-input[data-v-bfd0db87] {
|
|
6483
6472
|
border-width: var(--scalar-border-width);
|
|
6484
6473
|
border-color: transparent;
|
|
6485
6474
|
}
|
|
6486
|
-
.group:hover .group-hover-input[data-v-
|
|
6475
|
+
.group:hover .group-hover-input[data-v-bfd0db87] {
|
|
6487
6476
|
background: color-mix(
|
|
6488
6477
|
in srgb,
|
|
6489
6478
|
var(--scalar-background-1),
|
|
@@ -6491,7 +6480,7 @@ input[data-v-a6f4752a]::placeholder {
|
|
|
6491
6480
|
);
|
|
6492
6481
|
border-color: var(--scalar-border-color);
|
|
6493
6482
|
}
|
|
6494
|
-
.group-hover-input[data-v-
|
|
6483
|
+
.group-hover-input[data-v-bfd0db87]:focus {
|
|
6495
6484
|
background: transparent !important;
|
|
6496
6485
|
border-color: var(--scalar-border-color) !important;
|
|
6497
6486
|
}
|
|
@@ -6614,21 +6603,21 @@ to {
|
|
|
6614
6603
|
background: var(--scalar-background-2);
|
|
6615
6604
|
}
|
|
6616
6605
|
|
|
6617
|
-
[data-v-
|
|
6606
|
+
[data-v-c414dc68] .cm-editor {
|
|
6618
6607
|
height: 100%;
|
|
6619
6608
|
outline: none;
|
|
6620
6609
|
width: 100%;
|
|
6621
6610
|
}
|
|
6622
|
-
[data-v-
|
|
6611
|
+
[data-v-c414dc68] .cm-line {
|
|
6623
6612
|
padding: 0;
|
|
6624
6613
|
}
|
|
6625
|
-
[data-v-
|
|
6614
|
+
[data-v-c414dc68] .cm-content {
|
|
6626
6615
|
padding: 0;
|
|
6627
6616
|
display: flex;
|
|
6628
6617
|
align-items: center;
|
|
6629
6618
|
font-size: var(--scalar-small);
|
|
6630
6619
|
}
|
|
6631
|
-
.scroll-timeline-x[data-v-
|
|
6620
|
+
.scroll-timeline-x[data-v-c414dc68] {
|
|
6632
6621
|
-ms-overflow-style: none;
|
|
6633
6622
|
/* Fade the URL at the viewport edges. Mask lives on the scrollport (this
|
|
6634
6623
|
element) rather than on `.cm-scroller`, whose width follows the URL's
|
|
@@ -6641,31 +6630,31 @@ to {
|
|
|
6641
6630
|
transparent 100%
|
|
6642
6631
|
);
|
|
6643
6632
|
}
|
|
6644
|
-
.scroll-timeline-x-hidden[data-v-
|
|
6633
|
+
.scroll-timeline-x-hidden[data-v-c414dc68] {
|
|
6645
6634
|
overflow-x: auto;
|
|
6646
6635
|
}
|
|
6647
|
-
.scroll-timeline-x-hidden[data-v-
|
|
6636
|
+
.scroll-timeline-x-hidden[data-v-c414dc68] .cm-scroller {
|
|
6648
6637
|
scrollbar-width: none;
|
|
6649
6638
|
-ms-overflow-style: none;
|
|
6650
6639
|
padding-right: 20px;
|
|
6651
6640
|
overflow: auto;
|
|
6652
6641
|
}
|
|
6653
|
-
.scroll-timeline-x-hidden[data-v-
|
|
6642
|
+
.scroll-timeline-x-hidden[data-v-c414dc68]::-webkit-scrollbar {
|
|
6654
6643
|
width: 0;
|
|
6655
6644
|
height: 0;
|
|
6656
6645
|
display: none;
|
|
6657
6646
|
}
|
|
6658
|
-
.scroll-timeline-x-hidden[data-v-
|
|
6647
|
+
.scroll-timeline-x-hidden[data-v-c414dc68] .cm-scroller::-webkit-scrollbar {
|
|
6659
6648
|
width: 0;
|
|
6660
6649
|
height: 0;
|
|
6661
6650
|
display: none;
|
|
6662
6651
|
}
|
|
6663
|
-
.scroll-timeline-x-address[data-v-
|
|
6652
|
+
.scroll-timeline-x-address[data-v-c414dc68] {
|
|
6664
6653
|
line-height: 27px;
|
|
6665
6654
|
scrollbar-width: none; /* Firefox */
|
|
6666
6655
|
}
|
|
6667
6656
|
/* make clickable are to left of send button */
|
|
6668
|
-
.scroll-timeline-x-address[data-v-
|
|
6657
|
+
.scroll-timeline-x-address[data-v-c414dc68]:after {
|
|
6669
6658
|
content: '';
|
|
6670
6659
|
position: absolute;
|
|
6671
6660
|
height: 100%;
|
|
@@ -6673,12 +6662,12 @@ to {
|
|
|
6673
6662
|
right: 0;
|
|
6674
6663
|
cursor: text;
|
|
6675
6664
|
}
|
|
6676
|
-
.scroll-timeline-x-address[data-v-
|
|
6665
|
+
.scroll-timeline-x-address[data-v-c414dc68]:empty:before {
|
|
6677
6666
|
content: 'Enter URL or cURL request';
|
|
6678
6667
|
color: var(--scalar-color-3);
|
|
6679
6668
|
pointer-events: none;
|
|
6680
6669
|
}
|
|
6681
|
-
.address-bar-bg-states[data-v-
|
|
6670
|
+
.address-bar-bg-states[data-v-c414dc68] {
|
|
6682
6671
|
--scalar-address-bar-bg: color-mix(
|
|
6683
6672
|
in srgb,
|
|
6684
6673
|
var(--scalar-background-1),
|
|
@@ -6686,22 +6675,22 @@ to {
|
|
|
6686
6675
|
);
|
|
6687
6676
|
background: var(--scalar-address-bar-bg);
|
|
6688
6677
|
}
|
|
6689
|
-
.address-bar-bg-states[data-v-
|
|
6678
|
+
.address-bar-bg-states[data-v-c414dc68]:has(.cm-focused) {
|
|
6690
6679
|
--scalar-address-bar-bg: var(--scalar-background-1);
|
|
6691
6680
|
border-color: var(--scalar-border-color);
|
|
6692
6681
|
outline-width: 1px;
|
|
6693
6682
|
outline-style: solid;
|
|
6694
6683
|
}
|
|
6695
|
-
.address-bar-bg-states:has(.cm-focused) .fade-left[data-v-
|
|
6696
|
-
.address-bar-bg-states:has(.cm-focused) .fade-right[data-v-
|
|
6684
|
+
.address-bar-bg-states:has(.cm-focused) .fade-left[data-v-c414dc68],
|
|
6685
|
+
.address-bar-bg-states:has(.cm-focused) .fade-right[data-v-c414dc68] {
|
|
6697
6686
|
--scalar-address-bar-bg: var(--scalar-background-1);
|
|
6698
6687
|
}
|
|
6699
6688
|
|
|
6700
|
-
.app-exit-button[data-v-
|
|
6689
|
+
.app-exit-button[data-v-3769522e] {
|
|
6701
6690
|
color: white;
|
|
6702
6691
|
background: rgba(0, 0, 0, 0.1);
|
|
6703
6692
|
}
|
|
6704
|
-
.app-exit-button[data-v-
|
|
6693
|
+
.app-exit-button[data-v-3769522e]:hover {
|
|
6705
6694
|
background: rgba(255, 255, 255, 0.1);
|
|
6706
6695
|
}
|
|
6707
6696
|
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
@@ -44,6 +44,8 @@ export type OperationBlockProps = {
|
|
|
44
44
|
path: string;
|
|
45
45
|
/** Current request method */
|
|
46
46
|
method: HttpMethodType;
|
|
47
|
+
/** Whether `path` identifies an OpenAPI webhook. */
|
|
48
|
+
isWebhook?: boolean;
|
|
47
49
|
/** HTTP clients */
|
|
48
50
|
httpClients: AvailableClients;
|
|
49
51
|
/** The history for the operation */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OperationBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OperationBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"names":[],"mappings":"AAovBA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mCAAmC,CAAA;AACrF,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAA;AAG/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iDAAiD,CAAA;AACnF,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,iBAAiB,EAClB,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAML,KAAK,qBAAqB,EAE1B,KAAK,0BAA0B,EAChC,MAAM,yCAAyC,CAAA;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qEAAqE,CAAA;AACxG,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACb,MAAM,8DAA8D,CAAA;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uDAAuD,CAAA;AAyB5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD;;;;;;;;;;;;GAYG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAMzC,MAAM,MAAM,mBAAmB,GAAG;IAChC,gBAAgB;IAChB,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,uBAAuB;IACvB,QAAQ,EAAE,eAAe,CAAA;IACzB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,wBAAwB;IACxB,gBAAgB,EAAE,aAAa,EAAE,CAAA;IACjC,uBAAuB;IACvB,eAAe,EAAE,aAAa,EAAE,CAAA;IAChC,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,6BAA6B;IAC7B,MAAM,EAAE,cAAc,CAAA;IACtB,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,mBAAmB;IACnB,WAAW,EAAE,gBAAgB,CAAA;IAC7B,oCAAoC;IACpC,OAAO,CAAC,EAAE,YAAY,EAAE,CAAA;IACxB,oBAAoB;IACpB,MAAM,EAAE,YAAY,CAAA;IACpB,gCAAgC;IAChC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;IAC3B,gCAAgC;IAChC,cAAc,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC,yBAAyB,CAAC,CAAA;IACtE,mDAAmD;IACnD,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,sCAAsC;IACtC,UAAU,EAAE,UAAU,CAAA;IACtB,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oBAAoB;IACpB,MAAM,CAAC,EAAE,SAAS,GAAG,eAAe,CAAA;IACpC,uBAAuB;IACvB,SAAS,EAAE,eAAe,CAAA;IAC1B,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAA;IAClB,2CAA2C;IAC3C,QAAQ,EAAE,QAAQ,CAAA;IAClB,wCAAwC;IACxC,eAAe,EAAE,qBAAqB,CAAA;IACtC,qBAAqB;IACrB,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,uBAAuB;IACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,qCAAqC;IACrC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,8CAA8C;IAC9C,WAAW,EAAE,kBAAkB,CAAA;IAC/B,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,kCAAkC;IAClC,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,0CAA0C;IAC1C,uBAAuB,EAAE,0BAA0B,EAAE,CAAA;IACrD,4BAA4B;IAC5B,oBAAoB,EAAE,eAAe,CAAC,UAAU,CAAC,CAAA;IACjD,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC,sEAAsE;IACtE,+BAA+B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxD,6CAA6C;IAC7C,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;CAChC,CAAA;AACD,QAAA,MAAM,YAAY,kTA8uBd,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OperationBlock.vue.js","names":[],"sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * OperationBlock\n *\n * Orchestrates the operation view by wiring together the Header, OperationBlock,\n * and ResponseBlock. Forwards user interactions to the workspace event bus and\n * passes through configuration such as auth, servers, plugins, and environment.\n * This component keeps the Operation page lean by centralizing event emission\n * and prop wiring between the blocks.\n *\n * Notable behavior:\n * - Uses operation['x-scalar-method'] and operation['x-scalar-path'] to provide\n * draft overrides for the UI when present.\n */\nexport default {\n name: 'OperationBlock',\n}\n\nexport type OperationBlockProps = {\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Application version */\n appVersion: string\n /** Openapi document */\n document: OpenApiDocument\n /** Openapi document slug */\n documentSlug: string\n /** Workspace cookies */\n workspaceCookies: XScalarCookie[]\n /** Document cookies */\n documentCookies: XScalarCookie[]\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** HTTP clients */\n httpClients: AvailableClients\n /** The history for the operation */\n history?: HistoryEntry[]\n /** Client layout */\n layout: ClientLayout\n /** Currently selected server */\n server: ServerObject | null\n /** Currently selected client */\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** Meta information for the server */\n serverMeta: ServerMeta\n /** Hides the client button on the header */\n hideClientButton?: boolean\n /** Client integration */\n integration?: string | null\n /** Openapi document url for `modal` mode to open the client app */\n documentUrl?: string\n /** Client source */\n source?: 'gitbook' | 'api-reference'\n /** Operation object */\n operation: OperationObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Meta information for the auth update */\n authMeta: AuthMeta\n /** Document defined security schemes */\n securitySchemes: MergedSecuritySchemes\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Environment list */\n environments?: string[]\n /** Currently selected environment */\n activeEnvironment?: string\n /** For environment variables in the inputs */\n environment: XScalarEnvironment\n /** The proxy URL for sending requests */\n proxyUrl: string\n /** Currently selected security */\n selectedSecurity: SelectedSecurity\n /** Currently selected security schemes */\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n /** Security requirements */\n securityRequirements: OpenApiDocument['security']\n /** Default headers */\n defaultHeaders: Record<string, string>\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /** Subset of config options for the modal */\n options?: ModalProps['options']\n}\n</script>\n<script setup lang=\"ts\">\nimport { generateClientOptions } from '@scalar/blocks/code-example'\nimport { ERRORS } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport { buildSafeBodyRequest } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { executeHook, type ClientPlugin } from '@scalar/oas-utils/helpers'\nimport {\n AVAILABLE_CLIENTS,\n type AvailableClients,\n} from '@scalar/types/snippetz'\nimport { useClipboard } from '@scalar/use-hooks/useClipboard'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type { HistoryEntry } from '@scalar/workspace-store/entities/history/schema'\nimport type {\n AuthMeta,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n buildRequest,\n createVariablesStoreForRequest,\n getEnvironmentVariables,\n requestFactory,\n resolveExecutableRequestUrl,\n type MergedSecuritySchemes,\n type RequestPayload,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/operation'\nimport { computed, onBeforeUnmount, onMounted, ref, toValue, watch } from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport { harToFetchRequest } from '@/v2/blocks/operation-block/helpers/har-to-fetch-request'\nimport { harToFetchResponse } from '@/v2/blocks/operation-block/helpers/har-to-fetch-response'\nimport {\n getCookieRequestUrl,\n getResponseCookieActions,\n} from '@/v2/blocks/operation-block/helpers/persist-response-cookies'\nimport { getEnvironmentPersistenceActions } from '@/v2/blocks/operation-block/helpers/persist-script-environment'\nimport {\n getOperationExampleKey,\n isStreamingResponse,\n responseCache,\n} from '@/v2/blocks/operation-block/helpers/response-cache'\nimport {\n sendRequest,\n type ResponseInstance,\n} from '@/v2/blocks/operation-block/helpers/send-request'\nimport { validatePathParameters } from '@/v2/blocks/operation-block/helpers/validate-path-parameters'\nimport { RequestBlock } from '@/v2/blocks/request-block'\nimport { ResponseBlock } from '@/v2/blocks/response-block'\nimport { type History } from '@/v2/blocks/scalar-address-bar-block'\nimport type { ModalProps } from '@/v2/features/modal/Modal.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport Header from './components/Header.vue'\n\nconst {\n authMeta,\n environment,\n eventBus,\n exampleKey,\n document,\n documentSlug,\n workspaceCookies = [],\n documentCookies = [],\n hideClientButton,\n httpClients = AVAILABLE_CLIENTS,\n history = [],\n layout,\n method,\n operation,\n path,\n plugins = [],\n proxyUrl,\n requestBodyCompositionSelection,\n securitySchemes,\n selectedClient,\n server,\n environments,\n options,\n activeEnvironment,\n serverMeta,\n selectedSecurity,\n selectedSecuritySchemes,\n securityRequirements,\n defaultHeaders,\n} = defineProps<OperationBlockProps>()\n\n/** Hoist up client generation so it doesn't get re-generated on every operation */\nconst clientOptions = computed(() => generateClientOptions(httpClients))\n\nconst { toast } = useToasts()\nconst { copyToClipboard } = useClipboard()\n\n// Refs\nconst abortController = ref<AbortController | null>(null)\nconst response = ref<ResponseInstance | null>(null)\nconst requestPayload = ref<RequestPayload | null>(null)\n\n/** Cancel the request */\nconst cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED)\n\n/**\n * Persist server-set cookies from a response into the document cookie jar.\n *\n * Scalar keeps its own cookie jar because browsers hide `Set-Cookie` from `fetch`.\n * Mirroring browser behavior, cookies are stored scoped to the request host and\n * path and removed when the server expires them, so a value like a Django CSRF\n * token stays available for later PUT/PATCH requests after a page reload.\n */\nconst persistResponseCookies = (sendResult: {\n response: ResponseInstance\n requestPayload: RequestPayload\n}) => {\n const actions = getResponseCookieActions({\n cookieHeaderKeys: sendResult.response.cookieHeaderKeys ?? [],\n documentCookies,\n requestUrl: getCookieRequestUrl(String(sendResult.requestPayload[0])),\n })\n\n for (const action of actions) {\n if (action.type === 'delete') {\n eventBus.emit('cookie:delete:cookie', {\n collectionType: 'document',\n cookieName: action.cookieName,\n index: action.index,\n })\n continue\n }\n\n eventBus.emit('cookie:upsert:cookie', {\n collectionType: 'document',\n payload: action.cookie,\n ...(action.index === undefined ? {} : { index: action.index }),\n })\n }\n}\n\n/**\n * Copy the executable URL — same pipeline as Send (`requestFactory` +\n * `resolveExecutableRequestUrl`), including security query params.\n */\nconst copyAddressBarUrl = async (): Promise<void> => {\n const { request } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies: [...workspaceCookies, ...documentCookies],\n method,\n operation,\n path,\n proxyUrl,\n server,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n await copyToClipboard(\n resolveExecutableRequestUrl(request, getEnvironmentVariables(environment)),\n )\n}\n\n/** Execute the current operation example */\nconst handleExecute = async () => {\n eventBus.flushDebouncedEmits?.()\n\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast('Path parameters must have values.', 'error')\n return\n }\n\n const globalCookies = [...workspaceCookies, ...documentCookies]\n\n const { request: requestBuilder } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies,\n method,\n operation,\n path,\n proxyUrl,\n server,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n // Stop any previous streaming response\n if (response.value && 'reader' in response.value) {\n response.value.reader.cancel()\n }\n\n const variablesStore = createVariablesStoreForRequest()\n\n // Seed the script variable store with the active environment so pm.environment.get()\n // and pm.variables.get() resolve to the same values as {{…}} placeholders. Without\n // seeding, the store starts empty and every scripted read returns undefined.\n const seededEnvironment = getEnvironmentVariables(environment)\n variablesStore.setEnvironment?.(\n Object.entries(seededEnvironment).map(([key, value]) => ({ key, value })),\n )\n\n // Snapshot the persistence target at request start, alongside the seed. Persistence runs after\n // the request completes (awaits below), and the environment props are reactive — if the active\n // environment is switched mid-flight, or a concurrent request splices these arrays, reading\n // them at persist time would apply the script's writes to the wrong environment or resolve\n // workspace/document indexes against a shifted list. Copy the arrays (not just the reference)\n // so the seeded values and these targets stay fixed to the same point in time.\n const persistenceTarget = activeEnvironment\n ? {\n environmentName: activeEnvironment,\n mergedVariables: [...environment.variables],\n documentVariables: [\n ...(document['x-scalar-environments']?.[activeEnvironment]\n ?.variables ?? []),\n ],\n environmentExistsOnDocument: Boolean(\n document['x-scalar-environments']?.[activeEnvironment],\n ),\n }\n : null\n\n // Execute the beforeRequest hook (plugins receive RequestFactory, not fetch Request)\n await executeHook(\n {\n requestBuilder,\n document,\n operation,\n variablesStore,\n server,\n customFetch: toValue(options)?.customFetch,\n },\n 'beforeRequest',\n plugins,\n )\n\n // Persist environment variables set by scripts (pm.environment.set / unset) back to the active\n // environment so they survive to the next request, mirroring the Environment tab. Scope and\n // index are resolved from the seed-time environment so updates mutate in place. Defined here so\n // it can run on every exit path after the pre-request scripts — including a build failure or a\n // failed send — so a pre-request set, like a token stored before a flaky call, is not lost.\n const persistScriptEnvironment = () => {\n if (!persistenceTarget) {\n return\n }\n\n const environmentActions = getEnvironmentPersistenceActions({\n ...persistenceTarget,\n seededVariables: seededEnvironment,\n scriptVariables: variablesStore.getEnvironment(),\n })\n\n for (const action of environmentActions) {\n if (action.type === 'delete') {\n eventBus.emit('environment:delete:environment-variable', {\n environmentName: action.environmentName,\n index: action.index,\n collectionType: action.collectionType,\n })\n } else {\n eventBus.emit('environment:upsert:environment-variable', {\n environmentName: action.environmentName,\n variable: action.variable,\n index: action.index,\n collectionType: action.collectionType,\n })\n }\n }\n }\n\n // Resolve {{…}} placeholders from the script store so pre-request pm.environment.set() and\n // unset() take effect on this request. The store was seeded from the active environment, so\n // it already holds every value spreading seededEnvironment would add — minus the keys a script\n // unset, which must stay dropped (an object spread cannot remove them again).\n const envVariables = variablesStore.getVariables()\n\n // Build the fetch Request after hooks may have mutated the factory\n const built = buildRequest(requestBuilder, {\n envVariables,\n allowMissingRequestServerBase: layout === 'modal',\n })\n if (!built.ok) {\n // Save any pre-request script writes before bailing out on a build failure.\n persistScriptEnvironment()\n toast(built.message ?? built.error, 'error')\n return\n }\n\n // Store the abort controller for cancellation\n abortController.value = built.data.controller\n\n // Build the fetch Request once so the requestBuilt hook observes the exact bytes that are\n // sent. Rebuilding it later would generate a different multipart boundary, which breaks\n // plugins that hash the body (for example, request signing).\n const request = buildSafeBodyRequest(...built.data.requestPayload)\n\n // Execute the requestBuilt hook (plugins receive the exact fetch Request that will be sent)\n await executeHook(\n {\n request,\n requestBuilder,\n document,\n operation,\n variablesStore,\n },\n 'requestBuilt',\n plugins,\n )\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:sent', {\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n /** Execute the request */\n const [sendError, sendResult] = await sendRequest({\n isUsingProxy: built.data.isUsingProxy,\n requestPayload: built.data.requestPayload,\n request,\n plugins,\n customFetch: toValue(options)?.customFetch,\n })\n\n if (sendResult) {\n // Execute the responseReceived hook\n await executeHook(\n {\n response: sendResult.originalResponse.clone(),\n requestBuilder,\n request: buildSafeBodyRequest(...sendResult.requestPayload),\n document,\n operation,\n variablesStore,\n },\n 'responseReceived',\n plugins,\n )\n }\n\n // Save script environment writes (pre-request and, on success, post-response) back to the\n // active environment. Runs even when the send fails so a pre-request set is not lost.\n persistScriptEnvironment()\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:complete', {\n payload: sendResult\n ? {\n response: sendResult.originalResponse.clone(),\n requestPayload: sendResult.requestPayload,\n duration: sendResult.response.duration,\n timestamp: sendResult.timestamp,\n }\n : undefined,\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n if (sendError) {\n // clean up the response and request\n response.value = null\n requestPayload.value = null\n abortController.value = null\n\n toast(sendError.message, 'error')\n return\n }\n\n // Store the response\n response.value = sendResult.response\n requestPayload.value = sendResult.requestPayload\n\n // Persist server-set cookies into the document jar so values like a CSRF token\n // survive a page reload and get replayed on the next request, like a browser would.\n persistResponseCookies(sendResult)\n\n // Cache non-streaming responses so they can be restored when navigating back\n if (!isStreamingResponse(sendResult.response)) {\n responseCache.set(getOperationExampleKey(method, path, exampleKey), {\n response: sendResult.response,\n requestPayload: sendResult.requestPayload,\n })\n }\n}\n\nonMounted(() => {\n eventBus.on('operation:send:request:hotkey', handleExecute)\n eventBus.on('operation:cancel:request', cancelRequest)\n eventBus.on('copy-url:address-bar', copyAddressBarUrl)\n\n // Let plugins warm up per-operation resources (e.g. the scripts sandbox) so the first request\n // does not pay their cold-start cost.\n void executeHook({ document, operation }, 'onRequestMount', plugins)\n})\nonBeforeUnmount(() => {\n eventBus.off('operation:send:request:hotkey', handleExecute)\n eventBus.off('operation:cancel:request', cancelRequest)\n eventBus.off('copy-url:address-bar', copyAddressBarUrl)\n})\n\nconst operationHistory = computed<History[]>(() =>\n history\n .map((entry) => ({\n method: entry.request.method as HttpMethodType,\n path: entry.request.url,\n duration: entry.time,\n status: entry.response.status,\n }))\n .reverse(),\n)\n\nconst handleSelectHistoryItem = ({ index }: { index: number }) => {\n const transformedIndex = (history.length ?? 0) - index - 1\n const historyItem = history[transformedIndex]\n if (!historyItem) {\n return\n }\n\n const navigate = () =>\n eventBus.emit('ui:navigate', {\n page: 'example',\n method,\n path,\n exampleName: 'draft',\n callback: (status) => {\n // Do not replace the response if the navigation was not successful\n if (status !== 'success') {\n return\n }\n // Reconstruct the response\n const fetchResponse = harToFetchResponse({\n harResponse: historyItem.response,\n url: historyItem.request.url,\n method,\n path,\n duration: historyItem.time,\n })\n\n // Reconstruct the request\n const fetchRequest = harToFetchRequest({\n harRequest: historyItem.request,\n })\n\n // Update the response and request\n response.value = fetchResponse\n requestPayload.value = fetchRequest\n },\n })\n\n eventBus.emit('operation:reload:history', {\n meta: {\n path,\n method,\n },\n index: transformedIndex,\n callback: navigate,\n })\n}\n\nconst handleNavigateSettings = () => {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'overview',\n operationPath: path,\n method,\n })\n}\n\n/**\n * When the path, method, or example key changes, restore the response panel\n * for the new operation from the best available source:\n *\n * 1. `responseCache` — in-memory, populated on every successful send.\n * Has the live response object (including streams) so it wins.\n * 2. `history` — persisted in the workspace store. Used as a fallback when\n * the in-memory cache is empty (page reload, fresh app session, etc.)\n * so navigating to an operation that has been called before shows the\n * last response instead of an empty panel.\n * 3. Otherwise clear.\n *\n * Only the response is restored from history — the user's request inputs are\n * left alone, so we never silently overwrite an in-progress draft.\n */\nwatch(\n [() => path, () => method, () => exampleKey],\n ([newPath, newMethod, newExampleKey]) => {\n const cached = responseCache.get(\n getOperationExampleKey(newMethod, newPath, newExampleKey),\n )\n\n if (cached) {\n response.value = cached.response\n requestPayload.value = cached.requestPayload\n } else {\n // History is keyed only by document/path/method but each entry carries\n // the example it came from, so we walk from the end and pick the most\n // recent entry that matches the active example. Otherwise a cache miss\n // on example B would restore example A's response. Only runs on a\n // cache miss — the common case (in-session navigation) skips it.\n const latest = (() => {\n for (let i = history.length - 1; i >= 0; i--) {\n const entry = history[i]\n if (entry?.meta.example === newExampleKey) {\n return entry\n }\n }\n return undefined\n })()\n\n if (latest) {\n response.value = harToFetchResponse({\n harResponse: latest.response,\n url: latest.request.url,\n method: newMethod,\n path: newPath,\n duration: latest.time,\n })\n requestPayload.value = null\n } else {\n response.value = null\n requestPayload.value = null\n }\n }\n\n // Cancel any in-flight request\n cancelRequest()\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n // We cancel the request if the component is unmounted\n cancelRequest()\n})\n</script>\n<template>\n <div class=\"bg-b-1 flex h-full flex-col\">\n <div\n class=\"lg:min-h-header flex w-full flex-wrap items-center justify-center p-2 lg:p-0\">\n <!-- Address Bar -->\n <Header\n :activeEnvironment\n :documentSlug\n :documentUrl\n :environment\n :environments\n :eventBus\n :exampleKey\n :hideClientButton\n :history=\"operationHistory\"\n :integration\n :layout\n :method\n :path\n :server\n :serverMeta\n :servers\n :source\n @execute=\"handleExecute\"\n @navigate:settings=\"handleNavigateSettings\"\n @select:history:item=\"handleSelectHistoryItem\" />\n </div>\n\n <ViewLayout class=\"border-t\">\n <ViewLayoutContent class=\"flex-1\">\n <!-- Request Section -->\n <RequestBlock\n :authMeta\n :clientOptions\n :defaultHeaders\n :documentCookies\n :environment\n :eventBus\n :exampleKey\n :layout\n :method\n :operation\n :options=\"toValue(options)\"\n :path\n :plugins\n :proxyUrl\n :requestBodyCompositionSelection\n :securityRequirements\n :securitySchemes\n :selectedClient\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :layout\n :plugins\n :requestPayload\n :response\n :totalPerformedRequests=\"operationHistory.length\" />\n </ViewLayoutContent>\n </ViewLayout>\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"OperationBlock.vue.js","names":[],"sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * OperationBlock\n *\n * Orchestrates the operation view by wiring together the Header, OperationBlock,\n * and ResponseBlock. Forwards user interactions to the workspace event bus and\n * passes through configuration such as auth, servers, plugins, and environment.\n * This component keeps the Operation page lean by centralizing event emission\n * and prop wiring between the blocks.\n *\n * Notable behavior:\n * - Uses operation['x-scalar-method'] and operation['x-scalar-path'] to provide\n * draft overrides for the UI when present.\n */\nexport default {\n name: 'OperationBlock',\n}\n\nexport type OperationBlockProps = {\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Application version */\n appVersion: string\n /** Openapi document */\n document: OpenApiDocument\n /** Openapi document slug */\n documentSlug: string\n /** Workspace cookies */\n workspaceCookies: XScalarCookie[]\n /** Document cookies */\n documentCookies: XScalarCookie[]\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** Whether `path` identifies an OpenAPI webhook. */\n isWebhook?: boolean\n /** HTTP clients */\n httpClients: AvailableClients\n /** The history for the operation */\n history?: HistoryEntry[]\n /** Client layout */\n layout: ClientLayout\n /** Currently selected server */\n server: ServerObject | null\n /** Currently selected client */\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** Meta information for the server */\n serverMeta: ServerMeta\n /** Hides the client button on the header */\n hideClientButton?: boolean\n /** Client integration */\n integration?: string | null\n /** Openapi document url for `modal` mode to open the client app */\n documentUrl?: string\n /** Client source */\n source?: 'gitbook' | 'api-reference'\n /** Operation object */\n operation: OperationObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Meta information for the auth update */\n authMeta: AuthMeta\n /** Document defined security schemes */\n securitySchemes: MergedSecuritySchemes\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Environment list */\n environments?: string[]\n /** Currently selected environment */\n activeEnvironment?: string\n /** For environment variables in the inputs */\n environment: XScalarEnvironment\n /** The proxy URL for sending requests */\n proxyUrl: string\n /** Currently selected security */\n selectedSecurity: SelectedSecurity\n /** Currently selected security schemes */\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n /** Security requirements */\n securityRequirements: OpenApiDocument['security']\n /** Default headers */\n defaultHeaders: Record<string, string>\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /** Subset of config options for the modal */\n options?: ModalProps['options']\n}\n</script>\n<script setup lang=\"ts\">\nimport { generateClientOptions } from '@scalar/blocks/code-example'\nimport { ERRORS } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport { buildSafeBodyRequest } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { executeHook, type ClientPlugin } from '@scalar/oas-utils/helpers'\nimport {\n AVAILABLE_CLIENTS,\n type AvailableClients,\n} from '@scalar/types/snippetz'\nimport { useClipboard } from '@scalar/use-hooks/useClipboard'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type { HistoryEntry } from '@scalar/workspace-store/entities/history/schema'\nimport type {\n AuthMeta,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n buildRequest,\n createVariablesStoreForRequest,\n getEnvironmentVariables,\n requestFactory,\n resolveExecutableRequestUrl,\n type MergedSecuritySchemes,\n type RequestPayload,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/operation'\nimport { computed, onBeforeUnmount, onMounted, ref, toValue, watch } from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport { harToFetchRequest } from '@/v2/blocks/operation-block/helpers/har-to-fetch-request'\nimport { harToFetchResponse } from '@/v2/blocks/operation-block/helpers/har-to-fetch-response'\nimport {\n getCookieRequestUrl,\n getResponseCookieActions,\n} from '@/v2/blocks/operation-block/helpers/persist-response-cookies'\nimport { getEnvironmentPersistenceActions } from '@/v2/blocks/operation-block/helpers/persist-script-environment'\nimport {\n getOperationExampleKey,\n isStreamingResponse,\n responseCache,\n} from '@/v2/blocks/operation-block/helpers/response-cache'\nimport {\n sendRequest,\n type ResponseInstance,\n} from '@/v2/blocks/operation-block/helpers/send-request'\nimport { validatePathParameters } from '@/v2/blocks/operation-block/helpers/validate-path-parameters'\nimport { RequestBlock } from '@/v2/blocks/request-block'\nimport { ResponseBlock } from '@/v2/blocks/response-block'\nimport { type History } from '@/v2/blocks/scalar-address-bar-block'\nimport type { ModalProps } from '@/v2/features/modal/Modal.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport Header from './components/Header.vue'\n\nconst {\n authMeta,\n environment,\n eventBus,\n exampleKey,\n document,\n documentSlug,\n workspaceCookies = [],\n documentCookies = [],\n hideClientButton,\n httpClients = AVAILABLE_CLIENTS,\n history = [],\n layout,\n method,\n isWebhook = false,\n operation,\n path,\n plugins = [],\n proxyUrl,\n requestBodyCompositionSelection,\n securitySchemes,\n selectedClient,\n server,\n servers,\n environments,\n options,\n activeEnvironment,\n serverMeta,\n selectedSecurity,\n selectedSecuritySchemes,\n securityRequirements,\n defaultHeaders,\n} = defineProps<OperationBlockProps>()\n\n/** Hoist up client generation so it doesn't get re-generated on every operation */\nconst clientOptions = computed(() => generateClientOptions(httpClients))\n\nconst { toast } = useToasts()\nconst { copyToClipboard } = useClipboard()\n\n// Refs\nconst abortController = ref<AbortController | null>(null)\nconst response = ref<ResponseInstance | null>(null)\nconst requestPayload = ref<RequestPayload | null>(null)\n\n/**\n * A webhook has no OpenAPI server, so its destination is the full URL the user\n * enters at runtime. The field holds that whole URL (not a server-relative path),\n * and the request is sent to it directly with no separate server base.\n */\nconst webhookUrl = ref('')\n\nwatch(\n [() => path, () => method, () => isWebhook],\n () => {\n webhookUrl.value = ''\n },\n { immediate: true },\n)\n\nconst requestPath = computed(() => (isWebhook ? webhookUrl.value : path))\nconst requestServer = computed<ServerObject | null>(() =>\n isWebhook ? null : server,\n)\n\n/** Cancel the request */\nconst cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED)\n\n/**\n * Persist server-set cookies from a response into the document cookie jar.\n *\n * Scalar keeps its own cookie jar because browsers hide `Set-Cookie` from `fetch`.\n * Mirroring browser behavior, cookies are stored scoped to the request host and\n * path and removed when the server expires them, so a value like a Django CSRF\n * token stays available for later PUT/PATCH requests after a page reload.\n */\nconst persistResponseCookies = (sendResult: {\n response: ResponseInstance\n requestPayload: RequestPayload\n}) => {\n const actions = getResponseCookieActions({\n cookieHeaderKeys: sendResult.response.cookieHeaderKeys ?? [],\n documentCookies,\n requestUrl: getCookieRequestUrl(String(sendResult.requestPayload[0])),\n })\n\n for (const action of actions) {\n if (action.type === 'delete') {\n eventBus.emit('cookie:delete:cookie', {\n collectionType: 'document',\n cookieName: action.cookieName,\n index: action.index,\n })\n continue\n }\n\n eventBus.emit('cookie:upsert:cookie', {\n collectionType: 'document',\n payload: action.cookie,\n ...(action.index === undefined ? {} : { index: action.index }),\n })\n }\n}\n\n/**\n * Copy the executable URL — same pipeline as Send (`requestFactory` +\n * `resolveExecutableRequestUrl`), including security query params.\n */\nconst copyAddressBarUrl = async (): Promise<void> => {\n const { request } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies: [...workspaceCookies, ...documentCookies],\n method,\n operation,\n path: requestPath.value,\n proxyUrl,\n server: requestServer.value,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n await copyToClipboard(\n resolveExecutableRequestUrl(request, getEnvironmentVariables(environment)),\n )\n}\n\n/** Execute the current operation example */\nconst handleExecute = async () => {\n eventBus.flushDebouncedEmits?.()\n\n if (isWebhook && !requestPath.value.trim()) {\n toast('Webhook URL required. Enter a destination first.', 'error')\n return\n }\n\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast('Path parameters must have values.', 'error')\n return\n }\n\n const globalCookies = [...workspaceCookies, ...documentCookies]\n\n const { request: requestBuilder } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies,\n method,\n operation,\n path: requestPath.value,\n proxyUrl,\n server: requestServer.value,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n // Stop any previous streaming response\n if (response.value && 'reader' in response.value) {\n response.value.reader.cancel()\n }\n\n const variablesStore = createVariablesStoreForRequest()\n\n // Seed the script variable store with the active environment so pm.environment.get()\n // and pm.variables.get() resolve to the same values as {{…}} placeholders. Without\n // seeding, the store starts empty and every scripted read returns undefined.\n const seededEnvironment = getEnvironmentVariables(environment)\n variablesStore.setEnvironment?.(\n Object.entries(seededEnvironment).map(([key, value]) => ({ key, value })),\n )\n\n // Snapshot the persistence target at request start, alongside the seed. Persistence runs after\n // the request completes (awaits below), and the environment props are reactive — if the active\n // environment is switched mid-flight, or a concurrent request splices these arrays, reading\n // them at persist time would apply the script's writes to the wrong environment or resolve\n // workspace/document indexes against a shifted list. Copy the arrays (not just the reference)\n // so the seeded values and these targets stay fixed to the same point in time.\n const persistenceTarget = activeEnvironment\n ? {\n environmentName: activeEnvironment,\n mergedVariables: [...environment.variables],\n documentVariables: [\n ...(document['x-scalar-environments']?.[activeEnvironment]\n ?.variables ?? []),\n ],\n environmentExistsOnDocument: Boolean(\n document['x-scalar-environments']?.[activeEnvironment],\n ),\n }\n : null\n\n // Execute the beforeRequest hook (plugins receive RequestFactory, not fetch Request)\n await executeHook(\n {\n requestBuilder,\n document,\n operation,\n variablesStore,\n server: requestServer.value,\n customFetch: toValue(options)?.customFetch,\n },\n 'beforeRequest',\n plugins,\n )\n\n // Persist environment variables set by scripts (pm.environment.set / unset) back to the active\n // environment so they survive to the next request, mirroring the Environment tab. Scope and\n // index are resolved from the seed-time environment so updates mutate in place. Defined here so\n // it can run on every exit path after the pre-request scripts — including a build failure or a\n // failed send — so a pre-request set, like a token stored before a flaky call, is not lost.\n const persistScriptEnvironment = () => {\n if (!persistenceTarget) {\n return\n }\n\n const environmentActions = getEnvironmentPersistenceActions({\n ...persistenceTarget,\n seededVariables: seededEnvironment,\n scriptVariables: variablesStore.getEnvironment(),\n })\n\n for (const action of environmentActions) {\n if (action.type === 'delete') {\n eventBus.emit('environment:delete:environment-variable', {\n environmentName: action.environmentName,\n index: action.index,\n collectionType: action.collectionType,\n })\n } else {\n eventBus.emit('environment:upsert:environment-variable', {\n environmentName: action.environmentName,\n variable: action.variable,\n index: action.index,\n collectionType: action.collectionType,\n })\n }\n }\n }\n\n // Resolve {{…}} placeholders from the script store so pre-request pm.environment.set() and\n // unset() take effect on this request. The store was seeded from the active environment, so\n // it already holds every value spreading seededEnvironment would add — minus the keys a script\n // unset, which must stay dropped (an object spread cannot remove them again).\n const envVariables = variablesStore.getVariables()\n\n // Build the fetch Request after hooks may have mutated the factory\n const built = buildRequest(requestBuilder, {\n envVariables,\n allowMissingRequestServerBase: layout === 'modal',\n })\n if (!built.ok) {\n // Save any pre-request script writes before bailing out on a build failure.\n persistScriptEnvironment()\n toast(built.message ?? built.error, 'error')\n return\n }\n\n // Store the abort controller for cancellation\n abortController.value = built.data.controller\n\n // Build the fetch Request once so the requestBuilt hook observes the exact bytes that are\n // sent. Rebuilding it later would generate a different multipart boundary, which breaks\n // plugins that hash the body (for example, request signing).\n const request = buildSafeBodyRequest(...built.data.requestPayload)\n\n // Execute the requestBuilt hook (plugins receive the exact fetch Request that will be sent)\n await executeHook(\n {\n request,\n requestBuilder,\n document,\n operation,\n variablesStore,\n },\n 'requestBuilt',\n plugins,\n )\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:sent', {\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n /** Execute the request */\n const [sendError, sendResult] = await sendRequest({\n isUsingProxy: built.data.isUsingProxy,\n requestPayload: built.data.requestPayload,\n request,\n plugins,\n customFetch: toValue(options)?.customFetch,\n })\n\n if (sendResult) {\n // Execute the responseReceived hook\n await executeHook(\n {\n response: sendResult.originalResponse.clone(),\n requestBuilder,\n request: buildSafeBodyRequest(...sendResult.requestPayload),\n document,\n operation,\n variablesStore,\n },\n 'responseReceived',\n plugins,\n )\n }\n\n // Save script environment writes (pre-request and, on success, post-response) back to the\n // active environment. Runs even when the send fails so a pre-request set is not lost.\n persistScriptEnvironment()\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:complete', {\n payload: sendResult\n ? {\n response: sendResult.originalResponse.clone(),\n requestPayload: sendResult.requestPayload,\n duration: sendResult.response.duration,\n timestamp: sendResult.timestamp,\n }\n : undefined,\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n if (sendError) {\n // clean up the response and request\n response.value = null\n requestPayload.value = null\n abortController.value = null\n\n toast(sendError.message, 'error')\n return\n }\n\n // Store the response\n response.value = sendResult.response\n requestPayload.value = sendResult.requestPayload\n\n // Persist server-set cookies into the document jar so values like a CSRF token\n // survive a page reload and get replayed on the next request, like a browser would.\n persistResponseCookies(sendResult)\n\n // Cache non-streaming responses so they can be restored when navigating back\n if (!isStreamingResponse(sendResult.response)) {\n responseCache.set(getOperationExampleKey(method, path, exampleKey), {\n response: sendResult.response,\n requestPayload: sendResult.requestPayload,\n })\n }\n}\n\nonMounted(() => {\n eventBus.on('operation:send:request:hotkey', handleExecute)\n eventBus.on('operation:cancel:request', cancelRequest)\n eventBus.on('copy-url:address-bar', copyAddressBarUrl)\n\n // Let plugins warm up per-operation resources (e.g. the scripts sandbox) so the first request\n // does not pay their cold-start cost.\n void executeHook({ document, operation }, 'onRequestMount', plugins)\n})\nonBeforeUnmount(() => {\n eventBus.off('operation:send:request:hotkey', handleExecute)\n eventBus.off('operation:cancel:request', cancelRequest)\n eventBus.off('copy-url:address-bar', copyAddressBarUrl)\n})\n\nconst operationHistory = computed<History[]>(() =>\n history\n .map((entry) => ({\n method: entry.request.method as HttpMethodType,\n path: entry.request.url,\n duration: entry.time,\n status: entry.response.status,\n }))\n .reverse(),\n)\n\nconst handleSelectHistoryItem = ({ index }: { index: number }) => {\n const transformedIndex = (history.length ?? 0) - index - 1\n const historyItem = history[transformedIndex]\n if (!historyItem) {\n return\n }\n\n const navigate = () =>\n eventBus.emit('ui:navigate', {\n page: 'example',\n method,\n path,\n exampleName: 'draft',\n callback: (status) => {\n // Do not replace the response if the navigation was not successful\n if (status !== 'success') {\n return\n }\n // Reconstruct the response\n const fetchResponse = harToFetchResponse({\n harResponse: historyItem.response,\n url: historyItem.request.url,\n method,\n path,\n duration: historyItem.time,\n })\n\n // Reconstruct the request\n const fetchRequest = harToFetchRequest({\n harRequest: historyItem.request,\n })\n\n // Update the response and request\n response.value = fetchResponse\n requestPayload.value = fetchRequest\n },\n })\n\n eventBus.emit('operation:reload:history', {\n meta: {\n path,\n method,\n },\n index: transformedIndex,\n callback: navigate,\n })\n}\n\nconst handleNavigateSettings = () => {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'overview',\n operationPath: path,\n method,\n })\n}\n\n/**\n * When the path, method, or example key changes, restore the response panel\n * for the new operation from the best available source:\n *\n * 1. `responseCache` — in-memory, populated on every successful send.\n * Has the live response object (including streams) so it wins.\n * 2. `history` — persisted in the workspace store. Used as a fallback when\n * the in-memory cache is empty (page reload, fresh app session, etc.)\n * so navigating to an operation that has been called before shows the\n * last response instead of an empty panel.\n * 3. Otherwise clear.\n *\n * Only the response is restored from history — the user's request inputs are\n * left alone, so we never silently overwrite an in-progress draft.\n */\nwatch(\n [() => path, () => method, () => exampleKey],\n ([newPath, newMethod, newExampleKey]) => {\n const cached = responseCache.get(\n getOperationExampleKey(newMethod, newPath, newExampleKey),\n )\n\n if (cached) {\n response.value = cached.response\n requestPayload.value = cached.requestPayload\n } else {\n // History is keyed only by document/path/method but each entry carries\n // the example it came from, so we walk from the end and pick the most\n // recent entry that matches the active example. Otherwise a cache miss\n // on example B would restore example A's response. Only runs on a\n // cache miss — the common case (in-session navigation) skips it.\n const latest = (() => {\n for (let i = history.length - 1; i >= 0; i--) {\n const entry = history[i]\n if (entry?.meta.example === newExampleKey) {\n return entry\n }\n }\n return undefined\n })()\n\n if (latest) {\n response.value = harToFetchResponse({\n harResponse: latest.response,\n url: latest.request.url,\n method: newMethod,\n path: newPath,\n duration: latest.time,\n })\n requestPayload.value = null\n } else {\n response.value = null\n requestPayload.value = null\n }\n }\n\n // Cancel any in-flight request\n cancelRequest()\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n // We cancel the request if the component is unmounted\n cancelRequest()\n})\n</script>\n<template>\n <div class=\"bg-b-1 flex h-full flex-col\">\n <div\n class=\"lg:min-h-header flex w-full flex-wrap items-center justify-center p-2 lg:p-0\">\n <!-- Address Bar -->\n <Header\n :activeEnvironment\n :documentSlug\n :documentUrl\n :environment\n :environments\n :eventBus\n :exampleKey\n :hideClientButton\n :history=\"operationHistory\"\n :integration\n :isWebhook\n :layout\n :method\n :path=\"requestPath\"\n :server=\"requestServer\"\n :serverMeta\n :servers\n :source\n @execute=\"handleExecute\"\n @navigate:settings=\"handleNavigateSettings\"\n @select:history:item=\"handleSelectHistoryItem\"\n @update:webhook-url=\"(value) => (webhookUrl = value)\" />\n </div>\n\n <ViewLayout class=\"border-t\">\n <ViewLayoutContent class=\"flex-1\">\n <!-- Request Section -->\n <RequestBlock\n :authMeta\n :clientOptions\n :defaultHeaders\n :defaultRequestBodyView=\"\n document['x-scalar-default-request-body-view']\n \"\n :documentCookies\n :environment\n :eventBus\n :exampleKey\n :layout\n :method\n :operation\n :options=\"toValue(options)\"\n :path=\"requestPath\"\n :plugins\n :proxyUrl\n :requestBodyCompositionSelection\n :securityRequirements\n :securitySchemes\n :selectedClient\n :selectedSecurity\n :selectedSecuritySchemes\n :server=\"requestServer\"\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :layout\n :plugins\n :requestPayload\n :response\n :totalPerformedRequests=\"operationHistory.length\" />\n </ViewLayoutContent>\n </ViewLayout>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -34,6 +34,10 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
34
34
|
documentCookies: { default: () => [] },
|
|
35
35
|
path: {},
|
|
36
36
|
method: {},
|
|
37
|
+
isWebhook: {
|
|
38
|
+
type: Boolean,
|
|
39
|
+
default: false
|
|
40
|
+
},
|
|
37
41
|
httpClients: { default: () => AVAILABLE_CLIENTS },
|
|
38
42
|
history: { default: () => [] },
|
|
39
43
|
layout: {},
|
|
@@ -69,6 +73,21 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
69
73
|
const abortController = ref(null);
|
|
70
74
|
const response = ref(null);
|
|
71
75
|
const requestPayload = ref(null);
|
|
76
|
+
/**
|
|
77
|
+
* A webhook has no OpenAPI server, so its destination is the full URL the user
|
|
78
|
+
* enters at runtime. The field holds that whole URL (not a server-relative path),
|
|
79
|
+
* and the request is sent to it directly with no separate server base.
|
|
80
|
+
*/
|
|
81
|
+
const webhookUrl = ref("");
|
|
82
|
+
watch([
|
|
83
|
+
() => __props.path,
|
|
84
|
+
() => __props.method,
|
|
85
|
+
() => __props.isWebhook
|
|
86
|
+
], () => {
|
|
87
|
+
webhookUrl.value = "";
|
|
88
|
+
}, { immediate: true });
|
|
89
|
+
const requestPath = computed(() => __props.isWebhook ? webhookUrl.value : __props.path);
|
|
90
|
+
const requestServer = computed(() => __props.isWebhook ? null : __props.server);
|
|
72
91
|
/** Cancel the request */
|
|
73
92
|
const cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED);
|
|
74
93
|
/**
|
|
@@ -113,9 +132,9 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
113
132
|
globalCookies: [...__props.workspaceCookies, ...__props.documentCookies],
|
|
114
133
|
method: __props.method,
|
|
115
134
|
operation: __props.operation,
|
|
116
|
-
path:
|
|
135
|
+
path: requestPath.value,
|
|
117
136
|
proxyUrl: __props.proxyUrl,
|
|
118
|
-
server:
|
|
137
|
+
server: requestServer.value,
|
|
119
138
|
selectedSecuritySchemes: __props.selectedSecuritySchemes,
|
|
120
139
|
isElectron: isElectron(),
|
|
121
140
|
requestBodyCompositionSelection: __props.requestBodyCompositionSelection
|
|
@@ -125,6 +144,10 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
125
144
|
/** Execute the current operation example */
|
|
126
145
|
const handleExecute = async () => {
|
|
127
146
|
__props.eventBus.flushDebouncedEmits?.();
|
|
147
|
+
if (__props.isWebhook && !requestPath.value.trim()) {
|
|
148
|
+
toast("Webhook URL required. Enter a destination first.", "error");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
128
151
|
if (validatePathParameters(__props.operation.parameters ?? [], __props.exampleKey).ok === false) {
|
|
129
152
|
toast("Path parameters must have values.", "error");
|
|
130
153
|
return;
|
|
@@ -137,9 +160,9 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
137
160
|
globalCookies,
|
|
138
161
|
method: __props.method,
|
|
139
162
|
operation: __props.operation,
|
|
140
|
-
path:
|
|
163
|
+
path: requestPath.value,
|
|
141
164
|
proxyUrl: __props.proxyUrl,
|
|
142
|
-
server:
|
|
165
|
+
server: requestServer.value,
|
|
143
166
|
selectedSecuritySchemes: __props.selectedSecuritySchemes,
|
|
144
167
|
isElectron: isElectron(),
|
|
145
168
|
requestBodyCompositionSelection: __props.requestBodyCompositionSelection
|
|
@@ -162,7 +185,7 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
162
185
|
document: __props.document,
|
|
163
186
|
operation: __props.operation,
|
|
164
187
|
variablesStore,
|
|
165
|
-
server:
|
|
188
|
+
server: requestServer.value,
|
|
166
189
|
customFetch: toValue(__props.options)?.customFetch
|
|
167
190
|
}, "beforeRequest", __props.plugins);
|
|
168
191
|
const persistScriptEnvironment = () => {
|
|
@@ -374,16 +397,18 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
374
397
|
hideClientButton: __props.hideClientButton,
|
|
375
398
|
history: operationHistory.value,
|
|
376
399
|
integration: __props.integration,
|
|
400
|
+
isWebhook: __props.isWebhook,
|
|
377
401
|
layout: __props.layout,
|
|
378
402
|
method: __props.method,
|
|
379
|
-
path:
|
|
380
|
-
server:
|
|
403
|
+
path: requestPath.value,
|
|
404
|
+
server: requestServer.value,
|
|
381
405
|
serverMeta: __props.serverMeta,
|
|
382
406
|
servers: __props.servers,
|
|
383
407
|
source: __props.source,
|
|
384
408
|
onExecute: handleExecute,
|
|
385
409
|
"onNavigate:settings": handleNavigateSettings,
|
|
386
|
-
"onSelect:history:item": handleSelectHistoryItem
|
|
410
|
+
"onSelect:history:item": handleSelectHistoryItem,
|
|
411
|
+
"onUpdate:webhookUrl": _cache[0] || (_cache[0] = (value) => webhookUrl.value = value)
|
|
387
412
|
}, null, 8, [
|
|
388
413
|
"activeEnvironment",
|
|
389
414
|
"documentSlug",
|
|
@@ -395,6 +420,7 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
395
420
|
"hideClientButton",
|
|
396
421
|
"history",
|
|
397
422
|
"integration",
|
|
423
|
+
"isWebhook",
|
|
398
424
|
"layout",
|
|
399
425
|
"method",
|
|
400
426
|
"path",
|
|
@@ -408,6 +434,7 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
408
434
|
authMeta: __props.authMeta,
|
|
409
435
|
clientOptions: clientOptions.value,
|
|
410
436
|
defaultHeaders: __props.defaultHeaders,
|
|
437
|
+
defaultRequestBodyView: __props.document["x-scalar-default-request-body-view"],
|
|
411
438
|
documentCookies: __props.documentCookies,
|
|
412
439
|
environment: __props.environment,
|
|
413
440
|
eventBus: __props.eventBus,
|
|
@@ -416,7 +443,7 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
416
443
|
method: __props.method,
|
|
417
444
|
operation: __props.operation,
|
|
418
445
|
options: toValue(__props.options),
|
|
419
|
-
path:
|
|
446
|
+
path: requestPath.value,
|
|
420
447
|
plugins: __props.plugins,
|
|
421
448
|
proxyUrl: __props.proxyUrl,
|
|
422
449
|
requestBodyCompositionSelection: __props.requestBodyCompositionSelection,
|
|
@@ -425,12 +452,13 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
|
|
|
425
452
|
selectedClient: __props.selectedClient,
|
|
426
453
|
selectedSecurity: __props.selectedSecurity,
|
|
427
454
|
selectedSecuritySchemes: __props.selectedSecuritySchemes,
|
|
428
|
-
server:
|
|
455
|
+
server: requestServer.value,
|
|
429
456
|
workspaceCookies: __props.workspaceCookies
|
|
430
457
|
}, null, 8, [
|
|
431
458
|
"authMeta",
|
|
432
459
|
"clientOptions",
|
|
433
460
|
"defaultHeaders",
|
|
461
|
+
"defaultRequestBodyView",
|
|
434
462
|
"documentCookies",
|
|
435
463
|
"environment",
|
|
436
464
|
"eventBus",
|