@scalar/api-client 3.20.0 → 3.21.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/components/HttpMethod/HttpMethod.vue.d.ts +2 -2
  3. package/dist/components/HttpMethod/HttpMethod.vue.d.ts.map +1 -1
  4. package/dist/components/HttpMethod/HttpMethod.vue.js +1 -1
  5. package/dist/components/HttpMethod/HttpMethod.vue.js.map +1 -1
  6. package/dist/components/HttpMethod/HttpMethod.vue.script.js +15 -7
  7. package/dist/components/HttpMethod/HttpMethod.vue.script.js.map +1 -1
  8. package/dist/style.css +31 -32
  9. package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts +1 -2
  10. package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
  11. package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
  12. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +7 -0
  13. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
  14. package/dist/v2/blocks/operation-block/components/Header.vue.d.ts +1 -2
  15. package/dist/v2/blocks/operation-block/components/Header.vue.d.ts.map +1 -1
  16. package/dist/v2/blocks/operation-block/components/Header.vue.js +1 -1
  17. package/dist/v2/blocks/operation-block/components/Header.vue.js.map +1 -1
  18. package/dist/v2/blocks/operation-block/components/Header.vue.script.js.map +1 -1
  19. package/dist/v2/blocks/operation-block/components/OpenApiClientButton.vue.d.ts.map +1 -1
  20. package/dist/v2/blocks/operation-block/components/OpenApiClientButton.vue.js +1 -1
  21. package/dist/v2/blocks/operation-block/components/OpenApiClientButton.vue.js.map +1 -1
  22. package/dist/v2/blocks/operation-block/components/OpenApiClientButton.vue.script.js +2 -1
  23. package/dist/v2/blocks/operation-block/components/OpenApiClientButton.vue.script.js.map +1 -1
  24. package/dist/v2/blocks/operation-block/helpers/har-to-fetch-response.d.ts +1 -2
  25. package/dist/v2/blocks/operation-block/helpers/har-to-fetch-response.d.ts.map +1 -1
  26. package/dist/v2/blocks/operation-block/helpers/har-to-fetch-response.js.map +1 -1
  27. package/dist/v2/blocks/operation-block/helpers/send-request.d.ts +1 -2
  28. package/dist/v2/blocks/operation-block/helpers/send-request.d.ts.map +1 -1
  29. package/dist/v2/blocks/operation-block/helpers/send-request.js.map +1 -1
  30. package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts +1 -2
  31. package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts.map +1 -1
  32. package/dist/v2/blocks/request-block/RequestBlock.vue.js +1 -1
  33. package/dist/v2/blocks/request-block/RequestBlock.vue.js.map +1 -1
  34. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js +10 -2
  35. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
  36. package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts.map +1 -1
  37. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.d.ts.map +1 -1
  38. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.js.map +1 -1
  39. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js +10 -3
  40. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js.map +1 -1
  41. package/dist/v2/blocks/request-block/components/RequestParams.vue.d.ts +2 -0
  42. package/dist/v2/blocks/request-block/components/RequestParams.vue.d.ts.map +1 -1
  43. package/dist/v2/blocks/request-block/components/RequestParams.vue.js.map +1 -1
  44. package/dist/v2/blocks/request-block/components/RequestParams.vue.script.js +7 -2
  45. package/dist/v2/blocks/request-block/components/RequestParams.vue.script.js.map +1 -1
  46. package/dist/v2/blocks/request-block/components/RequestTable.vue.d.ts +4 -4
  47. package/dist/v2/blocks/request-block/components/RequestTableRow.vue.d.ts +4 -4
  48. package/dist/v2/blocks/request-block/helpers/create-parameter-rows.d.ts.map +1 -1
  49. package/dist/v2/blocks/request-block/helpers/create-parameter-rows.js +14 -6
  50. package/dist/v2/blocks/request-block/helpers/create-parameter-rows.js.map +1 -1
  51. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts +2 -3
  52. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts.map +1 -1
  53. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js +1 -1
  54. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js.map +1 -1
  55. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js +2 -2
  56. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js.map +1 -1
  57. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.d.ts +1 -2
  58. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.d.ts.map +1 -1
  59. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.js +1 -1
  60. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.js.map +1 -1
  61. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.script.js.map +1 -1
  62. package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
  63. package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.d.ts.map +1 -1
  64. package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.js.map +1 -1
  65. package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.script.js +7 -6
  66. package/dist/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue.script.js.map +1 -1
  67. package/dist/v2/components/data-table/DataTable.vue.d.ts.map +1 -1
  68. package/dist/v2/components/data-table/DataTable.vue.js.map +1 -1
  69. package/dist/v2/components/data-table/DataTable.vue.script.js +2 -1
  70. package/dist/v2/components/data-table/DataTable.vue.script.js.map +1 -1
  71. package/dist/v2/constants.js +1 -1
  72. package/dist/v2/features/localization/locales/ar.d.ts +4 -0
  73. package/dist/v2/features/localization/locales/ar.d.ts.map +1 -1
  74. package/dist/v2/features/localization/locales/ar.js +8 -2
  75. package/dist/v2/features/localization/locales/ar.js.map +1 -1
  76. package/dist/v2/features/localization/locales/de.d.ts +4 -0
  77. package/dist/v2/features/localization/locales/de.d.ts.map +1 -1
  78. package/dist/v2/features/localization/locales/de.js +8 -2
  79. package/dist/v2/features/localization/locales/de.js.map +1 -1
  80. package/dist/v2/features/localization/locales/es.d.ts +4 -0
  81. package/dist/v2/features/localization/locales/es.d.ts.map +1 -1
  82. package/dist/v2/features/localization/locales/es.js +8 -2
  83. package/dist/v2/features/localization/locales/es.js.map +1 -1
  84. package/dist/v2/features/localization/locales/fr.d.ts +4 -0
  85. package/dist/v2/features/localization/locales/fr.d.ts.map +1 -1
  86. package/dist/v2/features/localization/locales/fr.js +8 -2
  87. package/dist/v2/features/localization/locales/fr.js.map +1 -1
  88. package/dist/v2/features/localization/locales/pt.d.ts +4 -0
  89. package/dist/v2/features/localization/locales/pt.d.ts.map +1 -1
  90. package/dist/v2/features/localization/locales/pt.js +8 -2
  91. package/dist/v2/features/localization/locales/pt.js.map +1 -1
  92. package/dist/v2/features/localization/locales/ru.d.ts +4 -0
  93. package/dist/v2/features/localization/locales/ru.d.ts.map +1 -1
  94. package/dist/v2/features/localization/locales/ru.js +8 -2
  95. package/dist/v2/features/localization/locales/ru.js.map +1 -1
  96. package/dist/v2/features/localization/locales/zh-cn.d.ts +4 -0
  97. package/dist/v2/features/localization/locales/zh-cn.d.ts.map +1 -1
  98. package/dist/v2/features/localization/locales/zh-cn.js +8 -2
  99. package/dist/v2/features/localization/locales/zh-cn.js.map +1 -1
  100. package/dist/v2/features/localization/translations.d.ts +4 -0
  101. package/dist/v2/features/localization/translations.d.ts.map +1 -1
  102. package/dist/v2/features/localization/translations.js +8 -2
  103. package/dist/v2/features/localization/translations.js.map +1 -1
  104. package/dist/v2/features/modal/Modal.vue.d.ts +1 -2
  105. package/dist/v2/features/modal/Modal.vue.d.ts.map +1 -1
  106. package/dist/v2/features/modal/Modal.vue.js.map +1 -1
  107. package/dist/v2/features/modal/Modal.vue.script.js.map +1 -1
  108. package/dist/v2/features/modal/helpers/resolve-route-parameters.d.ts +2 -3
  109. package/dist/v2/features/modal/helpers/resolve-route-parameters.d.ts.map +1 -1
  110. package/dist/v2/features/modal/helpers/resolve-route-parameters.js +8 -7
  111. package/dist/v2/features/modal/helpers/resolve-route-parameters.js.map +1 -1
  112. package/dist/v2/features/modal/hooks/use-modal-sidebar.d.ts +2 -3
  113. package/dist/v2/features/modal/hooks/use-modal-sidebar.d.ts.map +1 -1
  114. package/dist/v2/features/modal/hooks/use-modal-sidebar.js.map +1 -1
  115. package/dist/v2/features/operation/Operation.vue.d.ts +1 -2
  116. package/dist/v2/features/operation/Operation.vue.d.ts.map +1 -1
  117. package/dist/v2/features/operation/Operation.vue.js.map +1 -1
  118. package/dist/v2/features/operation/Operation.vue.script.js.map +1 -1
  119. package/dist/v2/helpers/generate-location-id.d.ts +1 -2
  120. package/dist/v2/helpers/generate-location-id.d.ts.map +1 -1
  121. package/dist/v2/helpers/generate-location-id.js.map +1 -1
  122. package/dist/vue-styles.css +30 -30
  123. package/package.json +11 -11
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @scalar/api-client
2
2
 
3
+ ## 3.21.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#10189](https://github.com/scalar/scalar/pull/10189): Support OpenAPI 3.2 `in: querystring` parameters, including content-based serialization, editing the entire query string, schema rendering, and generated request URLs.
8
+
9
+ Non-form whole-query content, including JSON delimiters, is percent-encoded in request URLs and code samples. Use an example with `serializedValue` on the parameter itself for URI-ready query content that must retain its existing encoding.
10
+
11
+ Preserve the encoding of named query values when they coexist with whole-query content in generated code samples, while encoding query authentication values once.
12
+
13
+ Explain why the whole-query editor disables adding named parameters. Existing named parameters follow the whole-query value, preserving duplicate keys for the server to interpret.
14
+
15
+ - [#10186](https://github.com/scalar/scalar/pull/10186): Support OpenAPI 3.2 additionalOperations in operation storage, navigation, documentation, callbacks, and the API client. Preserve custom HTTP method spelling when displaying and sending requests and generating code samples.
16
+
17
+ Traversed operation and webhook methods now use the exported `OperationMethod` type, which accepts custom strings while retaining known-method editor completion. Consumers must handle unknown methods; this open type cannot provide exhaustive checking over the fixed HTTP method set. Unknown method presentation uses `colorClass` and `colorVar`, matching known methods. Preserve uppercase and mixed-case additional operation names consistently.
18
+
19
+ ### Patch Changes
20
+
21
+ - [#10186](https://github.com/scalar/scalar/pull/10186): Show a clear error when a request uses a Fetch-forbidden method (CONNECT, TRACE, or TRACK), including methods changed by pre-request scripts, instead of throwing while building the request.
22
+ - [#10186](https://github.com/scalar/scalar/pull/10186): Include the current document-defined custom method in the request method picker and preserve its spelling when selected.
23
+
24
+ Keep QUERY operation chunk names aligned with their references when additional operations are supported.
25
+
26
+ - [#10313](https://github.com/scalar/scalar/pull/10313): Render data table rows inside a `tbody` so server-rendered tables hydrate. The HTML parser inserts a `tbody` around rows that sit directly under a `table`, so markup rendered without one handed the client a `tbody` where the component tree held the row, and the table — an auth form, for example — was discarded and rebuilt after hydration.
27
+ - [#10186](https://github.com/scalar/scalar/pull/10186): Preserve request bodies for extension HTTP methods such as QUERY and PROPFIND in the browser client.
28
+ - [#10322](https://github.com/scalar/scalar/pull/10322): Use OpenAPI 3.2 dataValue and serializedValue examples for named parameters in the editor, outgoing requests, and code samples. Preserve already serialized values without encoding them twice.
29
+
30
+ Optional parameters with `dataValue` or `serializedValue` examples are now enabled by default, matching legacy `value` examples. Explicitly disabled examples remain disabled.
31
+
32
+ Parameter-level `serializedValue` examples remain editable as raw wire text, including parameter names and percent encoding (for example, `term=hello` rather than `hello`). Media-level cookie examples are percent-encoded when sent, matching generated snippets. Generating snippets no longer modifies the input cookies.
33
+
34
+ - [#10189](https://github.com/scalar/scalar/pull/10189): Translate the whole-query editor label and explanation in all supported API Client locales.
35
+ - [#10310](https://github.com/scalar/scalar/pull/10310): Connect SDK code samples to named request body examples using optional example and contentType fields. Keep the example switcher available for static samples and use the selected request example in API client snippets. Display unavailable linked samples as a localized status message in all supported languages.
36
+
3
37
  ## 3.20.0
4
38
 
5
39
  ### Minor Changes
@@ -4,9 +4,9 @@ type __VLS_Props = {
4
4
  isEditable?: boolean;
5
5
  };
6
6
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
7
- change: (value: "head" | "delete" | "query" | "get" | "options" | "patch" | "post" | "put" | "trace") => any;
7
+ change: (value: string) => any;
8
8
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
- onChange?: ((value: "head" | "delete" | "query" | "get" | "options" | "patch" | "post" | "put" | "trace") => any) | undefined;
9
+ onChange?: ((value: string) => any) | undefined;
10
10
  }>, {
11
11
  isSquare: boolean;
12
12
  isEditable: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"HttpMethod.vue.d.ts","sourceRoot":"","sources":["../../../src/components/HttpMethod/HttpMethod.vue"],"names":[],"mappings":"AAyGA,KAAK,WAAW,GAAG;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAC;AAgIJ,QAAA,MAAM,YAAY;;;;;cAnIH,OAAO;gBAEL,OAAO;6EAqItB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"HttpMethod.vue.d.ts","sourceRoot":"","sources":["../../../src/components/HttpMethod/HttpMethod.vue"],"names":[],"mappings":"AAoHA,KAAK,WAAW,GAAG;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAC;AA6IJ,QAAA,MAAM,YAAY;;;;;cAhJH,OAAO;gBAEL,OAAO;6EAkJtB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -2,7 +2,7 @@ import HttpMethod_vue_vue_type_script_setup_true_lang_default from "./HttpMethod
2
2
  /* empty css */
3
3
  import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export-helper.js";
4
4
  //#region src/components/HttpMethod/HttpMethod.vue
5
- var HttpMethod_default = /*#__PURE__*/ _plugin_vue_export_helper_default(HttpMethod_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-86e2b3ea"]]);
5
+ var HttpMethod_default = /*#__PURE__*/ _plugin_vue_export_helper_default(HttpMethod_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e7883636"]]);
6
6
  //#endregion
7
7
  export { HttpMethod_default as default };
8
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"HttpMethod.vue.js","names":[],"sources":["../../../src/components/HttpMethod/HttpMethod.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport {\n getHttpMethodInfo,\n REQUEST_METHODS,\n} from '@scalar/helpers/http/http-info'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n isSquare?: boolean\n method: string\n isEditable?: boolean\n }>(),\n { isSquare: false, isDisable: false, isEditable: false },\n)\n\nconst emit = defineEmits<{\n (e: 'change', value: HttpMethod): void\n}>()\n\nconst method = computed(() => getHttpMethodInfo(props.method))\nconst methodOptions = objectEntries(REQUEST_METHODS).map(([id]) => ({\n id,\n label: id.toUpperCase(),\n color: getHttpMethodInfo(id).colorClass,\n}))\nconst selectedMethod = computed({\n get: () => methodOptions.find(({ id }) => id === props.method),\n set: (opt) => opt?.id && emit('change', opt.id),\n})\n\nconst variants = cva({\n base: 'text-center font-code text-3xs justify-center items-center flex',\n variants: {\n isSquare: {\n true: 'px-2.5 whitespace-nowrap font-bold border-r h-fit m-auto',\n false: 'rounded-full',\n },\n isEditable: {\n true: 'http-bg-gradient rounded-md border',\n false: 'cursor-auto',\n },\n },\n})\n\nconst httpLabel = computed(() => method.value.short)\n</script>\n<template>\n <!-- Dropdown select -->\n <ScalarListbox\n v-if=\"isEditable\"\n v-model=\"selectedMethod\"\n class=\"font-code scalar-client mt-1 text-sm\"\n :options=\"methodOptions\">\n <div\n class=\"h-full\"\n :class=\"{ 'pointer-events-none': !isEditable }\">\n <button\n class=\"relative h-full\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n <span>{{ httpLabel }}</span>\n </button>\n </div>\n </ScalarListbox>\n <!-- Display only -->\n <div\n v-else\n class=\"relative gap-1 whitespace-nowrap\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n {{ method.short }}\n </div>\n</template>\n<style scoped>\n.http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.75), rgba(0, 0, 0, 0.035));\n}\n.http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.035), rgba(255, 255, 255, 0.75));\n}\n.dark-mode .http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.035), rgba(0, 0, 0, 0.15));\n}\n.dark-mode .http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.035));\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"HttpMethod.vue.js","names":[],"sources":["../../../src/components/HttpMethod/HttpMethod.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport {\n getHttpMethodInfo,\n REQUEST_METHODS,\n} from '@scalar/helpers/http/http-info'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n isSquare?: boolean\n method: string\n isEditable?: boolean\n }>(),\n { isSquare: false, isDisable: false, isEditable: false },\n)\n\nconst emit = defineEmits<{\n (e: 'change', value: string): void\n}>()\n\nconst method = computed(() => getHttpMethodInfo(props.method))\nconst methodOptions = computed(() => {\n const options = objectEntries(REQUEST_METHODS).map(([id]) => ({\n id: String(id),\n label: id.toUpperCase(),\n color: getHttpMethodInfo(id).colorClass,\n }))\n\n // Keep document-defined methods selectable without changing their spelling.\n if (!options.some(({ id }) => id === props.method)) {\n options.push({\n id: props.method,\n label: props.method,\n color: method.value.colorClass,\n })\n }\n\n return options\n})\nconst selectedMethod = computed({\n get: () => methodOptions.value.find(({ id }) => id === props.method),\n set: (opt) => opt?.id && emit('change', opt.id),\n})\n\nconst variants = cva({\n base: 'text-center font-code text-3xs justify-center items-center flex',\n variants: {\n isSquare: {\n true: 'px-2.5 whitespace-nowrap font-bold border-r h-fit m-auto',\n false: 'rounded-full',\n },\n isEditable: {\n true: 'http-bg-gradient rounded-md border',\n false: 'cursor-auto',\n },\n },\n})\n\nconst httpLabel = computed(() => method.value.short)\n</script>\n<template>\n <!-- Dropdown select -->\n <ScalarListbox\n v-if=\"isEditable\"\n v-model=\"selectedMethod\"\n class=\"font-code scalar-client mt-1 text-sm\"\n :options=\"methodOptions\">\n <div\n class=\"h-full\"\n :class=\"{ 'pointer-events-none': !isEditable }\">\n <button\n class=\"relative h-full\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n <span>{{ httpLabel }}</span>\n </button>\n </div>\n </ScalarListbox>\n <!-- Display only -->\n <div\n v-else\n class=\"relative gap-1 whitespace-nowrap\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n {{ method.short }}\n </div>\n</template>\n<style scoped>\n.http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.75), rgba(0, 0, 0, 0.035));\n}\n.http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.035), rgba(255, 255, 255, 0.75));\n}\n.dark-mode .http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.035), rgba(0, 0, 0, 0.15));\n}\n.dark-mode .http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.035));\n}\n</style>\n"],"mappings":""}
@@ -22,13 +22,21 @@ var HttpMethod_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
22
22
  const props = __props;
23
23
  const emit = __emit;
24
24
  const method = computed(() => getHttpMethodInfo(props.method));
25
- const methodOptions = objectEntries(REQUEST_METHODS).map(([id]) => ({
26
- id,
27
- label: id.toUpperCase(),
28
- color: getHttpMethodInfo(id).colorClass
29
- }));
25
+ const methodOptions = computed(() => {
26
+ const options = objectEntries(REQUEST_METHODS).map(([id]) => ({
27
+ id: String(id),
28
+ label: id.toUpperCase(),
29
+ color: getHttpMethodInfo(id).colorClass
30
+ }));
31
+ if (!options.some(({ id }) => id === props.method)) options.push({
32
+ id: props.method,
33
+ label: props.method,
34
+ color: method.value.colorClass
35
+ });
36
+ return options;
37
+ });
30
38
  const selectedMethod = computed({
31
- get: () => methodOptions.find(({ id }) => id === props.method),
39
+ get: () => methodOptions.value.find(({ id }) => id === props.method),
32
40
  set: (opt) => opt?.id && emit("change", opt.id)
33
41
  });
34
42
  const variants = cva({
@@ -51,7 +59,7 @@ var HttpMethod_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
51
59
  modelValue: selectedMethod.value,
52
60
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedMethod.value = $event),
53
61
  class: "font-code scalar-client mt-1 text-sm",
54
- options: unref(methodOptions)
62
+ options: methodOptions.value
55
63
  }, {
56
64
  default: withCtx(() => [createElementVNode("div", { class: normalizeClass(["h-full", { "pointer-events-none": !__props.isEditable }]) }, [createElementVNode("button", {
57
65
  class: normalizeClass(["relative h-full", unref(cx)(unref(variants)({
@@ -1 +1 @@
1
- {"version":3,"file":"HttpMethod.vue.script.js","names":[],"sources":["../../../src/components/HttpMethod/HttpMethod.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport {\n getHttpMethodInfo,\n REQUEST_METHODS,\n} from '@scalar/helpers/http/http-info'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n isSquare?: boolean\n method: string\n isEditable?: boolean\n }>(),\n { isSquare: false, isDisable: false, isEditable: false },\n)\n\nconst emit = defineEmits<{\n (e: 'change', value: HttpMethod): void\n}>()\n\nconst method = computed(() => getHttpMethodInfo(props.method))\nconst methodOptions = objectEntries(REQUEST_METHODS).map(([id]) => ({\n id,\n label: id.toUpperCase(),\n color: getHttpMethodInfo(id).colorClass,\n}))\nconst selectedMethod = computed({\n get: () => methodOptions.find(({ id }) => id === props.method),\n set: (opt) => opt?.id && emit('change', opt.id),\n})\n\nconst variants = cva({\n base: 'text-center font-code text-3xs justify-center items-center flex',\n variants: {\n isSquare: {\n true: 'px-2.5 whitespace-nowrap font-bold border-r h-fit m-auto',\n false: 'rounded-full',\n },\n isEditable: {\n true: 'http-bg-gradient rounded-md border',\n false: 'cursor-auto',\n },\n },\n})\n\nconst httpLabel = computed(() => method.value.short)\n</script>\n<template>\n <!-- Dropdown select -->\n <ScalarListbox\n v-if=\"isEditable\"\n v-model=\"selectedMethod\"\n class=\"font-code scalar-client mt-1 text-sm\"\n :options=\"methodOptions\">\n <div\n class=\"h-full\"\n :class=\"{ 'pointer-events-none': !isEditable }\">\n <button\n class=\"relative h-full\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n <span>{{ httpLabel }}</span>\n </button>\n </div>\n </ScalarListbox>\n <!-- Display only -->\n <div\n v-else\n class=\"relative gap-1 whitespace-nowrap\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n {{ method.short }}\n </div>\n</template>\n<style scoped>\n.http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.75), rgba(0, 0, 0, 0.035));\n}\n.http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.035), rgba(255, 255, 255, 0.75));\n}\n.dark-mode .http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.035), rgba(0, 0, 0, 0.15));\n}\n.dark-mode .http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.035));\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAWA,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,MAAM,SAAS,eAAe,kBAAkB,MAAM,MAAM,CAAC;EAC7D,MAAM,gBAAgB,cAAc,eAAe,CAAC,CAAC,KAAK,CAAC,SAAS;GAClE;GACA,OAAO,GAAG,YAAY;GACtB,OAAO,kBAAkB,EAAE,CAAC,CAAC;EAC/B,EAAE;EACF,MAAM,iBAAiB,SAAS;GAC9B,WAAW,cAAc,MAAM,EAAE,SAAS,OAAO,MAAM,MAAM;GAC7D,MAAM,QAAQ,KAAK,MAAM,KAAK,UAAU,IAAI,EAAE;EAChD,CAAC;EAED,MAAM,WAAW,IAAI;GACnB,MAAM;GACN,UAAU;IACR,UAAU;KACR,MAAM;KACN,OAAO;IACT;IACA,YAAY;KACV,MAAM;KACN,OAAO;IACT;GACF;EACF,CAAC;EAED,MAAM,YAAY,eAAe,OAAO,MAAM,KAAK;;UAKzC,QAAA,cAAA,UAAA,GADR,YAegB,MAAA,aAAA,GAAA;;gBAbL,eAAA;gFAAc,QAAA;IACvB,OAAM;IACL,SAAS,MAAA,aAAA;;2BAUJ,CATN,mBASM,OAAA,EARJ,OAAK,eAAA,CAAC,UAAQ,EAAA,uBAAA,CACoB,QAAA,WAAU,CAAA,CAAA,EAAA,GAAA,CAC5C,mBAKS,UAAA;KAJP,OAAK,eAAA,CAAC,mBACE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;MAAA,UAAG,QAAA;MAAQ,YAAE,QAAA;KAAU,CAAA,GAAK,OAAA,MAAO,UAAU,CAAA,CAAA;KAChE,MAAK;QACL,mBAA4B,QAAA,MAAA,gBAAnB,UAAA,KAAS,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA;;qDAKxB,mBAMM,OAAA;;IAJJ,OAAK,eAAA,CAAC,oCACE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;KAAA,UAAG,QAAA;KAAQ,YAAE,QAAA;IAAU,CAAA,GAAK,OAAA,MAAO,UAAU,CAAA,CAAA;IAChE,MAAK;sBACF,OAAA,MAAO,KAAK,GAAA,CAAA"}
1
+ {"version":3,"file":"HttpMethod.vue.script.js","names":[],"sources":["../../../src/components/HttpMethod/HttpMethod.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport {\n getHttpMethodInfo,\n REQUEST_METHODS,\n} from '@scalar/helpers/http/http-info'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { cva, cx } from '@scalar/use-hooks/useBindCx'\nimport { computed } from 'vue'\n\nconst props = withDefaults(\n defineProps<{\n isSquare?: boolean\n method: string\n isEditable?: boolean\n }>(),\n { isSquare: false, isDisable: false, isEditable: false },\n)\n\nconst emit = defineEmits<{\n (e: 'change', value: string): void\n}>()\n\nconst method = computed(() => getHttpMethodInfo(props.method))\nconst methodOptions = computed(() => {\n const options = objectEntries(REQUEST_METHODS).map(([id]) => ({\n id: String(id),\n label: id.toUpperCase(),\n color: getHttpMethodInfo(id).colorClass,\n }))\n\n // Keep document-defined methods selectable without changing their spelling.\n if (!options.some(({ id }) => id === props.method)) {\n options.push({\n id: props.method,\n label: props.method,\n color: method.value.colorClass,\n })\n }\n\n return options\n})\nconst selectedMethod = computed({\n get: () => methodOptions.value.find(({ id }) => id === props.method),\n set: (opt) => opt?.id && emit('change', opt.id),\n})\n\nconst variants = cva({\n base: 'text-center font-code text-3xs justify-center items-center flex',\n variants: {\n isSquare: {\n true: 'px-2.5 whitespace-nowrap font-bold border-r h-fit m-auto',\n false: 'rounded-full',\n },\n isEditable: {\n true: 'http-bg-gradient rounded-md border',\n false: 'cursor-auto',\n },\n },\n})\n\nconst httpLabel = computed(() => method.value.short)\n</script>\n<template>\n <!-- Dropdown select -->\n <ScalarListbox\n v-if=\"isEditable\"\n v-model=\"selectedMethod\"\n class=\"font-code scalar-client mt-1 text-sm\"\n :options=\"methodOptions\">\n <div\n class=\"h-full\"\n :class=\"{ 'pointer-events-none': !isEditable }\">\n <button\n class=\"relative h-full\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n <span>{{ httpLabel }}</span>\n </button>\n </div>\n </ScalarListbox>\n <!-- Display only -->\n <div\n v-else\n class=\"relative gap-1 whitespace-nowrap\"\n :class=\"cx(variants({ isSquare, isEditable }), method.colorClass)\"\n type=\"button\">\n {{ method.short }}\n </div>\n</template>\n<style scoped>\n.http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.75), rgba(0, 0, 0, 0.035));\n}\n.http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.035), rgba(255, 255, 255, 0.75));\n}\n.dark-mode .http-bg-gradient {\n background: linear-gradient(rgba(255, 255, 255, 0.035), rgba(0, 0, 0, 0.15));\n}\n.dark-mode .http-bg-gradient:hover {\n background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.035));\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAUA,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,MAAM,SAAS,eAAe,kBAAkB,MAAM,MAAM,CAAC;EAC7D,MAAM,gBAAgB,eAAe;GACnC,MAAM,UAAU,cAAc,eAAe,CAAC,CAAC,KAAK,CAAC,SAAS;IAC5D,IAAI,OAAO,EAAE;IACb,OAAO,GAAG,YAAY;IACtB,OAAO,kBAAkB,EAAE,CAAC,CAAC;GAC/B,EAAE;GAGF,IAAI,CAAC,QAAQ,MAAM,EAAE,SAAS,OAAO,MAAM,MAAM,GAC/C,QAAQ,KAAK;IACX,IAAI,MAAM;IACV,OAAO,MAAM;IACb,OAAO,OAAO,MAAM;GACtB,CAAC;GAGH,OAAO;EACT,CAAC;EACD,MAAM,iBAAiB,SAAS;GAC9B,WAAW,cAAc,MAAM,MAAM,EAAE,SAAS,OAAO,MAAM,MAAM;GACnE,MAAM,QAAQ,KAAK,MAAM,KAAK,UAAU,IAAI,EAAE;EAChD,CAAC;EAED,MAAM,WAAW,IAAI;GACnB,MAAM;GACN,UAAU;IACR,UAAU;KACR,MAAM;KACN,OAAO;IACT;IACA,YAAY;KACV,MAAM;KACN,OAAO;IACT;GACF;EACF,CAAC;EAED,MAAM,YAAY,eAAe,OAAO,MAAM,KAAK;;UAKzC,QAAA,cAAA,UAAA,GADR,YAegB,MAAA,aAAA,GAAA;;gBAbL,eAAA;gFAAc,QAAA;IACvB,OAAM;IACL,SAAS,cAAA;;2BAUJ,CATN,mBASM,OAAA,EARJ,OAAK,eAAA,CAAC,UAAQ,EAAA,uBAAA,CACoB,QAAA,WAAU,CAAA,CAAA,EAAA,GAAA,CAC5C,mBAKS,UAAA;KAJP,OAAK,eAAA,CAAC,mBACE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;MAAA,UAAG,QAAA;MAAQ,YAAE,QAAA;KAAU,CAAA,GAAK,OAAA,MAAO,UAAU,CAAA,CAAA;KAChE,MAAK;QACL,mBAA4B,QAAA,MAAA,gBAAnB,UAAA,KAAS,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA;;qDAKxB,mBAMM,OAAA;;IAJJ,OAAK,eAAA,CAAC,oCACE,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ,CAAA;KAAA,UAAG,QAAA;KAAQ,YAAE,QAAA;IAAU,CAAA,GAAK,OAAA,MAAO,UAAU,CAAA,CAAA;IAChE,MAAK;sBACF,OAAA,MAAO,KAAK,GAAA,CAAA"}
package/dist/style.css CHANGED
@@ -1905,8 +1905,7 @@
1905
1905
  border-radius: var(--scalar-radius);
1906
1906
  }
1907
1907
 
1908
- .sidebar-heading-type[data-v-bdef8365] {
1909
- text-transform: uppercase;
1908
+ .sidebar-heading-type[data-v-5edae08d] {
1910
1909
  color: var(--method-color, var(--scalar-color-1));
1911
1910
  font-size: 10px;
1912
1911
  line-height: 14px;
@@ -6049,16 +6048,16 @@
6049
6048
  }
6050
6049
  }
6051
6050
 
6052
- .http-bg-gradient[data-v-86e2b3ea] {
6051
+ .http-bg-gradient[data-v-e7883636] {
6053
6052
  background: linear-gradient(rgba(255, 255, 255, 0.75), rgba(0, 0, 0, 0.035));
6054
6053
  }
6055
- .http-bg-gradient[data-v-86e2b3ea]:hover {
6054
+ .http-bg-gradient[data-v-e7883636]:hover {
6056
6055
  background: linear-gradient(rgba(0, 0, 0, 0.035), rgba(255, 255, 255, 0.75));
6057
6056
  }
6058
- .dark-mode .http-bg-gradient[data-v-86e2b3ea] {
6057
+ .dark-mode .http-bg-gradient[data-v-e7883636] {
6059
6058
  background: linear-gradient(rgba(255, 255, 255, 0.035), rgba(0, 0, 0, 0.15));
6060
6059
  }
6061
- .dark-mode .http-bg-gradient[data-v-86e2b3ea]:hover {
6060
+ .dark-mode .http-bg-gradient[data-v-e7883636]:hover {
6062
6061
  background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.035));
6063
6062
  }
6064
6063
 
@@ -6084,7 +6083,7 @@ to {
6084
6083
  }
6085
6084
  }
6086
6085
 
6087
- .open-api-client-button[data-v-8cc9a5bf] {
6086
+ .open-api-client-button[data-v-746e7aef] {
6088
6087
  cursor: pointer;
6089
6088
  width: 100%;
6090
6089
  padding: 9px 12px;
@@ -6103,7 +6102,7 @@ to {
6103
6102
  gap: 6px;
6104
6103
  color: var(--scalar-sidebar-color-1);
6105
6104
  }
6106
- .open-api-client-button[data-v-8cc9a5bf]:hover {
6105
+ .open-api-client-button[data-v-746e7aef]:hover {
6107
6106
  background: var(
6108
6107
  --scalar-sidebar-item-hover-background,
6109
6108
  var(--scalar-background-2)
@@ -6531,20 +6530,20 @@ input[data-v-135d11b9]::placeholder {
6531
6530
  scrollbar-width: none;
6532
6531
  }
6533
6532
 
6534
- .request-section-content[data-v-4811507f] {
6533
+ .request-section-content[data-v-ec56362c] {
6535
6534
  --scalar-border-width: 0.5px;
6536
6535
  }
6537
- .request-section-content-filter[data-v-4811507f] {
6536
+ .request-section-content-filter[data-v-ec56362c] {
6538
6537
  box-shadow: 0 -10px 0 10px var(--scalar-background-1);
6539
6538
  }
6540
- .request-item:focus-within .request-meta-buttons[data-v-4811507f] {
6539
+ .request-item:focus-within .request-meta-buttons[data-v-ec56362c] {
6541
6540
  opacity: 1;
6542
6541
  }
6543
- .group-hover-input[data-v-4811507f] {
6542
+ .group-hover-input[data-v-ec56362c] {
6544
6543
  border-width: var(--scalar-border-width);
6545
6544
  border-color: transparent;
6546
6545
  }
6547
- .group:hover .group-hover-input[data-v-4811507f] {
6546
+ .group:hover .group-hover-input[data-v-ec56362c] {
6548
6547
  background: color-mix(
6549
6548
  in srgb,
6550
6549
  var(--scalar-background-1),
@@ -6552,7 +6551,7 @@ input[data-v-135d11b9]::placeholder {
6552
6551
  );
6553
6552
  border-color: var(--scalar-border-color);
6554
6553
  }
6555
- .group-hover-input[data-v-4811507f]:focus {
6554
+ .group-hover-input[data-v-ec56362c]:focus {
6556
6555
  background: transparent !important;
6557
6556
  border-color: var(--scalar-border-color) !important;
6558
6557
  }
@@ -6671,28 +6670,28 @@ to {
6671
6670
  margin-top: 0;
6672
6671
  }
6673
6672
 
6674
- .address-bar-history-button[data-v-36f5ec26]:hover {
6673
+ .address-bar-history-button[data-v-a54819b1]:hover {
6675
6674
  background: var(--scalar-background-3);
6676
6675
  }
6677
- .address-bar-history-button[data-v-36f5ec26]:focus-within {
6676
+ .address-bar-history-button[data-v-a54819b1]:focus-within {
6678
6677
  background: var(--scalar-background-2);
6679
6678
  }
6680
6679
 
6681
- [data-v-65ff0ebf] .cm-editor {
6680
+ [data-v-0e96b5b8] .cm-editor {
6682
6681
  height: 100%;
6683
6682
  outline: none;
6684
6683
  width: 100%;
6685
6684
  }
6686
- [data-v-65ff0ebf] .cm-line {
6685
+ [data-v-0e96b5b8] .cm-line {
6687
6686
  padding: 0;
6688
6687
  }
6689
- [data-v-65ff0ebf] .cm-content {
6688
+ [data-v-0e96b5b8] .cm-content {
6690
6689
  padding: 0;
6691
6690
  display: flex;
6692
6691
  align-items: center;
6693
6692
  font-size: var(--scalar-small);
6694
6693
  }
6695
- .scroll-timeline-x[data-v-65ff0ebf] {
6694
+ .scroll-timeline-x[data-v-0e96b5b8] {
6696
6695
  -ms-overflow-style: none;
6697
6696
  /* Fade the URL at the viewport edges. Mask lives on the scrollport (this
6698
6697
  element) rather than on `.cm-scroller`, whose width follows the URL's
@@ -6705,31 +6704,31 @@ to {
6705
6704
  transparent 100%
6706
6705
  );
6707
6706
  }
6708
- .scroll-timeline-x-hidden[data-v-65ff0ebf] {
6707
+ .scroll-timeline-x-hidden[data-v-0e96b5b8] {
6709
6708
  overflow-x: auto;
6710
6709
  }
6711
- .scroll-timeline-x-hidden[data-v-65ff0ebf] .cm-scroller {
6710
+ .scroll-timeline-x-hidden[data-v-0e96b5b8] .cm-scroller {
6712
6711
  scrollbar-width: none;
6713
6712
  -ms-overflow-style: none;
6714
6713
  padding-right: 20px;
6715
6714
  overflow: auto;
6716
6715
  }
6717
- .scroll-timeline-x-hidden[data-v-65ff0ebf]::-webkit-scrollbar {
6716
+ .scroll-timeline-x-hidden[data-v-0e96b5b8]::-webkit-scrollbar {
6718
6717
  width: 0;
6719
6718
  height: 0;
6720
6719
  display: none;
6721
6720
  }
6722
- .scroll-timeline-x-hidden[data-v-65ff0ebf] .cm-scroller::-webkit-scrollbar {
6721
+ .scroll-timeline-x-hidden[data-v-0e96b5b8] .cm-scroller::-webkit-scrollbar {
6723
6722
  width: 0;
6724
6723
  height: 0;
6725
6724
  display: none;
6726
6725
  }
6727
- .scroll-timeline-x-address[data-v-65ff0ebf] {
6726
+ .scroll-timeline-x-address[data-v-0e96b5b8] {
6728
6727
  line-height: 27px;
6729
6728
  scrollbar-width: none; /* Firefox */
6730
6729
  }
6731
6730
  /* make clickable are to left of send button */
6732
- .scroll-timeline-x-address[data-v-65ff0ebf]:after {
6731
+ .scroll-timeline-x-address[data-v-0e96b5b8]:after {
6733
6732
  content: '';
6734
6733
  position: absolute;
6735
6734
  height: 100%;
@@ -6737,7 +6736,7 @@ to {
6737
6736
  right: 0;
6738
6737
  cursor: text;
6739
6738
  }
6740
- .address-bar-bg-states[data-v-65ff0ebf] {
6739
+ .address-bar-bg-states[data-v-0e96b5b8] {
6741
6740
  --scalar-address-bar-bg: color-mix(
6742
6741
  in srgb,
6743
6742
  var(--scalar-background-1),
@@ -6745,22 +6744,22 @@ to {
6745
6744
  );
6746
6745
  background: var(--scalar-address-bar-bg);
6747
6746
  }
6748
- .address-bar-bg-states[data-v-65ff0ebf]:has(.cm-focused) {
6747
+ .address-bar-bg-states[data-v-0e96b5b8]:has(.cm-focused) {
6749
6748
  --scalar-address-bar-bg: var(--scalar-background-1);
6750
6749
  border-color: var(--scalar-border-color);
6751
6750
  outline-width: 1px;
6752
6751
  outline-style: solid;
6753
6752
  }
6754
- .address-bar-bg-states:has(.cm-focused) .fade-left[data-v-65ff0ebf],
6755
- .address-bar-bg-states:has(.cm-focused) .fade-right[data-v-65ff0ebf] {
6753
+ .address-bar-bg-states:has(.cm-focused) .fade-left[data-v-0e96b5b8],
6754
+ .address-bar-bg-states:has(.cm-focused) .fade-right[data-v-0e96b5b8] {
6756
6755
  --scalar-address-bar-bg: var(--scalar-background-1);
6757
6756
  }
6758
6757
 
6759
- .app-exit-button[data-v-70c49191] {
6758
+ .app-exit-button[data-v-cf0538d1] {
6760
6759
  color: white;
6761
6760
  background: rgba(0, 0, 0, 0.1);
6762
6761
  }
6763
- .app-exit-button[data-v-70c49191]:hover {
6762
+ .app-exit-button[data-v-cf0538d1]:hover {
6764
6763
  background: rgba(255, 255, 255, 0.1);
6765
6764
  }
6766
6765
  /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@@ -1,4 +1,3 @@
1
- import type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods';
2
1
  import { type ClientPlugin } from '@scalar/oas-utils/helpers';
3
2
  import { type AvailableClients } from '@scalar/types/snippetz';
4
3
  import type { WorkspaceStore } from '@scalar/workspace-store/client';
@@ -46,7 +45,7 @@ export type OperationBlockProps = {
46
45
  /** Current request path */
47
46
  path: string;
48
47
  /** Current request method */
49
- method: HttpMethodType;
48
+ method: string;
50
49
  /** Whether `path` identifies an OpenAPI webhook. */
51
50
  isWebhook?: boolean;
52
51
  /** HTTP clients */
@@ -1 +1 @@
1
- {"version":3,"file":"OperationBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OperationBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"names":[],"mappings":"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"}
@@ -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 /** Keep execution and editing unavailable until the selected payload is ready. */\n externalExamplesPending?: boolean\n externalExamplesFailed?: boolean\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 /** Operation before downloaded examples are overlaid, for parameter edit targets. */\n sourceOperation?: 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 { ScalarButton } from '@scalar/components/button'\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 {\n executeHook,\n executeResponseHook,\n type ClientPlugin,\n} 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.2/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.2/strict/operation'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n toValue,\n watch,\n} from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.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 { useLocalization } from '@/v2/features/localization'\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 externalExamplesPending = false,\n externalExamplesFailed = false,\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 sourceOperation,\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\ndefineEmits<{ (e: 'retry:externalExamples'): void }>()\n\nconst { translate } = useLocalization()\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 if (externalExamplesPending) return\n eventBus.flushDebouncedEmits?.()\n // Source edits must reach the overlaid operation prop before building the request.\n await nextTick()\n\n if (isWebhook && !requestPath.value.trim()) {\n toast(translate('apiClient.operationBlock.webhookUrlRequired'), 'error')\n return\n }\n\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast(translate('apiClient.operationBlock.pathParametersRequired'), '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 onResponseReceived: async (response, responseDuration) => {\n const result = await executeResponseHook(\n {\n response,\n responseDuration,\n requestBuilder,\n request: buildSafeBodyRequest(...built.data.requestPayload),\n document,\n operation,\n variablesStore,\n },\n plugins,\n )\n return result.response\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 :executionDisabled=\"externalExamplesPending\"\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 <ViewLayoutSection\n v-if=\"externalExamplesPending\"\n aria-label=\"Request\">\n <template #title>Request</template>\n <div\n class=\"text-c-2 p-4\"\n role=\"status\">\n <template v-if=\"externalExamplesFailed\">\n Could not load this example.\n <ScalarButton\n size=\"sm\"\n variant=\"ghost\"\n @click=\"$emit('retry:externalExamples')\">\n Retry\n </ScalarButton>\n </template>\n <template v-else>Loading example…</template>\n </div>\n </ViewLayoutSection>\n <RequestBlock\n v-else\n :authMeta\n :clientOptions\n :defaultHeaders\n :defaultRequestBodyView=\"\n document['x-scalar-default-request-body-view']\n \"\n :documentCookies\n :documentSlug\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 :sourceOperation\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :executionDisabled=\"externalExamplesPending\"\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 /** Keep execution and editing unavailable until the selected payload is ready. */\n externalExamplesPending?: boolean\n externalExamplesFailed?: boolean\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: string\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 /** Operation before downloaded examples are overlaid, for parameter edit targets. */\n sourceOperation?: 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 { ScalarButton } from '@scalar/components/button'\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 { isForbiddenHttpMethod } from '@scalar/helpers/http/is-forbidden-http-method'\nimport {\n executeHook,\n executeResponseHook,\n type ClientPlugin,\n} 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.2/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.2/strict/operation'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n toValue,\n watch,\n} from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.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 { useLocalization } from '@/v2/features/localization'\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 externalExamplesPending = false,\n externalExamplesFailed = false,\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 sourceOperation,\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\ndefineEmits<{ (e: 'retry:externalExamples'): void }>()\n\nconst { translate } = useLocalization()\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 if (externalExamplesPending) return\n eventBus.flushDebouncedEmits?.()\n // Source edits must reach the overlaid operation prop before building the request.\n await nextTick()\n\n if (isWebhook && !requestPath.value.trim()) {\n toast(translate('apiClient.operationBlock.webhookUrlRequired'), 'error')\n return\n }\n\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast(translate('apiClient.operationBlock.pathParametersRequired'), '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 // Check the final method because pre-request scripts can change it.\n const requestMethod = built.data.requestPayload[1].method ?? 'GET'\n if (isForbiddenHttpMethod(requestMethod)) {\n persistScriptEnvironment()\n toast(\n translate('apiClient.operationBlock.forbiddenMethod', {\n method: requestMethod.toUpperCase(),\n }),\n 'error',\n )\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 onResponseReceived: async (response, responseDuration) => {\n const result = await executeResponseHook(\n {\n response,\n responseDuration,\n requestBuilder,\n request: buildSafeBodyRequest(...built.data.requestPayload),\n document,\n operation,\n variablesStore,\n },\n plugins,\n )\n return result.response\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 string,\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 :executionDisabled=\"externalExamplesPending\"\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 <ViewLayoutSection\n v-if=\"externalExamplesPending\"\n aria-label=\"Request\">\n <template #title>Request</template>\n <div\n class=\"text-c-2 p-4\"\n role=\"status\">\n <template v-if=\"externalExamplesFailed\">\n Could not load this example.\n <ScalarButton\n size=\"sm\"\n variant=\"ghost\"\n @click=\"$emit('retry:externalExamples')\">\n Retry\n </ScalarButton>\n </template>\n <template v-else>Loading example…</template>\n </div>\n </ViewLayoutSection>\n <RequestBlock\n v-else\n :authMeta\n :clientOptions\n :defaultHeaders\n :defaultRequestBodyView=\"\n document['x-scalar-default-request-body-view']\n \"\n :documentCookies\n :documentSlug\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 :sourceOperation\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :executionDisabled=\"externalExamplesPending\"\n :layout\n :plugins\n :requestPayload\n :response\n :totalPerformedRequests=\"operationHistory.length\" />\n </ViewLayoutContent>\n </ViewLayout>\n </div>\n</template>\n"],"mappings":""}
@@ -19,6 +19,7 @@ import { ERRORS } from "@scalar/helpers/errors/normalize-error";
19
19
  import { isElectron } from "@scalar/helpers/general/is-electron";
20
20
  import { buildSafeBodyRequest } from "@scalar/helpers/http/can-method-have-body";
21
21
  import { generateClientOptions } from "@scalar/blocks/code-example";
22
+ import { isForbiddenHttpMethod } from "@scalar/helpers/http/is-forbidden-http-method";
22
23
  import { executeHook, executeResponseHook } from "@scalar/oas-utils/helpers";
23
24
  import { AVAILABLE_CLIENTS } from "@scalar/types/snippetz";
24
25
  import { useClipboard } from "@scalar/use-hooks/useClipboard";
@@ -236,6 +237,12 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
236
237
  toast(built.message ?? built.error, "error");
237
238
  return;
238
239
  }
240
+ const requestMethod = built.data.requestPayload[1].method ?? "GET";
241
+ if (isForbiddenHttpMethod(requestMethod)) {
242
+ persistScriptEnvironment();
243
+ toast(translate("apiClient.operationBlock.forbiddenMethod", { method: requestMethod.toUpperCase() }), "error");
244
+ return;
245
+ }
239
246
  abortController.value = built.data.controller;
240
247
  const request = buildSafeBodyRequest(...built.data.requestPayload);
241
248
  await executeHook({