@scalar/api-client 3.21.3 → 3.21.5
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 +20 -0
- package/dist/plugins/posthog/sanitize-event-payload.d.ts.map +1 -1
- package/dist/plugins/posthog/sanitize-event-payload.js +1 -0
- package/dist/plugins/posthog/sanitize-event-payload.js.map +1 -1
- package/dist/style.css +59 -49
- 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 +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
- 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 +1 -1
- package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js +3 -0
- package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.js +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.script.js +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.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/blocks/scalar-auth-selector-block/components/OAuth2.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.script.js +53 -22
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.d.ts +4 -0
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.script.js +23 -12
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.script.js +30 -6
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthTab.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-device-authorization.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-device-authorization.js +3 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-device-authorization.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.js +9 -3
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.js.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.d.ts.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.js +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.js.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.script.js.map +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.d.ts.map +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.js +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.js.map +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.script.js +15 -11
- package/dist/v2/components/data-table/DataTableInput.vue.script.js.map +1 -1
- package/dist/v2/components/server/ServerDropdown.vue.d.ts.map +1 -1
- package/dist/v2/components/server/ServerDropdown.vue.js.map +1 -1
- package/dist/v2/components/server/ServerDropdown.vue.script.js +1 -1
- package/dist/v2/components/server/ServerDropdown.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/localization/locales/ar.d.ts +3 -0
- package/dist/v2/features/localization/locales/ar.d.ts.map +1 -1
- package/dist/v2/features/localization/locales/ar.js +4 -1
- package/dist/v2/features/localization/locales/ar.js.map +1 -1
- package/dist/v2/features/localization/locales/de.d.ts +3 -0
- package/dist/v2/features/localization/locales/de.d.ts.map +1 -1
- package/dist/v2/features/localization/locales/de.js +4 -1
- package/dist/v2/features/localization/locales/de.js.map +1 -1
- package/dist/v2/features/localization/locales/es.d.ts +3 -0
- package/dist/v2/features/localization/locales/es.d.ts.map +1 -1
- package/dist/v2/features/localization/locales/es.js +4 -1
- package/dist/v2/features/localization/locales/es.js.map +1 -1
- package/dist/v2/features/localization/locales/fr.d.ts +3 -0
- package/dist/v2/features/localization/locales/fr.d.ts.map +1 -1
- package/dist/v2/features/localization/locales/fr.js +4 -1
- package/dist/v2/features/localization/locales/fr.js.map +1 -1
- package/dist/v2/features/localization/locales/pt.d.ts +3 -0
- package/dist/v2/features/localization/locales/pt.d.ts.map +1 -1
- package/dist/v2/features/localization/locales/pt.js +4 -1
- package/dist/v2/features/localization/locales/pt.js.map +1 -1
- package/dist/v2/features/localization/locales/ru.d.ts +3 -0
- package/dist/v2/features/localization/locales/ru.d.ts.map +1 -1
- package/dist/v2/features/localization/locales/ru.js +4 -1
- package/dist/v2/features/localization/locales/ru.js.map +1 -1
- package/dist/v2/features/localization/locales/zh-cn.d.ts +3 -0
- package/dist/v2/features/localization/locales/zh-cn.d.ts.map +1 -1
- package/dist/v2/features/localization/locales/zh-cn.js +4 -1
- package/dist/v2/features/localization/locales/zh-cn.js.map +1 -1
- package/dist/v2/features/localization/translations.d.ts +3 -0
- package/dist/v2/features/localization/translations.d.ts.map +1 -1
- package/dist/v2/features/localization/translations.js +4 -1
- package/dist/v2/features/localization/translations.js.map +1 -1
- package/dist/v2/workspace-events.d.ts.map +1 -1
- package/dist/v2/workspace-events.js +1 -0
- package/dist/v2/workspace-events.js.map +1 -1
- package/dist/vue-styles.css +29 -31
- package/package.json +11 -11
|
@@ -2,7 +2,7 @@ import _plugin_vue_export_helper_default from "../../../../_virtual/_plugin-vue_
|
|
|
2
2
|
import AddressBarHistory_vue_vue_type_script_setup_true_lang_default from "./AddressBarHistory.vue.script.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue
|
|
5
|
-
var AddressBarHistory_default = /*#__PURE__*/ _plugin_vue_export_helper_default(AddressBarHistory_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
5
|
+
var AddressBarHistory_default = /*#__PURE__*/ _plugin_vue_export_helper_default(AddressBarHistory_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-8704eaef"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { AddressBarHistory_default as default };
|
|
8
8
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddressBarHistory.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n} from '@scalar/components/floating'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { formatMilliseconds } from '@scalar/helpers/formatters/format-milliseconds'\nimport { httpStatusCodes } from '@scalar/helpers/http/http-status-codes'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport ValueEmitter from '@/v2/components/layout/ValueEmitter.vue'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport { getStatusCodeColor } from './httpStatusCodeColors'\n\nconst { target } = defineProps<{\n /** The id of the target to use for the popover (e.g. address bar) */\n target: string\n /** List of all request history items */\n history: History[]\n}>()\n\nconst emits = defineEmits<{\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the open state of the history popover */\n (e: 'update:open', value: boolean): void\n}>()\n\nconst { translate } = useLocalization()\n\nexport type History = {\n method: string\n path: string\n duration: number\n status: number\n}\n</script>\n<template>\n <Menu\n v-slot=\"{ open }\"\n as=\"div\">\n <!-- Emit the slot value back out the parent -->\n <ValueEmitter\n :value=\"open\"\n @change=\"(value) => emits('update:open', value)\"\n @unmount=\"emits('update:open', false)\" />\n\n <ScalarFloating\n :offset=\"0\"\n resize\n :target=\"target\">\n <!-- History -->\n <MenuButton\n v-if=\"history.length\"\n class=\"address-bar-history-button text-c-3 focus:text-c-1 relative mr-1 rounded-lg p-1.5\">\n <ScalarIcon\n icon=\"History\"\n size=\"sm\"\n thickness=\"2.25\" />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBarHistory.requestHistory')\n }}</span>\n </MenuButton>\n <!-- History shadow and placement-->\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <!-- History Item -->\n <MenuItems\n class=\"custom-scroll grid max-h-[inherit] grid-cols-[44px_1fr_repeat(3,auto)] items-center p-0.75\"\n static\n :style=\"{ width }\">\n <MenuItem\n v-for=\"(entry, index) in history\"\n :key=\"index\"\n as=\"button\"\n class=\"font-code ui-active:*:bg-b-2 text-c-2 contents text-sm font-medium *:flex *:h-8 *:cursor-pointer *:items-center *:rounded-none *:px-1.5 *:first:rounded-l *:last:rounded-r\"\n :value=\"index\"\n @click=\"emits('select:history:item', { index })\">\n <HttpMethod\n class=\"text-[11px]\"\n :method=\"entry.method\" />\n <div class=\"min-w-0\">\n <div class=\"text-c-1 min-w-0 truncate\">\n {{ entry.path }}\n </div>\n </div>\n <div>{{ formatMilliseconds(entry.duration) }}</div>\n <div :class=\"[getStatusCodeColor(entry.status).color]\">\n {{ entry.status }}\n </div>\n <div>\n {{ httpStatusCodes[entry.status]?.name }}\n </div>\n </MenuItem>\n </MenuItems>\n <ScalarFloatingBackdrop
|
|
1
|
+
{"version":3,"file":"AddressBarHistory.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n} from '@scalar/components/floating'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { formatMilliseconds } from '@scalar/helpers/formatters/format-milliseconds'\nimport { httpStatusCodes } from '@scalar/helpers/http/http-status-codes'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport ValueEmitter from '@/v2/components/layout/ValueEmitter.vue'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport { getStatusCodeColor } from './httpStatusCodeColors'\n\nconst { target } = defineProps<{\n /** The id of the target to use for the popover (e.g. address bar) */\n target: string\n /** List of all request history items */\n history: History[]\n}>()\n\nconst emits = defineEmits<{\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the open state of the history popover */\n (e: 'update:open', value: boolean): void\n}>()\n\nconst { translate } = useLocalization()\n\nexport type History = {\n method: string\n path: string\n duration: number\n status: number\n}\n</script>\n<template>\n <Menu\n v-slot=\"{ open }\"\n as=\"div\">\n <!-- Emit the slot value back out the parent -->\n <ValueEmitter\n :value=\"open\"\n @change=\"(value) => emits('update:open', value)\"\n @unmount=\"emits('update:open', false)\" />\n\n <ScalarFloating\n :offset=\"0\"\n resize\n :target=\"target\">\n <!-- History -->\n <MenuButton\n v-if=\"history.length\"\n class=\"address-bar-history-button text-c-3 focus:text-c-1 relative mr-1 rounded-lg p-1.5\">\n <ScalarIcon\n icon=\"History\"\n size=\"sm\"\n thickness=\"2.25\" />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBarHistory.requestHistory')\n }}</span>\n </MenuButton>\n <!-- History shadow and placement-->\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <!-- History Item -->\n <MenuItems\n class=\"custom-scroll grid max-h-[inherit] grid-cols-[44px_1fr_repeat(3,auto)] items-center p-0.75\"\n static\n :style=\"{ width }\">\n <MenuItem\n v-for=\"(entry, index) in history\"\n :key=\"index\"\n as=\"button\"\n class=\"font-code ui-active:*:bg-b-2 text-c-2 contents text-sm font-medium *:flex *:h-8 *:cursor-pointer *:items-center *:rounded-none *:px-1.5 *:first:rounded-l *:last:rounded-r\"\n :value=\"index\"\n @click=\"emits('select:history:item', { index })\">\n <HttpMethod\n class=\"text-[11px]\"\n :method=\"entry.method\" />\n <div class=\"min-w-0\">\n <div class=\"text-c-1 min-w-0 truncate\">\n {{ entry.path }}\n </div>\n </div>\n <div>{{ formatMilliseconds(entry.duration) }}</div>\n <div :class=\"[getStatusCodeColor(entry.status).color]\">\n {{ entry.status }}\n </div>\n <div>\n {{ httpStatusCodes[entry.status]?.name }}\n </div>\n </MenuItem>\n </MenuItems>\n <ScalarFloatingBackdrop\n class=\"rounded-none rounded-b-lg border border-t-0 after:hidden\" />\n </template>\n </ScalarFloating>\n </Menu>\n</template>\n<style scoped>\n.address-bar-history-button:hover {\n background: var(--scalar-background-3);\n}\n.address-bar-history-button:focus-within {\n background: var(--scalar-background-2);\n}\n</style>\n"],"mappings":""}
|
|
@@ -74,7 +74,7 @@ var AddressBarHistory_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
74
74
|
}, 1032, ["value", "onClick"]);
|
|
75
75
|
}), 128))]),
|
|
76
76
|
_: 1
|
|
77
|
-
}, 8, ["style"]), createVNode(unref(ScalarFloatingBackdrop), { class: "
|
|
77
|
+
}, 8, ["style"]), createVNode(unref(ScalarFloatingBackdrop), { class: "rounded-none rounded-b-lg border border-t-0 after:hidden" })]),
|
|
78
78
|
key: "0"
|
|
79
79
|
} : void 0]), 1032, ["target"])]),
|
|
80
80
|
_: 1
|
package/dist/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue.script.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddressBarHistory.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n} from '@scalar/components/floating'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { formatMilliseconds } from '@scalar/helpers/formatters/format-milliseconds'\nimport { httpStatusCodes } from '@scalar/helpers/http/http-status-codes'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport ValueEmitter from '@/v2/components/layout/ValueEmitter.vue'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport { getStatusCodeColor } from './httpStatusCodeColors'\n\nconst { target } = defineProps<{\n /** The id of the target to use for the popover (e.g. address bar) */\n target: string\n /** List of all request history items */\n history: History[]\n}>()\n\nconst emits = defineEmits<{\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the open state of the history popover */\n (e: 'update:open', value: boolean): void\n}>()\n\nconst { translate } = useLocalization()\n\nexport type History = {\n method: string\n path: string\n duration: number\n status: number\n}\n</script>\n<template>\n <Menu\n v-slot=\"{ open }\"\n as=\"div\">\n <!-- Emit the slot value back out the parent -->\n <ValueEmitter\n :value=\"open\"\n @change=\"(value) => emits('update:open', value)\"\n @unmount=\"emits('update:open', false)\" />\n\n <ScalarFloating\n :offset=\"0\"\n resize\n :target=\"target\">\n <!-- History -->\n <MenuButton\n v-if=\"history.length\"\n class=\"address-bar-history-button text-c-3 focus:text-c-1 relative mr-1 rounded-lg p-1.5\">\n <ScalarIcon\n icon=\"History\"\n size=\"sm\"\n thickness=\"2.25\" />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBarHistory.requestHistory')\n }}</span>\n </MenuButton>\n <!-- History shadow and placement-->\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <!-- History Item -->\n <MenuItems\n class=\"custom-scroll grid max-h-[inherit] grid-cols-[44px_1fr_repeat(3,auto)] items-center p-0.75\"\n static\n :style=\"{ width }\">\n <MenuItem\n v-for=\"(entry, index) in history\"\n :key=\"index\"\n as=\"button\"\n class=\"font-code ui-active:*:bg-b-2 text-c-2 contents text-sm font-medium *:flex *:h-8 *:cursor-pointer *:items-center *:rounded-none *:px-1.5 *:first:rounded-l *:last:rounded-r\"\n :value=\"index\"\n @click=\"emits('select:history:item', { index })\">\n <HttpMethod\n class=\"text-[11px]\"\n :method=\"entry.method\" />\n <div class=\"min-w-0\">\n <div class=\"text-c-1 min-w-0 truncate\">\n {{ entry.path }}\n </div>\n </div>\n <div>{{ formatMilliseconds(entry.duration) }}</div>\n <div :class=\"[getStatusCodeColor(entry.status).color]\">\n {{ entry.status }}\n </div>\n <div>\n {{ httpStatusCodes[entry.status]?.name }}\n </div>\n </MenuItem>\n </MenuItems>\n <ScalarFloatingBackdrop
|
|
1
|
+
{"version":3,"file":"AddressBarHistory.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBarHistory.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'\nimport {\n ScalarFloating,\n ScalarFloatingBackdrop,\n} from '@scalar/components/floating'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { formatMilliseconds } from '@scalar/helpers/formatters/format-milliseconds'\nimport { httpStatusCodes } from '@scalar/helpers/http/http-status-codes'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport ValueEmitter from '@/v2/components/layout/ValueEmitter.vue'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport { getStatusCodeColor } from './httpStatusCodeColors'\n\nconst { target } = defineProps<{\n /** The id of the target to use for the popover (e.g. address bar) */\n target: string\n /** List of all request history items */\n history: History[]\n}>()\n\nconst emits = defineEmits<{\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the open state of the history popover */\n (e: 'update:open', value: boolean): void\n}>()\n\nconst { translate } = useLocalization()\n\nexport type History = {\n method: string\n path: string\n duration: number\n status: number\n}\n</script>\n<template>\n <Menu\n v-slot=\"{ open }\"\n as=\"div\">\n <!-- Emit the slot value back out the parent -->\n <ValueEmitter\n :value=\"open\"\n @change=\"(value) => emits('update:open', value)\"\n @unmount=\"emits('update:open', false)\" />\n\n <ScalarFloating\n :offset=\"0\"\n resize\n :target=\"target\">\n <!-- History -->\n <MenuButton\n v-if=\"history.length\"\n class=\"address-bar-history-button text-c-3 focus:text-c-1 relative mr-1 rounded-lg p-1.5\">\n <ScalarIcon\n icon=\"History\"\n size=\"sm\"\n thickness=\"2.25\" />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBarHistory.requestHistory')\n }}</span>\n </MenuButton>\n <!-- History shadow and placement-->\n <template\n v-if=\"open\"\n #floating=\"{ width }\">\n <!-- History Item -->\n <MenuItems\n class=\"custom-scroll grid max-h-[inherit] grid-cols-[44px_1fr_repeat(3,auto)] items-center p-0.75\"\n static\n :style=\"{ width }\">\n <MenuItem\n v-for=\"(entry, index) in history\"\n :key=\"index\"\n as=\"button\"\n class=\"font-code ui-active:*:bg-b-2 text-c-2 contents text-sm font-medium *:flex *:h-8 *:cursor-pointer *:items-center *:rounded-none *:px-1.5 *:first:rounded-l *:last:rounded-r\"\n :value=\"index\"\n @click=\"emits('select:history:item', { index })\">\n <HttpMethod\n class=\"text-[11px]\"\n :method=\"entry.method\" />\n <div class=\"min-w-0\">\n <div class=\"text-c-1 min-w-0 truncate\">\n {{ entry.path }}\n </div>\n </div>\n <div>{{ formatMilliseconds(entry.duration) }}</div>\n <div :class=\"[getStatusCodeColor(entry.status).color]\">\n {{ entry.status }}\n </div>\n <div>\n {{ httpStatusCodes[entry.status]?.name }}\n </div>\n </MenuItem>\n </MenuItems>\n <ScalarFloatingBackdrop\n class=\"rounded-none rounded-b-lg border border-t-0 after:hidden\" />\n </template>\n </ScalarFloating>\n </Menu>\n</template>\n<style scoped>\n.address-bar-history-button:hover {\n background: var(--scalar-background-3);\n}\n.address-bar-history-button:focus-within {\n background: var(--scalar-background-2);\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EAuBA,MAAM,QAAQ;EAOd,MAAM,EAAE,cAAc,gBAAgB;;uBAUpC,YA8DO,MAAA,IAAA,GAAA,EA5DL,IAAG,MAAK,GAAA;sBAKmC,EANjC,WAAI,CAGd,YAG2C,sBAAA;KAFxC,OAAO;KACP,UAAM,OAAA,OAAA,OAAA,MAAG,UAAU,MAAK,eAAgB,KAAK;KAC7C,WAAO,OAAA,OAAA,OAAA,MAAA,WAAE,MAAK,eAAA,KAAA;4BAEjB,YAoDiB,MAAA,cAAA,GAAA;KAnDd,QAAQ;KACT,QAAA;KACC,QAAQ,QAAA;;4BAYI,CATL,QAAA,QAAQ,UAAA,UAAA,GADhB,YAUa,MAAA,UAAA,GAAA;;MARX,OAAM;;6BAIe,CAHrB,YAGqB,MAAA,UAAA,GAAA;OAFnB,MAAK;OACL,MAAK;OACL,WAAU;UACZ,mBAES,QAFT,YAES,gBADP,MAAA,SAAA,CAAS,CAAA,4CAAA,CAAA,GAAA,CAAA,CAAA,CAAA;;;;QAKL,OAAA;WACL;kBA6BW,EA7BC,YAAK,CAElB,YA2BY,MAAA,SAAA,GAAA;MA1BV,OAAM;MACN,QAAA;MACC,OAAK,eAAA,EAAI,MAAK,CAAA;;6BAEoB,EAAA,UAAA,IAAA,GADnC,mBAsBW,UAAA,MAAA,WArBgB,QAAA,UAAjB,OAAO,UAAK;2BADtB,YAsBW,MAAA,QAAA,GAAA;QApBR,KAAK;QACN,IAAG;QACH,OAAM;QACL,OAAO;QACP,UAAK,WAAE,MAAK,uBAAA,EAA0B,MAAK,CAAA;;+BAGjB;SAF3B,YAE2B,MAAA,kBAAA,GAAA;UADzB,OAAM;UACL,QAAQ,MAAM;;SACjB,mBAIM,OAJN,YAIM,CAHJ,mBAEM,OAFN,YAEM,gBADD,MAAM,IAAI,GAAA,CAAA,CAAA,CAAA;SAGjB,mBAAmD,OAAA,MAAA,gBAA3C,MAAA,kBAAA,CAAkB,CAAC,MAAM,QAAQ,CAAA,GAAA,CAAA;SACzC,mBAEM,OAAA,EAFA,OAAK,eAAA,CAAG,MAAA,kBAAA,CAAkB,CAAC,MAAM,MAAM,CAAA,CAAE,KAAK,CAAA,EAAA,GAAA,gBAC/C,MAAM,MAAM,GAAA,CAAA;SAEjB,mBAEM,OAAA,MAAA,gBADD,MAAA,eAAA,CAAe,CAAC,MAAM,OAAM,EAAG,IAAI,GAAA,CAAA;;;;;;uBAI5C,YACqE,MAAA,sBAAA,GAAA,EAAnE,OAAM,2DAA0D,CAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthSelector.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarButton,\n type ScalarButton as ScalarButtonType,\n} from '@scalar/components/button'\nimport { ScalarComboboxMultiselect } from '@scalar/components/combobox'\nimport type { Icon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarListboxCheckbox } from '@scalar/components/listbox'\nimport { useModal } from '@scalar/components/modal'\nimport { ScalarIconCaretDown, ScalarIconTrash } from '@scalar/icons'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n AuthMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n isAuthOptional,\n type MergedSecuritySchemes,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n OpenApiDocument,\n SecurityRequirementObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { computed, ref, useId } from 'vue'\n\nimport DeleteRequestAuthModal from '@/v2/blocks/scalar-auth-selector-block/components/DeleteRequestAuthModal.vue'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport {\n formatComplexScheme,\n formatScheme,\n getSecuritySchemeOptions,\n type SecuritySchemeOption,\n} from '@/v2/blocks/scalar-auth-selector-block/helpers/security-scheme'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport RequestAuthDataTable from './RequestAuthDataTable.vue'\n\nconst {\n environment,\n eventBus,\n createAnySecurityScheme = false,\n canDeleteSchemes = true,\n defaultOpen = true,\n heading = true,\n hideSingleRequiredScheme = false,\n isStatic = false,\n meta,\n proxyUrl,\n securityRequirements,\n securitySchemes,\n selectedSecurity,\n server,\n title,\n options,\n documentType,\n} = defineProps<{\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n /** Allows adding authentication which is not in the document */\n createAnySecurityScheme?: boolean\n /**\n * Whether schemes can be deleted from the selector. Enabled for the editable client, disabled in\n * the read-only reference where the schemes come from the document and cannot be removed.\n */\n canDeleteSchemes?: boolean\n /** Whether the authentication disclosure should start expanded */\n defaultOpen?: boolean\n /**\n * Whether the title belongs in the document outline. The client renders the\n * selector as one section of a request, so it is a heading there. A surface\n * that only offers the schemes as a card of controls, such as the reference,\n * turns it off and the card is named as a group instead.\n */\n heading?: boolean\n /** Hides a sole active required choice in the reference while preserving client controls. */\n hideSingleRequiredScheme?: boolean\n /** Creates a static disclosure that cannot be collapsed */\n isStatic?: boolean\n meta: AuthMeta\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n selectedSecurity: SelectedSecurity | undefined\n server: ServerObject | null\n title: string\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n /** Type of the document the schemes belong to, used to label the missing-type warning */\n documentType?: 'openapi' | 'asyncapi'\n}>()\n\nconst { translate, translations } = useLocalization()\n\nconst titleId = useId()\nconst comboboxButtonRef = ref<typeof ScalarButtonType | null>(null)\nconst isDisclosureOpen = ref(false)\n\nconst deleteModal = useModal()\nconst schemeToDelete = ref<{\n label: string\n payload: SecurityRequirementObject\n} | null>(null)\n\n/**\n * Determines if authentication is required or optional\n *\n * Auth is optional when there is an empty security requirement and no complex requirements.\n * Complex requirements have multiple auth schemes combined (e.g., API key + OAuth).\n */\nconst authIndicator = computed<{ icon: Icon; text: string } | null>(() => {\n if (!securityRequirements?.length) {\n return null\n }\n\n const isOptional = isAuthOptional(securityRequirements)\n\n return {\n icon: isOptional ? 'Unlock' : 'Lock',\n text: isOptional\n ? translate('apiClient.authSelector.optional')\n : translate('apiClient.authSelector.required'),\n }\n})\n\n/** All available auth scheme options for the dropdown */\nconst availableSchemeOptions = computed(() =>\n getSecuritySchemeOptions(\n securityRequirements ?? [],\n securitySchemes ?? {},\n selectedSecurity?.selectedSchemes ?? [],\n createAnySecurityScheme,\n translations.value.apiClient,\n ),\n)\n\n/** Currently active auth schemes selected for this operation or collection */\nconst activeSchemeOptions = computed<SecuritySchemeOption[]>(() => {\n const schemes = selectedSecurity?.selectedSchemes\n if (!schemes?.length) {\n return []\n }\n\n return schemes.flatMap((requirement) => {\n const schemeNames = Object.keys(requirement)\n\n if (schemeNames.length === 0) {\n return []\n }\n\n // Complex auth requirement with multiple schemes\n if (schemeNames.length > 1) {\n return formatComplexScheme(requirement)\n }\n\n // Simple auth requirement with single scheme\n const schemeName = schemeNames[0]\n if (!schemeName) {\n return []\n }\n\n const scheme = getResolvedRef(securitySchemes?.[schemeName])\n if (!scheme) {\n return []\n }\n\n return formatScheme({\n name: schemeName,\n value: requirement,\n })\n })\n})\n\n/** Keep selection available whenever it can change auth or recover an empty saved selection. */\nconst showSchemeSelector = computed<boolean>(() => {\n if (\n !hideSingleRequiredScheme ||\n createAnySecurityScheme ||\n securityRequirements?.length !== 1 ||\n Object.keys(securityRequirements[0] ?? {}).length === 0\n ) {\n return true\n }\n\n const schemeOptions = availableSchemeOptions.value.flatMap((option) =>\n 'options' in option ? option.options : [option],\n )\n\n return (\n schemeOptions.length !== 1 ||\n activeSchemeOptions.value.length !== 1 ||\n activeSchemeOptions.value[0]?.id !== schemeOptions[0]?.id\n )\n})\n\n/**\n * Opens the combobox dropdown when clicking the auth indicator badge.\n * Prevents the disclosure from toggling if it is already open.\n */\nconst handleAuthIndicatorClick = (event: Event): void => {\n if (!showSchemeSelector.value) {\n return\n }\n\n if (isDisclosureOpen.value) {\n event.stopPropagation()\n }\n comboboxButtonRef.value?.$el.click()\n}\n\n/**\n * Updates the selected auth schemes.\n * Separates existing schemes from newly created ones for the event payload.\n */\nconst handleSchemeSelection = (selected: SecuritySchemeOption[]): void => {\n const existingSchemes = selected\n .filter((option) => option.payload === undefined)\n .map((option) => unpackProxyObject(option.value, { depth: 2 }))\n\n const newSchemes = selected\n .filter((option) => option.payload !== undefined)\n .map((option) => ({\n name: option.label,\n scheme: option.payload!,\n }))\n\n eventBus.emit('auth:update:selected-security-schemes', {\n selectedRequirements: existingSchemes,\n newSchemes,\n meta,\n })\n}\n\n/** Shows the delete confirmation modal for the selected scheme */\nconst handleDeleteRequest = (option: {\n label: string\n value: SecurityRequirementObject\n}): void => {\n schemeToDelete.value = { label: option.label, payload: option.value }\n deleteModal.show()\n}\n\n/** Deletes the selected auth scheme after confirmation */\nconst handleDeleteConfirm = (): void => {\n if (!schemeToDelete.value) {\n return\n }\n\n eventBus.emit('auth:delete:security-scheme', {\n names: Object.keys(schemeToDelete.value.payload),\n })\n\n schemeToDelete.value = null\n deleteModal.hide()\n}\n\ndefineExpose({\n authIndicator,\n selectedSchemeOptions: activeSchemeOptions,\n schemeOptions: availableSchemeOptions,\n})\n</script>\n<template>\n <CollapsibleSection\n class=\"group/params relative\"\n :defaultOpen\n :heading\n :isStatic=\"isStatic\"\n :itemCount=\"activeSchemeOptions.length\"\n @update:modelValue=\"(open) => (isDisclosureOpen = open)\">\n <template #title>\n <div\n :id=\"titleId\"\n class=\"inline-flex items-center gap-0.5 leading-[20px]\">\n <span>{{ title }}</span>\n\n <span\n v-if=\"authIndicator\"\n class=\"text-c-3 -my-0.5 -mr-1 rounded px-1 py-0.5 leading-[normal] font-normal\"\n :class=\"{\n 'text-c-1': authIndicator.icon === 'Lock',\n 'hover:bg-b-3 hover:text-c-1 cursor-pointer': showSchemeSelector,\n }\"\n data-testid=\"auth-indicator\"\n @click=\"handleAuthIndicatorClick\">\n {{ authIndicator.text }}\n </span>\n </div>\n </template>\n\n <!-- The client keeps its selection and deletion controls. -->\n <template\n v-if=\"showSchemeSelector\"\n #actions>\n <ScalarComboboxMultiselect\n class=\"w-72 text-xs\"\n :modelValue=\"activeSchemeOptions\"\n multiple\n :options=\"availableSchemeOptions\"\n placement=\"bottom-end\"\n teleport\n @delete=\"handleDeleteRequest\"\n @update:modelValue=\"handleSchemeSelection\">\n <ScalarButton\n ref=\"comboboxButtonRef\"\n :aria-describedby=\"titleId\"\n class=\"group/combobox-button hover:text-c-1 text-c-2 flex h-fit w-full items-center gap-1 px-0.75 py-0.25 text-base font-normal\"\n variant=\"ghost\">\n <!-- Single auth scheme selected -->\n <template v-if=\"activeSchemeOptions.length === 1\">\n {{\n translate('apiClient.authSelector.selectedType', {\n type: activeSchemeOptions[0]?.label ?? '',\n })\n }}\n </template>\n\n <!-- Multiple auth schemes selected -->\n <template v-else-if=\"activeSchemeOptions.length > 1\">\n {{ translate('apiClient.authSelector.multipleTypes') }}\n </template>\n\n <!-- No auth schemes selected -->\n <template v-else>\n {{ translate('apiClient.authSelector.selectType') }}\n </template>\n\n <ScalarIconCaretDown\n class=\"size-3 shrink-0 transition-transform duration-100 group-aria-expanded/combobox-button:rotate-180\"\n weight=\"bold\" />\n </ScalarButton>\n\n <template #option=\"{ option, selected }\">\n <ScalarListboxCheckbox\n multiselect\n :selected=\"selected\" />\n <div class=\"min-w-0 flex-1 truncate\">\n {{ option.label }}\n </div>\n <ScalarIconButton\n v-if=\"option.isDeletable && canDeleteSchemes\"\n class=\"-m-0.5 shrink-0 p-0.5 opacity-0 group-hover/item:opacity-100\"\n :icon=\"ScalarIconTrash\"\n :label=\"\n translate('apiClient.authSelector.deleteScheme', {\n name: option.label,\n })\n \"\n size=\"xs\"\n @click.stop=\"handleDeleteRequest(option)\" />\n </template>\n </ScalarComboboxMultiselect>\n </template>\n\n <!-- Auth Table -->\n <RequestAuthDataTable\n :activeAuthIndex=\"selectedSecurity?.selectedIndex ?? 0\"\n :documentType\n :environment\n :eventBus\n :isStatic\n :meta\n :options\n :proxyUrl\n :securitySchemes\n :selectedSchemeOptions=\"activeSchemeOptions\"\n :server />\n\n <!-- Delete Auth Modal -->\n <DeleteRequestAuthModal\n v-if=\"schemeToDelete\"\n :label=\"schemeToDelete.label\"\n :scheme=\"schemeToDelete\"\n :state=\"deleteModal\"\n @close=\"deleteModal.hide()\"\n @delete=\"handleDeleteConfirm\" />\n </CollapsibleSection>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AuthSelector.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarButton,\n type ScalarButton as ScalarButtonType,\n} from '@scalar/components/button'\nimport { ScalarComboboxMultiselect } from '@scalar/components/combobox'\nimport type { Icon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarListboxCheckbox } from '@scalar/components/listbox'\nimport { useModal } from '@scalar/components/modal'\nimport { ScalarIconCaretDown, ScalarIconTrash } from '@scalar/icons'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n AuthMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n isAuthOptional,\n type MergedSecuritySchemes,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n OpenApiDocument,\n SecurityRequirementObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { computed, ref, useId } from 'vue'\n\nimport DeleteRequestAuthModal from '@/v2/blocks/scalar-auth-selector-block/components/DeleteRequestAuthModal.vue'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport {\n formatComplexScheme,\n formatScheme,\n getSecuritySchemeOptions,\n type SecuritySchemeOption,\n} from '@/v2/blocks/scalar-auth-selector-block/helpers/security-scheme'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport RequestAuthDataTable from './RequestAuthDataTable.vue'\n\nconst {\n environment,\n eventBus,\n createAnySecurityScheme = false,\n canDeleteSchemes = true,\n defaultOpen = true,\n heading = true,\n hideSingleRequiredScheme = false,\n isStatic = false,\n meta,\n proxyUrl,\n securityRequirements,\n securitySchemes,\n selectedSecurity,\n server,\n title,\n options,\n documentType,\n} = defineProps<{\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n /** Allows adding authentication which is not in the document */\n createAnySecurityScheme?: boolean\n /**\n * Whether schemes can be deleted from the selector. Enabled for the editable client, disabled in\n * the read-only reference where the schemes come from the document and cannot be removed.\n */\n canDeleteSchemes?: boolean\n /** Whether the authentication disclosure should start expanded */\n defaultOpen?: boolean\n /**\n * Whether the title belongs in the document outline. The client renders the\n * selector as one section of a request, so it is a heading there. A surface\n * that only offers the schemes as a card of controls, such as the reference,\n * turns it off and the card is named as a group instead.\n */\n heading?: boolean\n /** Hides a sole active required choice in the reference while preserving client controls. */\n hideSingleRequiredScheme?: boolean\n /** Creates a static disclosure that cannot be collapsed */\n isStatic?: boolean\n meta: AuthMeta\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n selectedSecurity: SelectedSecurity | undefined\n server: ServerObject | null\n title: string\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n /** Type of the document the schemes belong to, used to label the missing-type warning */\n documentType?: 'openapi' | 'asyncapi'\n}>()\n\nconst { translate, translations } = useLocalization()\n\nconst titleId = useId()\nconst comboboxButtonRef = ref<typeof ScalarButtonType | null>(null)\nconst isDisclosureOpen = ref(false)\n\nconst deleteModal = useModal()\nconst schemeToDelete = ref<{\n label: string\n payload: SecurityRequirementObject\n} | null>(null)\n\n/**\n * Determines if authentication is required or optional\n *\n * Auth is optional when there is an empty security requirement and no complex requirements.\n * Complex requirements have multiple auth schemes combined (e.g., API key + OAuth).\n */\nconst authIndicator = computed<{ icon: Icon; text: string } | null>(() => {\n if (!securityRequirements?.length) {\n return null\n }\n\n const isOptional = isAuthOptional(securityRequirements)\n\n return {\n icon: isOptional ? 'Unlock' : 'Lock',\n text: isOptional\n ? translate('apiClient.authSelector.optional')\n : translate('apiClient.authSelector.required'),\n }\n})\n\n/** All available auth scheme options for the dropdown */\nconst availableSchemeOptions = computed(() =>\n getSecuritySchemeOptions(\n securityRequirements ?? [],\n securitySchemes ?? {},\n selectedSecurity?.selectedSchemes ?? [],\n createAnySecurityScheme,\n translations.value.apiClient,\n ),\n)\n\n/** Currently active auth schemes selected for this operation or collection */\nconst activeSchemeOptions = computed<SecuritySchemeOption[]>(() => {\n const schemes = selectedSecurity?.selectedSchemes\n if (!schemes?.length) {\n return []\n }\n\n return schemes.flatMap((requirement) => {\n const schemeNames = Object.keys(requirement)\n\n if (schemeNames.length === 0) {\n return []\n }\n\n // Complex auth requirement with multiple schemes\n if (schemeNames.length > 1) {\n return formatComplexScheme(requirement)\n }\n\n // Simple auth requirement with single scheme\n const schemeName = schemeNames[0]\n if (!schemeName) {\n return []\n }\n\n const scheme = getResolvedRef(securitySchemes?.[schemeName])\n if (!scheme) {\n return []\n }\n\n return formatScheme({\n name: schemeName,\n value: requirement,\n })\n })\n})\n\n/** Keep selection available whenever it can change auth or recover an empty saved selection. */\nconst showSchemeSelector = computed<boolean>(() => {\n if (\n !hideSingleRequiredScheme ||\n createAnySecurityScheme ||\n securityRequirements?.length !== 1 ||\n Object.keys(securityRequirements[0] ?? {}).length === 0\n ) {\n return true\n }\n\n const schemeOptions = availableSchemeOptions.value.flatMap((option) =>\n 'options' in option ? option.options : [option],\n )\n\n return (\n schemeOptions.length !== 1 ||\n activeSchemeOptions.value.length !== 1 ||\n activeSchemeOptions.value[0]?.id !== schemeOptions[0]?.id\n )\n})\n\n/**\n * Opens the combobox dropdown when clicking the auth indicator badge.\n * Prevents the disclosure from toggling if it is already open.\n */\nconst handleAuthIndicatorClick = (event: Event): void => {\n if (!showSchemeSelector.value) {\n return\n }\n\n if (isDisclosureOpen.value) {\n event.stopPropagation()\n }\n comboboxButtonRef.value?.$el.click()\n}\n\n/**\n * Updates the selected auth schemes.\n * Separates existing schemes from newly created ones for the event payload.\n */\nconst handleSchemeSelection = (selected: SecuritySchemeOption[]): void => {\n const existingSchemes = selected\n .filter((option) => option.payload === undefined)\n .map((option) => unpackProxyObject(option.value, { depth: 2 }))\n\n const newSchemes = selected\n .filter((option) => option.payload !== undefined)\n .map((option) => ({\n name: option.label,\n scheme: option.payload!,\n }))\n\n eventBus.emit('auth:update:selected-security-schemes', {\n selectedRequirements: existingSchemes,\n newSchemes,\n meta,\n })\n}\n\n/** Shows the delete confirmation modal for the selected scheme */\nconst handleDeleteRequest = (option: {\n label: string\n value: SecurityRequirementObject\n}): void => {\n schemeToDelete.value = { label: option.label, payload: option.value }\n deleteModal.show()\n}\n\n/** Deletes the selected auth scheme after confirmation */\nconst handleDeleteConfirm = (): void => {\n if (!schemeToDelete.value) {\n return\n }\n\n eventBus.emit('auth:delete:security-scheme', {\n names: Object.keys(schemeToDelete.value.payload),\n })\n\n schemeToDelete.value = null\n deleteModal.hide()\n}\n\ndefineExpose({\n authIndicator,\n selectedSchemeOptions: activeSchemeOptions,\n schemeOptions: availableSchemeOptions,\n})\n</script>\n<template>\n <CollapsibleSection\n class=\"group/params relative\"\n :defaultOpen\n :heading\n :isStatic=\"isStatic\"\n :itemCount=\"activeSchemeOptions.length\"\n @update:modelValue=\"(open) => (isDisclosureOpen = open)\">\n <template #title>\n <div\n :id=\"titleId\"\n class=\"inline-flex items-center gap-0.5 leading-[20px]\">\n <span>{{ title }}</span>\n\n <span\n v-if=\"authIndicator\"\n class=\"text-c-3 -my-0.5 -mr-1 rounded px-1 py-0.5 leading-[normal] font-normal\"\n :class=\"{\n 'text-c-1': authIndicator.icon === 'Lock',\n 'hover:bg-b-3 hover:text-c-1 cursor-pointer': showSchemeSelector,\n }\"\n data-testid=\"auth-indicator\"\n @click=\"handleAuthIndicatorClick\">\n {{ authIndicator.text }}\n </span>\n </div>\n </template>\n\n <!-- The client keeps its selection and deletion controls. -->\n <template\n v-if=\"showSchemeSelector\"\n #actions>\n <ScalarComboboxMultiselect\n class=\"w-72 text-xs\"\n :modelValue=\"activeSchemeOptions\"\n multiple\n :options=\"availableSchemeOptions\"\n placement=\"bottom-end\"\n teleport\n @delete=\"handleDeleteRequest\"\n @update:modelValue=\"handleSchemeSelection\">\n <ScalarButton\n ref=\"comboboxButtonRef\"\n :aria-describedby=\"titleId\"\n class=\"group/combobox-button hover:text-c-1 text-c-2 flex h-fit w-full items-center gap-1 px-0.75 py-0.25 text-base font-normal\"\n variant=\"ghost\">\n <!-- Single auth scheme selected -->\n <template v-if=\"activeSchemeOptions.length === 1\">\n {{\n translate('apiClient.authSelector.selectedType', {\n type: activeSchemeOptions[0]?.label ?? '',\n })\n }}\n </template>\n\n <!-- Multiple auth schemes selected -->\n <template v-else-if=\"activeSchemeOptions.length > 1\">\n {{ translate('apiClient.authSelector.multipleTypes') }}\n </template>\n\n <!-- No auth schemes selected -->\n <template v-else>\n {{ translate('apiClient.authSelector.selectType') }}\n </template>\n\n <ScalarIconCaretDown\n class=\"size-3 shrink-0 transition-transform duration-100 group-aria-expanded/combobox-button:rotate-180\"\n weight=\"bold\" />\n </ScalarButton>\n\n <template #option=\"{ option, selected }\">\n <ScalarListboxCheckbox\n multiselect\n :selected=\"selected\" />\n <div class=\"min-w-0 flex-1 truncate\">\n {{ option.label }}\n </div>\n <ScalarIconButton\n v-if=\"option.isDeletable && canDeleteSchemes\"\n class=\"-m-0.5 shrink-0 p-0.5 opacity-0 group-hover/item:opacity-100\"\n :icon=\"ScalarIconTrash\"\n :label=\"\n translate('apiClient.authSelector.deleteScheme', {\n name: option.label,\n })\n \"\n size=\"xs\"\n @click.stop=\"handleDeleteRequest(option)\" />\n </template>\n </ScalarComboboxMultiselect>\n </template>\n\n <!-- Auth Table -->\n <RequestAuthDataTable\n :activeAuthIndex=\"selectedSecurity?.selectedIndex ?? 0\"\n :documentType\n :environment\n :eventBus\n :isStatic\n :meta\n :options\n :proxyUrl\n :securitySchemes\n :selectedSchemeOptions=\"activeSchemeOptions\"\n :server />\n\n <!-- Delete Auth Modal -->\n <DeleteRequestAuthModal\n v-if=\"schemeToDelete\"\n :label=\"schemeToDelete.label\"\n :scheme=\"schemeToDelete\"\n :state=\"deleteModal\"\n @close=\"deleteModal.hide()\"\n @delete=\"handleDeleteConfirm\" />\n </CollapsibleSection>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OAuth2.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OAuth2.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue"],"names":[],"mappings":"AA0tBA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AAIzE,OAAO,KAAK,EAEV,iBAAiB,EAClB,MAAM,gCAAgC,CAAA;AAKvC,OAAO,EAKL,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAChC,MAAM,yCAAyC,CAAA;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AAEnH,OAAO,KAAK,EAEV,YAAY,EACb,MAAM,8DAA8D,CAAA;AAIrE,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,sDAAsD,CAAA;AAS7D,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG,IAAI,CAC9B,sBAAsB,EACtB,mBAAmB,CACpB,GAAG;IACF,2GAA2G;IAC3G,WAAW,CAAC,EAAE,OAAO,KAAK,CAAA;IAC1B,0FAA0F;IAC1F,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;CAC9C,CAAA;wBACoB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IAEhB,wCAAwC;iBAC3B,kBAAkB;IAC/B,kBAAkB;WACX,sBAAsB;IAC7B,6BAA6B;UACvB,MAAM,sBAAsB;IAClC,sBAAsB;oBACN,MAAM,EAAE;IACxB,sBAAsB;YACd,0BAA0B;IAClC,mCAAmC;YAC3B,YAAY,GAAG,IAAI;IAC3B,gBAAgB;cACN,MAAM;IAChB,kCAAkC;UAC5B,MAAM;IACZ,2CAA2C;cACjC,iBAAiB;IAC3B,uDAAuD;cAC7C,aAAa;IACvB;;;;OAIG;kBACW,OAAO;;;iBAmBC,MAAM,EAAE;gBAAU,MAAM;mBAAa,OAAO;;;;;;;;;;;;;;;;IA5ClE,wCAAwC;iBAC3B,kBAAkB;IAC/B,kBAAkB;WACX,sBAAsB;IAC7B,6BAA6B;UACvB,MAAM,sBAAsB;IAClC,sBAAsB;oBACN,MAAM,EAAE;IACxB,sBAAsB;YACd,0BAA0B;IAClC,mCAAmC;YAC3B,YAAY,GAAG,IAAI;IAC3B,gBAAgB;cACN,MAAM;IAChB,kCAAkC;UAC5B,MAAM;IACZ,2CAA2C;cACjC,iBAAiB;IAC3B,uDAAuD;cAC7C,aAAa;IACvB;;;;OAIG;kBACW,OAAO;;;iBAmBC,MAAM,EAAE;gBAAU,MAAM;mBAAa,OAAO;;;;;;;;;;;;;;;kFAu2ChE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OAuth2.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue"],"sourcesContent":["<script lang=\"ts\">\n/** Any config options required for the OAuth2 flow */\nexport type OAuth2Options = Pick<\n ApiClientConfiguration,\n 'oauth2RedirectUri'\n> & {\n /** Optional fetch override (IPC-backed on desktop) used for token exchange, refresh, and OIDC discovery */\n customFetch?: typeof fetch\n /** Optional redirect capture (loopback-backed on desktop) for interactive OAuth2 flows */\n captureOAuth2Callback?: CaptureOAuth2Callback\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { useLoadingState } from '@scalar/components/loading'\nimport type { ApiClientConfiguration } from '@scalar/types/api-reference'\nimport { pkceOptions } from '@scalar/types/entities'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { SecretsOAuthFlows } from '@scalar/workspace-store/entities/auth'\nimport type {\n ApiReferenceEvents,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n getEnvironmentVariables,\n type OAuthFlowAuthorizationCodeSecret,\n type OAuthFlowClientCredentialsSecret,\n type OAuthFlowPasswordSecret,\n type OAuthFlowsObjectSecret,\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 { XScalarCredentialsLocation } from '@scalar/workspace-store/schemas/extensions/security/x-scalar-credentials-location'\nimport { type XusePkce } from '@scalar/workspace-store/schemas/extensions/security/x-use-pkce'\nimport type {\n OAuthFlow,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\n\nimport OAuthScopesInput from '@/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue'\nimport {\n authorizeOauth2,\n refreshOauth2Token,\n type CaptureOAuth2Callback,\n} from '@/v2/blocks/scalar-auth-selector-block/helpers/oauth'\nimport { resolveDefaultOAuth2RedirectUri } from '@/v2/blocks/scalar-auth-selector-block/helpers/resolve-default-oauth2-redirect-url'\nimport { DataTableRow } from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport type { DeviceAuthorizationPrompt } from '../helpers/oauth-device-authorization'\nimport RequestAuthDataTableInput from './RequestAuthDataTableInput.vue'\n\nconst {\n environment,\n flows,\n type,\n scheme,\n selectedScopes: selectedScopesProp,\n server,\n proxyUrl,\n eventBus,\n name,\n options = {},\n} = defineProps<{\n /** Current environment configuration */\n environment: XScalarEnvironment\n /** OAuth flows */\n flows: OAuthFlowsObjectSecret\n /** Type of the OAuth flow */\n type: keyof OAuthFlowsObjectSecret\n /** Selected scopes */\n selectedScopes: string[]\n /** Security scheme */\n scheme: SecuritySchemeObjectSecret\n /** Current server configuration */\n server: ServerObject | null\n /** Proxy URL */\n proxyUrl: string\n /** Name of the security scheme */\n name: string\n /** Event bus for authentication updates */\n eventBus: WorkspaceEventBus\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n /**\n * Hides the Authorize / Refresh / Clear actions. Used when the form is embedded as a\n * configuration-only view (e.g. behind a gear icon), so the host component owns the\n * authorize action and the tokens are routed to the scheme it chooses.\n */\n hideActions?: boolean\n}>()\n\nconst emits = defineEmits<{\n (\n e: 'update:selectedScopes',\n payload: { scopes?: string[]; scope?: string; selected?: boolean },\n ): void\n (\n e: 'upsert:scope',\n payload: Omit<ApiReferenceEvents['auth:upsert:scopes'], 'name'>,\n ): void\n (\n e: 'delete:scope',\n payload: Omit<ApiReferenceEvents['auth:delete:scopes'], 'name'>,\n ): void\n}>()\n\nconst devicePrompt = ref<DeviceAuthorizationPrompt | null>(null)\nlet deviceController: AbortController | undefined\nconst cancelDeviceAuthorization = (): void => {\n deviceController?.abort()\n devicePrompt.value = null\n}\nonBeforeUnmount(cancelDeviceAuthorization)\nwatch(() => [type, name, flows], cancelDeviceAuthorization)\n\nconst { translate } = useLocalization()\n\nconst loader = useLoadingState()\nconst { toast } = useToasts()\n\n/** The current OAuth flow based on the selected type */\nconst flow = computed(() => flows[type]!)\ntype NonImplicitFlow =\n | OAuthFlowPasswordSecret\n | OAuthFlowClientCredentialsSecret\n | OAuthFlowAuthorizationCodeSecret\n\n/** We filter selected scopes to only include scopes that are defined on this flow (own keys only). */\nconst selectedScopes = computed(() => {\n const definedScopes = flow.value.scopes\n if (!definedScopes) {\n return []\n }\n return selectedScopesProp.filter((scope) =>\n Object.hasOwn(definedScopes, scope),\n )\n})\n\n/**\n * Show the client secret whenever the flow supports one. PKCE and a client\n * secret are independent, so confidential clients can use both together\n * (see RFC 9700 Section 2.1.1).\n */\nconst showClientSecret = computed(\n () => 'x-scalar-secret-client-secret' in flow.value,\n)\n\nconst clientSecretValue = computed((): string => {\n const f = flow.value as { 'x-scalar-secret-client-secret'?: string }\n return f['x-scalar-secret-client-secret'] ?? ''\n})\n\n/** Updates the security scheme base */\nconst handleOauth2Update = (\n payload: Partial<OAuthFlow & XScalarCredentialsLocation> & {\n deviceAuthorizationUrl?: string\n },\n): void => {\n // OpenIdConnect uses the secrets update for all\n if (scheme.type === 'openIdConnect') {\n return handleOauth2SecretsUpdate(payload)\n }\n\n // Only OAuth2 schemes carry flows on the document; this component is never\n // rendered for the other scheme types.\n if (scheme.type !== 'oauth2') {\n return\n }\n\n eventBus.emit('auth:update:security-scheme', {\n payload: {\n type: scheme.type,\n flows: {\n [type]: payload,\n },\n },\n name,\n })\n}\n\n/** Updates the flow secrets */\nconst handleOauth2SecretsUpdate = (\n payload: Omit<Partial<SecretsOAuthFlows[keyof SecretsOAuthFlows]>, 'type'>,\n): void => {\n // This component only renders for oauth2/openIdConnect. Other scheme types (e.g. mutualTLS)\n // carry no flow secrets, so there is nothing to persist and no matching secrets payload type.\n if (scheme.type !== 'oauth2' && scheme.type !== 'openIdConnect') {\n return\n }\n\n eventBus.emit('auth:update:security-scheme-secrets', {\n payload: {\n type: scheme.type,\n [type]: payload,\n },\n name,\n })\n}\n\n/** Clears the flow secrets */\nconst clearOauth2Secrets = (): void => {\n if (loader.isLoading) {\n return\n }\n eventBus.emit('auth:clear:security-scheme-secrets', {\n name,\n })\n}\n\n/** Clears stored access and refresh tokens (authorized state) */\nconst handleClearAccessTokens = (): void => {\n if (loader.isLoading) {\n return\n }\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-token': '',\n 'x-scalar-secret-refresh-token': '',\n })\n}\n\n/** Track redirect URI prefill per flow instance to support document switching */\nconst prefilledFlowIdentity = ref<string | null>(null)\nconst hasHandledRedirectPrefill = ref(false)\n\nconst resolveFlowIdentity = (\n currentFlow: OAuthFlowsObjectSecret[keyof OAuthFlowsObjectSecret] | undefined,\n): string =>\n JSON.stringify({\n type,\n authorizationUrl:\n currentFlow && 'authorizationUrl' in currentFlow\n ? currentFlow.authorizationUrl\n : '',\n tokenUrl:\n currentFlow && 'tokenUrl' in currentFlow ? currentFlow.tokenUrl : '',\n refreshUrl: currentFlow?.refreshUrl ?? '',\n scopes: Object.keys(currentFlow?.scopes ?? {}),\n })\n\n/** Default the redirect-uri to the current origin if we have access to window */\nwatch(\n () => flow.value,\n (currentFlow) => {\n if (!currentFlow || !('x-scalar-secret-redirect-uri' in currentFlow)) {\n return\n }\n\n const flowIdentity = resolveFlowIdentity(currentFlow)\n if (prefilledFlowIdentity.value !== flowIdentity) {\n prefilledFlowIdentity.value = flowIdentity\n hasHandledRedirectPrefill.value = false\n }\n\n if (hasHandledRedirectPrefill.value) {\n return\n }\n\n const newRedirectUri = (currentFlow as OAuthFlowAuthorizationCodeSecret)[\n 'x-scalar-secret-redirect-uri'\n ]\n const defaultRedirectUri = resolveDefaultOAuth2RedirectUri(options)\n\n hasHandledRedirectPrefill.value = true\n\n // The desktop loopback path computes the redirect URI at authorize time (an\n // ephemeral 127.0.0.1 port), so persisting a default into the document would\n // only bake in a stale, unused value. Leave it empty and show a hint instead.\n if (\n newRedirectUri ||\n !defaultRedirectUri ||\n options.captureOAuth2Callback\n ) {\n return\n }\n\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-redirect-uri': defaultRedirectUri,\n })\n },\n { immediate: true },\n)\n\n/**\n * Authorizes the user using the specified OAuth flow.\n * Opens the appropriate OAuth dialog and/or performs the token exchange.\n */\nconst handleAuthorize = async (): Promise<void> => {\n if (loader.isLoading) {\n return\n }\n\n loader.start()\n const controller = new AbortController()\n deviceController = controller\n\n const [error, tokens] = await authorizeOauth2(\n flows,\n type,\n selectedScopes.value,\n server,\n proxyUrl,\n getEnvironmentVariables(environment),\n options.customFetch,\n options.captureOAuth2Callback,\n {\n signal: controller.signal,\n onPrompt: (prompt) => {\n devicePrompt.value = prompt\n },\n },\n )\n devicePrompt.value = null\n\n await loader.clear()\n if (controller.signal.aborted) {\n return\n }\n\n if (tokens?.accessToken) {\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-token': tokens.accessToken,\n ...(tokens.refreshToken\n ? { 'x-scalar-secret-refresh-token': tokens.refreshToken }\n : {}),\n })\n } else {\n console.error(error)\n toast(\n error?.message ?? translate('apiClient.oauth2.failedToauthorize'),\n 'error',\n )\n }\n}\n\n/** Refreshing requires a token issued by the provider, not just a compatible flow. */\nconst canRefreshToken = computed<boolean>(\n (): boolean =>\n type !== 'implicit' && Boolean(flow.value['x-scalar-secret-refresh-token']),\n)\n\n/**\n * Refresh URL placeholder, shows tokenUrl as hint if refreshUrl is not specified.\n * This helps users understand that tokenUrl will be used as fallback.\n */\nconst refreshUrlPlaceholder = computed(() => {\n if ('tokenUrl' in flow.value && flow.value.tokenUrl) {\n return flow.value.tokenUrl\n }\n return 'https://galaxy.scalar.com/oauth/refresh'\n})\n\n/**\n * Uses the stored refresh token to obtain a new access token\n * via grant_type=refresh_token.\n */\nconst handleRefresh = async (): Promise<void> => {\n // The explicit flow check narrows the type for refreshOauth2Token; the computed boolean cannot.\n if (loader.isLoading || type === 'implicit' || !canRefreshToken.value) {\n return\n }\n\n loader.start()\n\n const [error, tokens] = await refreshOauth2Token(\n flows,\n type,\n proxyUrl,\n server,\n getEnvironmentVariables(environment),\n options.customFetch,\n )\n\n await loader.clear()\n\n if (tokens?.accessToken) {\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-token': tokens.accessToken,\n ...(tokens.refreshToken\n ? { 'x-scalar-secret-refresh-token': tokens.refreshToken }\n : {}),\n })\n } else {\n console.error(error)\n toast(\n error?.message ?? translate('apiClient.oauth2.failedToRefreshToken'),\n 'error',\n )\n }\n}\n\n/** Updates the secret location */\nconst handleSecretLocationUpdate = (value: string): void => {\n const credentialsLocation = value === 'body' ? 'body' : 'header'\n\n if (scheme.type !== 'openIdConnect') {\n handleOauth2Update({\n 'x-scalar-credentials-location': credentialsLocation,\n })\n }\n\n handleOauth2SecretsUpdate({\n 'x-scalar-credentials-location': credentialsLocation,\n })\n}\n</script>\n\n<template>\n <!-- Access Token Display: Shows when user is already authorized -->\n <template v-if=\"Boolean(flow['x-scalar-secret-token'])\">\n <DataTableRow>\n <RequestAuthDataTableInput\n class=\"border-r-transparent\"\n :environment\n :modelValue=\"flow['x-scalar-secret-token']\"\n placeholder=\"QUxMIFlPVVIgQkFTRSBBUkUgQkVMT05HIFRPIFVT\"\n type=\"password\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-token': v })\n \">\n {{ translate('apiClient.oauth2.accessToken') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"canRefreshToken\">\n <RequestAuthDataTableInput\n class=\"border-r-transparent\"\n :environment\n :modelValue=\"flow.refreshUrl ?? ''\"\n :placeholder=\"refreshUrlPlaceholder\"\n @update:modelValue=\"(v) => handleOauth2Update({ refreshUrl: v })\">\n {{ translate('apiClient.oauth2.refreshUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow\n v-if=\"!hideActions\"\n class=\"min-w-full\">\n <div class=\"flex h-8 items-center justify-end gap-2 border-t\">\n <ScalarButton\n v-if=\"canRefreshToken\"\n class=\"p-0 px-2 py-0.5\"\n :loader\n size=\"sm\"\n variant=\"outlined\"\n @click=\"handleRefresh\">\n {{ translate('apiClient.oauth2.refresh') }}\n </ScalarButton>\n <ScalarButton\n class=\"mr-1 p-0 px-2 py-0.5\"\n :disabled=\"loader.isLoading\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"handleClearAccessTokens\">\n {{ translate('apiClient.oauth2.clear') }}\n </ScalarButton>\n </div>\n </DataTableRow>\n </template>\n\n <!-- Authorization Form: Shows when user needs to authorize -->\n <template v-else>\n <DataTableRow v-if=\"'deviceAuthorizationUrl' in flow\">\n <RequestAuthDataTableInput\n :environment\n :modelValue=\"flow.deviceAuthorizationUrl\"\n @update:modelValue=\"\n (v) => handleOauth2Update({ deviceAuthorizationUrl: v })\n \">\n {{ translate('apiClient.oauth2.deviceAuthorizationUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n <DataTableRow v-if=\"devicePrompt\">\n <div class=\"w-full p-2\">\n <div\n class=\"bg-b-2 flex flex-col gap-3 rounded-lg border p-3 text-sm\"\n role=\"status\">\n <p class=\"text-c-2 text-xs leading-normal\">\n {{ translate('apiClient.oauth2.deviceVerificationPrompt') }}\n </p>\n <strong\n class=\"bg-b-1 font-code w-fit max-w-full rounded border px-3 py-2 text-base font-medium tracking-widest break-all select-all\">\n {{ devicePrompt.userCode }}\n </strong>\n <a\n class=\"text-c-accent w-fit max-w-full text-xs break-all underline underline-offset-2\"\n :href=\"\n devicePrompt.verificationUriComplete ||\n devicePrompt.verificationUri\n \"\n rel=\"noopener noreferrer\"\n target=\"_blank\">\n {{ devicePrompt.verificationUri }}\n </a>\n <div class=\"flex items-center justify-between gap-3\">\n <p class=\"text-c-2 text-xs\">\n {{ translate('apiClient.oauth2.waitingForAuthorization') }}\n </p>\n <ScalarButton\n size=\"xs\"\n variant=\"outlined\"\n @click=\"cancelDeviceAuthorization\">\n {{ translate('apiClient.oauth2.cancelDeviceAuthorization') }}\n </ScalarButton>\n </div>\n </div>\n </div>\n </DataTableRow>\n <DataTableRow>\n <RequestAuthDataTableInput\n v-if=\"'authorizationUrl' in flow\"\n containerClass=\"border-r-0\"\n :environment\n :modelValue=\"flow['x-scalar-secret-auth-url'] ?? ''\"\n placeholder=\"https://galaxy.scalar.com/authorize\"\n @update:modelValue=\"\n (v) => {\n handleOauth2SecretsUpdate({ 'x-scalar-secret-auth-url': v })\n handleOauth2Update({ authorizationUrl: v })\n }\n \">\n {{ translate('apiClient.oauth2.authUrl') }}\n </RequestAuthDataTableInput>\n\n <RequestAuthDataTableInput\n v-if=\"'tokenUrl' in flow\"\n :environment\n :modelValue=\"flow['x-scalar-secret-token-url'] ?? ''\"\n placeholder=\"https://galaxy.scalar.com/token\"\n @update:modelValue=\"\n (v) => {\n handleOauth2SecretsUpdate({ 'x-scalar-secret-token-url': v })\n handleOauth2Update({ tokenUrl: v })\n }\n \">\n {{ translate('apiClient.oauth2.tokenUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"'x-scalar-secret-redirect-uri' in flow\">\n <RequestAuthDataTableInput\n :environment\n :modelValue=\"flow['x-scalar-secret-redirect-uri']\"\n :placeholder=\"\n options.captureOAuth2Callback\n ? `${resolveDefaultOAuth2RedirectUri(options) || 'http://127.0.0.1'} (handled automatically)`\n : translate('apiClient.oauth2.optionalRedirectUrl')\n \"\n @update:modelValue=\"\n (v) => {\n hasHandledRedirectPrefill = true\n handleOauth2SecretsUpdate({ 'x-scalar-secret-redirect-uri': v })\n }\n \">\n {{ translate('apiClient.oauth2.redirectUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <template\n v-if=\"\n 'x-scalar-secret-username' in flow && 'x-scalar-secret-password' in flow\n \">\n <DataTableRow>\n <RequestAuthDataTableInput\n class=\"text-c-2\"\n :environment\n :modelValue=\"flow['x-scalar-secret-username']\"\n placeholder=\"janedoe\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-username': v })\n \">\n {{ translate('apiClient.oauth2.username') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow>\n <RequestAuthDataTableInput\n :environment\n :modelValue=\"flow['x-scalar-secret-password']\"\n placeholder=\"********\"\n type=\"password\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-password': v })\n \">\n {{ translate('apiClient.oauth2.password') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n </template>\n\n <DataTableRow>\n <RequestAuthDataTableInput\n :environment\n :modelValue=\"flow['x-scalar-secret-client-id']\"\n placeholder=\"12345\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-client-id': v })\n \">\n {{ translate('apiClient.oauth2.clientID') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"showClientSecret\">\n <RequestAuthDataTableInput\n :environment\n :modelValue=\"clientSecretValue\"\n placeholder=\"XYZ123\"\n type=\"password\"\n @update:modelValue=\"\n (v) =>\n handleOauth2SecretsUpdate({ 'x-scalar-secret-client-secret': v })\n \">\n {{ translate('apiClient.oauth2.clientSecret') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"'x-usePkce' in flow\">\n <RequestAuthDataTableInput\n :enum=\"pkceOptions\"\n :environment\n :modelValue=\"flow['x-usePkce']\"\n readOnly\n @update:modelValue=\"\n (v) =>\n handleOauth2Update({\n 'x-usePkce': v as XusePkce['x-usePkce'],\n })\n \">\n {{ translate('apiClient.oauth2.usePKCE') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <!-- Credentials Location -->\n <DataTableRow v-if=\"type !== 'implicit'\">\n <RequestAuthDataTableInput\n :enum=\"['header', 'body']\"\n :environment\n :modelValue=\"\n (flow as NonImplicitFlow)['x-scalar-credentials-location'] || 'header'\n \"\n placeholder=\"header\"\n readOnly\n @update:modelValue=\"(v) => handleSecretLocationUpdate(v)\">\n {{ translate('apiClient.oauth2.credentialsLocation') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <!-- Scopes -->\n <DataTableRow>\n <OAuthScopesInput\n :flow\n :flowType=\"type\"\n :selectedScopes\n @delete:scope=\"(v) => emits('delete:scope', v)\"\n @update:selectedScopes=\"(v) => emits('update:selectedScopes', v)\"\n @upsert:scope=\"(v) => emits('upsert:scope', v)\" />\n </DataTableRow>\n\n <DataTableRow\n v-if=\"!hideActions\"\n class=\"min-w-full\">\n <div class=\"flex h-8 w-full items-center justify-end border-t\">\n <!-- Allow clearing the oauth section and going back to discovery -->\n <ScalarButton\n v-if=\"scheme.type === 'openIdConnect'\"\n class=\"mr-1 p-0 px-2 py-0.5\"\n :disabled=\"loader.isLoading\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"clearOauth2Secrets\">\n {{ translate('apiClient.oauth2.clear') }}\n </ScalarButton>\n\n <ScalarButton\n class=\"mr-0.75 p-0 px-2 py-0.5\"\n :loader\n size=\"sm\"\n variant=\"outlined\"\n @click=\"handleAuthorize\">\n {{ translate('apiClient.oauth2.authorize') }}\n </ScalarButton>\n </div>\n </DataTableRow>\n </template>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"OAuth2.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue"],"sourcesContent":["<script lang=\"ts\">\n/** Any config options required for the OAuth2 flow */\nexport type OAuth2Options = Pick<\n ApiClientConfiguration,\n 'oauth2RedirectUri'\n> & {\n /** Optional fetch override (IPC-backed on desktop) used for token exchange, refresh, and OIDC discovery */\n customFetch?: typeof fetch\n /** Optional redirect capture (loopback-backed on desktop) for interactive OAuth2 flows */\n captureOAuth2Callback?: CaptureOAuth2Callback\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { useLoadingState } from '@scalar/components/loading'\nimport type { ApiClientConfiguration } from '@scalar/types/api-reference'\nimport { pkceOptions } from '@scalar/types/entities'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { SecretsOAuthFlows } from '@scalar/workspace-store/entities/auth'\nimport type {\n ApiReferenceEvents,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n isSecretFieldCleared,\n type AuthSecretField,\n} from '@scalar/workspace-store/helpers/auth-secret-fields'\nimport {\n getEnvironmentVariables,\n type OAuthFlowAuthorizationCodeSecret,\n type OAuthFlowClientCredentialsSecret,\n type OAuthFlowPasswordSecret,\n type OAuthFlowsObjectSecret,\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 { XScalarCredentialsLocation } from '@scalar/workspace-store/schemas/extensions/security/x-scalar-credentials-location'\nimport type {\n OAuthFlow,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\n\nimport OAuthScopesInput from '@/v2/blocks/scalar-auth-selector-block/components/OAuthScopesInput.vue'\nimport {\n authorizeOauth2,\n refreshOauth2Token,\n type CaptureOAuth2Callback,\n} from '@/v2/blocks/scalar-auth-selector-block/helpers/oauth'\nimport { resolveDefaultOAuth2RedirectUri } from '@/v2/blocks/scalar-auth-selector-block/helpers/resolve-default-oauth2-redirect-url'\nimport { DataTableRow } from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport type { DeviceAuthorizationPrompt } from '../helpers/oauth-device-authorization'\nimport RequestAuthDataTableInput from './RequestAuthDataTableInput.vue'\n\nconst {\n environment,\n flows,\n type,\n scheme,\n selectedScopes: selectedScopesProp,\n server,\n proxyUrl,\n eventBus,\n name,\n options = {},\n} = defineProps<{\n /** Current environment configuration */\n environment: XScalarEnvironment\n /** OAuth flows */\n flows: OAuthFlowsObjectSecret\n /** Type of the OAuth flow */\n type: keyof OAuthFlowsObjectSecret\n /** Selected scopes */\n selectedScopes: string[]\n /** Security scheme */\n scheme: SecuritySchemeObjectSecret\n /** Current server configuration */\n server: ServerObject | null\n /** Proxy URL */\n proxyUrl: string\n /** Name of the security scheme */\n name: string\n /** Event bus for authentication updates */\n eventBus: WorkspaceEventBus\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n /**\n * Hides the Authorize / Refresh / Clear actions. Used when the form is embedded as a\n * configuration-only view (e.g. behind a gear icon), so the host component owns the\n * authorize action and the tokens are routed to the scheme it chooses.\n */\n hideActions?: boolean\n}>()\n\nconst emits = defineEmits<{\n (\n e: 'update:selectedScopes',\n payload: { scopes?: string[]; scope?: string; selected?: boolean },\n ): void\n (\n e: 'upsert:scope',\n payload: Omit<ApiReferenceEvents['auth:upsert:scopes'], 'name'>,\n ): void\n (\n e: 'delete:scope',\n payload: Omit<ApiReferenceEvents['auth:delete:scopes'], 'name'>,\n ): void\n}>()\n\nconst devicePrompt = ref<DeviceAuthorizationPrompt | null>(null)\nlet deviceController: AbortController | undefined\nconst cancelDeviceAuthorization = (): void => {\n deviceController?.abort()\n devicePrompt.value = null\n}\nonBeforeUnmount(cancelDeviceAuthorization)\nwatch(() => [type, name, flows], cancelDeviceAuthorization)\n\nconst { translate } = useLocalization()\n\nconst loader = useLoadingState()\nconst { toast } = useToasts()\n\n/** The current OAuth flow based on the selected type */\nconst flow = computed(() => flows[type]!)\ntype NonImplicitFlow =\n | OAuthFlowPasswordSecret\n | OAuthFlowClientCredentialsSecret\n | OAuthFlowAuthorizationCodeSecret\n\n/** We filter selected scopes to only include scopes that are defined on this flow (own keys only). */\nconst selectedScopes = computed(() => {\n const definedScopes = flow.value.scopes\n if (!definedScopes) {\n return []\n }\n return selectedScopesProp.filter((scope) =>\n Object.hasOwn(definedScopes, scope),\n )\n})\n\n/**\n * Show the client secret whenever the flow supports one. PKCE and a client\n * secret are independent, so confidential clients can use both together\n * (see RFC 9700 Section 2.1.1).\n */\nconst showClientSecret = computed(\n () => 'x-scalar-secret-client-secret' in flow.value,\n)\n\nconst clientSecretValue = computed((): string => {\n const f = flow.value as { 'x-scalar-secret-client-secret'?: string }\n return f['x-scalar-secret-client-secret'] ?? ''\n})\n\n/** Updates the security scheme base */\nconst handleOauth2Update = (\n payload: Partial<OAuthFlow & XScalarCredentialsLocation> & {\n deviceAuthorizationUrl?: string\n },\n): void => {\n // OpenIdConnect uses the secrets update for all\n if (scheme.type === 'openIdConnect') {\n return handleOauth2SecretsUpdate(payload)\n }\n\n // Only OAuth2 schemes carry flows on the document; this component is never\n // rendered for the other scheme types.\n if (scheme.type !== 'oauth2') {\n return\n }\n\n eventBus.emit('auth:update:security-scheme', {\n payload: {\n type: scheme.type,\n flows: {\n [type]: payload,\n },\n },\n name,\n })\n}\n\n/** Updates the flow secrets */\nconst handleOauth2SecretsUpdate = (\n payload: Omit<Partial<SecretsOAuthFlows[keyof SecretsOAuthFlows]>, 'type'>,\n): void => {\n // This component only renders for oauth2/openIdConnect. Other scheme types (e.g. mutualTLS)\n // carry no flow secrets, so there is nothing to persist and no matching secrets payload type.\n if (scheme.type !== 'oauth2' && scheme.type !== 'openIdConnect') {\n return\n }\n\n eventBus.emit('auth:update:security-scheme-secrets', {\n payload: {\n type: scheme.type,\n [type]: payload,\n },\n name,\n })\n}\n\n/** Reset only this field; redirect URI may need its browser default prefilled again. */\nconst handleResetSecret = (field: AuthSecretField): void => {\n if (field === 'x-scalar-secret-redirect-uri') {\n hasHandledRedirectPrefill.value = false\n }\n eventBus.emit('auth:reset:security-scheme-secret', {\n name,\n flow: type,\n field,\n })\n}\n\n/** Fixed-choice fields must never write an empty string into the document. */\nconst handlePkceUpdate = (value: string): void => {\n if (value === 'SHA-256' || value === 'plain' || value === 'no') {\n handleOauth2Update({ 'x-usePkce': value })\n }\n}\n\n/** Clears the flow secrets */\nconst clearOauth2Secrets = (): void => {\n if (loader.isLoading) {\n return\n }\n eventBus.emit('auth:clear:security-scheme-secrets', {\n name,\n })\n}\n\n/** Clears stored access and refresh tokens (authorized state) */\nconst handleClearAccessTokens = (): void => {\n if (loader.isLoading) {\n return\n }\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-token': '',\n 'x-scalar-secret-refresh-token': '',\n })\n}\n\n/** Track redirect URI prefill per flow instance to support document switching */\nconst prefilledFlowIdentity = ref<string | null>(null)\nconst hasHandledRedirectPrefill = ref(false)\n\nconst resolveFlowIdentity = (\n currentFlow: OAuthFlowsObjectSecret[keyof OAuthFlowsObjectSecret] | undefined,\n): string =>\n JSON.stringify({\n type,\n authorizationUrl:\n currentFlow && 'authorizationUrl' in currentFlow\n ? currentFlow.authorizationUrl\n : '',\n tokenUrl:\n currentFlow && 'tokenUrl' in currentFlow ? currentFlow.tokenUrl : '',\n refreshUrl: currentFlow?.refreshUrl ?? '',\n scopes: Object.keys(currentFlow?.scopes ?? {}),\n })\n\n/** Default the redirect-uri to the current origin if we have access to window */\nwatch(\n () => flow.value,\n (currentFlow) => {\n if (!currentFlow || !('x-scalar-secret-redirect-uri' in currentFlow)) {\n return\n }\n\n const flowIdentity = resolveFlowIdentity(currentFlow)\n if (prefilledFlowIdentity.value !== flowIdentity) {\n prefilledFlowIdentity.value = flowIdentity\n hasHandledRedirectPrefill.value = false\n }\n\n if (hasHandledRedirectPrefill.value) {\n return\n }\n\n const newRedirectUri = (currentFlow as OAuthFlowAuthorizationCodeSecret)[\n 'x-scalar-secret-redirect-uri'\n ]\n const defaultRedirectUri = resolveDefaultOAuth2RedirectUri(options)\n\n hasHandledRedirectPrefill.value = true\n\n // The desktop loopback path computes the redirect URI at authorize time (an\n // ephemeral 127.0.0.1 port), so persisting a default into the document would\n // only bake in a stale, unused value. Leave it empty and show a hint instead.\n if (\n isSecretFieldCleared(currentFlow, 'x-scalar-secret-redirect-uri') ||\n newRedirectUri ||\n !defaultRedirectUri ||\n options.captureOAuth2Callback\n ) {\n return\n }\n\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-redirect-uri': defaultRedirectUri,\n })\n },\n { immediate: true },\n)\n\n/**\n * Authorizes the user using the specified OAuth flow.\n * Opens the appropriate OAuth dialog and/or performs the token exchange.\n */\nconst handleAuthorize = async (): Promise<void> => {\n if (loader.isLoading) {\n return\n }\n\n loader.start()\n const controller = new AbortController()\n deviceController = controller\n\n const [error, tokens] = await authorizeOauth2(\n flows,\n type,\n selectedScopes.value,\n server,\n proxyUrl,\n getEnvironmentVariables(environment),\n options.customFetch,\n options.captureOAuth2Callback,\n {\n signal: controller.signal,\n onPrompt: (prompt) => {\n devicePrompt.value = prompt\n },\n },\n )\n devicePrompt.value = null\n\n await loader.clear()\n if (controller.signal.aborted) {\n return\n }\n\n if (tokens?.accessToken) {\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-token': tokens.accessToken,\n ...(tokens.refreshToken\n ? { 'x-scalar-secret-refresh-token': tokens.refreshToken }\n : {}),\n })\n } else {\n console.error(error)\n toast(\n error?.message ?? translate('apiClient.oauth2.failedToauthorize'),\n 'error',\n )\n }\n}\n\n/** Refreshing requires a token issued by the provider, not just a compatible flow. */\nconst canRefreshToken = computed<boolean>(\n (): boolean =>\n type !== 'implicit' && Boolean(flow.value['x-scalar-secret-refresh-token']),\n)\n\n/**\n * Refresh URL placeholder, shows tokenUrl as hint if refreshUrl is not specified.\n * This helps users understand that tokenUrl will be used as fallback.\n */\nconst refreshUrlPlaceholder = computed(() => {\n if ('tokenUrl' in flow.value && flow.value.tokenUrl) {\n return flow.value.tokenUrl\n }\n return 'https://galaxy.scalar.com/oauth/refresh'\n})\n\n/**\n * Uses the stored refresh token to obtain a new access token\n * via grant_type=refresh_token.\n */\nconst handleRefresh = async (): Promise<void> => {\n // The explicit flow check narrows the type for refreshOauth2Token; the computed boolean cannot.\n if (loader.isLoading || type === 'implicit' || !canRefreshToken.value) {\n return\n }\n\n loader.start()\n\n const [error, tokens] = await refreshOauth2Token(\n flows,\n type,\n proxyUrl,\n server,\n getEnvironmentVariables(environment),\n options.customFetch,\n )\n\n await loader.clear()\n\n if (tokens?.accessToken) {\n handleOauth2SecretsUpdate({\n 'x-scalar-secret-token': tokens.accessToken,\n ...(tokens.refreshToken\n ? { 'x-scalar-secret-refresh-token': tokens.refreshToken }\n : {}),\n })\n } else {\n console.error(error)\n toast(\n error?.message ?? translate('apiClient.oauth2.failedToRefreshToken'),\n 'error',\n )\n }\n}\n\n/** Updates the secret location */\nconst handleSecretLocationUpdate = (value: string): void => {\n if (value !== 'body' && value !== 'header') return\n const credentialsLocation = value\n\n if (scheme.type !== 'openIdConnect') {\n handleOauth2Update({\n 'x-scalar-credentials-location': credentialsLocation,\n })\n }\n\n handleOauth2SecretsUpdate({\n 'x-scalar-credentials-location': credentialsLocation,\n })\n}\n</script>\n\n<template>\n <!-- Access Token Display: Shows when user is already authorized -->\n <template\n v-if=\"\n Boolean(flow['x-scalar-secret-token']) ||\n isSecretFieldCleared(flow, 'x-scalar-secret-token')\n \">\n <DataTableRow>\n <RequestAuthDataTableInput\n canReset\n class=\"border-r-transparent\"\n :environment\n :modelValue=\"flow['x-scalar-secret-token']\"\n placeholder=\"QUxMIFlPVVIgQkFTRSBBUkUgQkVMT05HIFRPIFVT\"\n type=\"password\"\n @reset=\"handleResetSecret('x-scalar-secret-token')\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-token': v })\n \">\n {{ translate('apiClient.oauth2.accessToken') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"canRefreshToken\">\n <RequestAuthDataTableInput\n class=\"border-r-transparent\"\n :environment\n :modelValue=\"flow.refreshUrl ?? ''\"\n :placeholder=\"refreshUrlPlaceholder\"\n @update:modelValue=\"(v) => handleOauth2Update({ refreshUrl: v })\">\n {{ translate('apiClient.oauth2.refreshUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow\n v-if=\"!hideActions\"\n class=\"min-w-full\">\n <div class=\"flex h-8 items-center justify-end gap-2 border-t\">\n <ScalarButton\n v-if=\"canRefreshToken\"\n class=\"p-0 px-2 py-0.5\"\n :loader\n size=\"sm\"\n variant=\"outlined\"\n @click=\"handleRefresh\">\n {{ translate('apiClient.oauth2.refresh') }}\n </ScalarButton>\n <ScalarButton\n class=\"mr-1 p-0 px-2 py-0.5\"\n :disabled=\"loader.isLoading\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"handleClearAccessTokens\">\n {{ translate('apiClient.oauth2.clearTokens') }}\n </ScalarButton>\n </div>\n </DataTableRow>\n </template>\n\n <!-- Authorization Form: Shows when user needs to authorize -->\n <template v-else>\n <DataTableRow v-if=\"'deviceAuthorizationUrl' in flow\">\n <RequestAuthDataTableInput\n :environment\n :modelValue=\"flow.deviceAuthorizationUrl\"\n @update:modelValue=\"\n (v) => handleOauth2Update({ deviceAuthorizationUrl: v })\n \">\n {{ translate('apiClient.oauth2.deviceAuthorizationUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n <DataTableRow v-if=\"devicePrompt\">\n <div class=\"w-full p-2\">\n <div\n class=\"bg-b-2 flex flex-col gap-3 rounded-lg border p-3 text-sm\"\n role=\"status\">\n <p class=\"text-c-2 text-xs leading-normal\">\n {{ translate('apiClient.oauth2.deviceVerificationPrompt') }}\n </p>\n <strong\n class=\"bg-b-1 font-code w-fit max-w-full rounded border px-3 py-2 text-base font-medium tracking-widest break-all select-all\">\n {{ devicePrompt.userCode }}\n </strong>\n <a\n class=\"text-c-accent w-fit max-w-full text-xs break-all underline underline-offset-2\"\n :href=\"\n devicePrompt.verificationUriComplete ||\n devicePrompt.verificationUri\n \"\n rel=\"noopener noreferrer\"\n target=\"_blank\">\n {{ devicePrompt.verificationUri }}\n </a>\n <div class=\"flex items-center justify-between gap-3\">\n <p class=\"text-c-2 text-xs\">\n {{ translate('apiClient.oauth2.waitingForAuthorization') }}\n </p>\n <ScalarButton\n size=\"xs\"\n variant=\"outlined\"\n @click=\"cancelDeviceAuthorization\">\n {{ translate('apiClient.oauth2.cancelDeviceAuthorization') }}\n </ScalarButton>\n </div>\n </div>\n </div>\n </DataTableRow>\n <DataTableRow>\n <RequestAuthDataTableInput\n v-if=\"'authorizationUrl' in flow\"\n canReset\n containerClass=\"border-r-0\"\n :environment\n :modelValue=\"flow['x-scalar-secret-auth-url'] ?? ''\"\n placeholder=\"https://galaxy.scalar.com/authorize\"\n @reset=\"handleResetSecret('x-scalar-secret-auth-url')\"\n @update:modelValue=\"\n (v) => {\n handleOauth2SecretsUpdate({ 'x-scalar-secret-auth-url': v })\n if (v) handleOauth2Update({ authorizationUrl: v })\n }\n \">\n {{ translate('apiClient.oauth2.authUrl') }}\n </RequestAuthDataTableInput>\n\n <RequestAuthDataTableInput\n v-if=\"'tokenUrl' in flow\"\n canReset\n :environment\n :modelValue=\"flow['x-scalar-secret-token-url'] ?? ''\"\n placeholder=\"https://galaxy.scalar.com/token\"\n @reset=\"handleResetSecret('x-scalar-secret-token-url')\"\n @update:modelValue=\"\n (v) => {\n handleOauth2SecretsUpdate({ 'x-scalar-secret-token-url': v })\n if (v) handleOauth2Update({ tokenUrl: v })\n }\n \">\n {{ translate('apiClient.oauth2.tokenUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"'x-scalar-secret-redirect-uri' in flow\">\n <RequestAuthDataTableInput\n canReset\n :environment\n :modelValue=\"flow['x-scalar-secret-redirect-uri']\"\n :placeholder=\"\n options.captureOAuth2Callback\n ? `${resolveDefaultOAuth2RedirectUri(options) || 'http://127.0.0.1'} (handled automatically)`\n : translate('apiClient.oauth2.optionalRedirectUrl')\n \"\n @reset=\"handleResetSecret('x-scalar-secret-redirect-uri')\"\n @update:modelValue=\"\n (v) => {\n hasHandledRedirectPrefill = true\n handleOauth2SecretsUpdate({ 'x-scalar-secret-redirect-uri': v })\n }\n \">\n {{ translate('apiClient.oauth2.redirectUrl') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <template\n v-if=\"\n 'x-scalar-secret-username' in flow && 'x-scalar-secret-password' in flow\n \">\n <DataTableRow>\n <RequestAuthDataTableInput\n canReset\n class=\"text-c-2\"\n :environment\n :modelValue=\"flow['x-scalar-secret-username']\"\n placeholder=\"janedoe\"\n @reset=\"handleResetSecret('x-scalar-secret-username')\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-username': v })\n \">\n {{ translate('apiClient.oauth2.username') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow>\n <RequestAuthDataTableInput\n canReset\n :environment\n :modelValue=\"flow['x-scalar-secret-password']\"\n placeholder=\"********\"\n type=\"password\"\n @reset=\"handleResetSecret('x-scalar-secret-password')\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-password': v })\n \">\n {{ translate('apiClient.oauth2.password') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n </template>\n\n <DataTableRow>\n <RequestAuthDataTableInput\n canReset\n :environment\n :modelValue=\"flow['x-scalar-secret-client-id']\"\n placeholder=\"12345\"\n @reset=\"handleResetSecret('x-scalar-secret-client-id')\"\n @update:modelValue=\"\n (v) => handleOauth2SecretsUpdate({ 'x-scalar-secret-client-id': v })\n \">\n {{ translate('apiClient.oauth2.clientID') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"showClientSecret\">\n <RequestAuthDataTableInput\n canReset\n :environment\n :modelValue=\"clientSecretValue\"\n placeholder=\"XYZ123\"\n type=\"password\"\n @reset=\"handleResetSecret('x-scalar-secret-client-secret')\"\n @update:modelValue=\"\n (v) =>\n handleOauth2SecretsUpdate({ 'x-scalar-secret-client-secret': v })\n \">\n {{ translate('apiClient.oauth2.clientSecret') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <DataTableRow v-if=\"'x-usePkce' in flow\">\n <RequestAuthDataTableInput\n :enum=\"pkceOptions\"\n :environment\n :modelValue=\"flow['x-usePkce']\"\n readOnly\n @update:modelValue=\"handlePkceUpdate\">\n {{ translate('apiClient.oauth2.usePKCE') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <!-- Credentials Location -->\n <DataTableRow v-if=\"type !== 'implicit'\">\n <RequestAuthDataTableInput\n :enum=\"['header', 'body']\"\n :environment\n :modelValue=\"\n (flow as NonImplicitFlow)['x-scalar-credentials-location'] || 'header'\n \"\n placeholder=\"header\"\n readOnly\n @update:modelValue=\"(v) => handleSecretLocationUpdate(v)\">\n {{ translate('apiClient.oauth2.credentialsLocation') }}\n </RequestAuthDataTableInput>\n </DataTableRow>\n\n <!-- Scopes -->\n <DataTableRow>\n <OAuthScopesInput\n :flow\n :flowType=\"type\"\n :selectedScopes\n @delete:scope=\"(v) => emits('delete:scope', v)\"\n @update:selectedScopes=\"(v) => emits('update:selectedScopes', v)\"\n @upsert:scope=\"(v) => emits('upsert:scope', v)\" />\n </DataTableRow>\n\n <DataTableRow\n v-if=\"!hideActions\"\n class=\"min-w-full\">\n <div class=\"flex h-8 w-full items-center justify-end border-t\">\n <!-- Allow clearing the oauth section and going back to discovery -->\n <ScalarButton\n v-if=\"scheme.type === 'openIdConnect'\"\n class=\"mr-1 p-0 px-2 py-0.5\"\n :disabled=\"loader.isLoading\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"clearOauth2Secrets\">\n {{ translate('apiClient.oauth2.resetDiscovery') }}\n </ScalarButton>\n\n <ScalarButton\n class=\"mr-0.75 p-0 px-2 py-0.5\"\n :loader\n size=\"sm\"\n variant=\"outlined\"\n @click=\"handleAuthorize\">\n {{ translate('apiClient.oauth2.authorize') }}\n </ScalarButton>\n </div>\n </DataTableRow>\n </template>\n</template>\n"],"mappings":""}
|
|
@@ -10,6 +10,7 @@ import { getEnvironmentVariables } from "@scalar/workspace-store/request-example
|
|
|
10
10
|
import { useToasts } from "@scalar/use-toasts";
|
|
11
11
|
import { useLoadingState } from "@scalar/components/loading";
|
|
12
12
|
import { pkceOptions } from "@scalar/types/entities";
|
|
13
|
+
import { isSecretFieldCleared } from "@scalar/workspace-store/helpers/auth-secret-fields";
|
|
13
14
|
//#region src/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue?vue&type=script&setup=true&lang.ts
|
|
14
15
|
var _hoisted_1 = { class: "flex h-8 items-center justify-end gap-2 border-t" };
|
|
15
16
|
var _hoisted_2 = { class: "w-full p-2" };
|
|
@@ -99,6 +100,19 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
99
100
|
name: __props.name
|
|
100
101
|
});
|
|
101
102
|
};
|
|
103
|
+
/** Reset only this field; redirect URI may need its browser default prefilled again. */
|
|
104
|
+
const handleResetSecret = (field) => {
|
|
105
|
+
if (field === "x-scalar-secret-redirect-uri") hasHandledRedirectPrefill.value = false;
|
|
106
|
+
__props.eventBus.emit("auth:reset:security-scheme-secret", {
|
|
107
|
+
name: __props.name,
|
|
108
|
+
flow: __props.type,
|
|
109
|
+
field
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
/** Fixed-choice fields must never write an empty string into the document. */
|
|
113
|
+
const handlePkceUpdate = (value) => {
|
|
114
|
+
if (value === "SHA-256" || value === "plain" || value === "no") handleOauth2Update({ "x-usePkce": value });
|
|
115
|
+
};
|
|
102
116
|
/** Clears the flow secrets */
|
|
103
117
|
const clearOauth2Secrets = () => {
|
|
104
118
|
if (loader.isLoading) return;
|
|
@@ -134,7 +148,7 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
134
148
|
const newRedirectUri = currentFlow["x-scalar-secret-redirect-uri"];
|
|
135
149
|
const defaultRedirectUri = resolveDefaultOAuth2RedirectUri(__props.options);
|
|
136
150
|
hasHandledRedirectPrefill.value = true;
|
|
137
|
-
if (newRedirectUri || !defaultRedirectUri || __props.options.captureOAuth2Callback) return;
|
|
151
|
+
if (isSecretFieldCleared(currentFlow, "x-scalar-secret-redirect-uri") || newRedirectUri || !defaultRedirectUri || __props.options.captureOAuth2Callback) return;
|
|
138
152
|
handleOauth2SecretsUpdate({ "x-scalar-secret-redirect-uri": defaultRedirectUri });
|
|
139
153
|
}, { immediate: true });
|
|
140
154
|
/**
|
|
@@ -194,20 +208,23 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
194
208
|
};
|
|
195
209
|
/** Updates the secret location */
|
|
196
210
|
const handleSecretLocationUpdate = (value) => {
|
|
197
|
-
|
|
211
|
+
if (value !== "body" && value !== "header") return;
|
|
212
|
+
const credentialsLocation = value;
|
|
198
213
|
if (__props.scheme.type !== "openIdConnect") handleOauth2Update({ "x-scalar-credentials-location": credentialsLocation });
|
|
199
214
|
handleOauth2SecretsUpdate({ "x-scalar-credentials-location": credentialsLocation });
|
|
200
215
|
};
|
|
201
216
|
return (_ctx, _cache) => {
|
|
202
|
-
return Boolean(flow.value["x-scalar-secret-token"]) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
217
|
+
return Boolean(flow.value["x-scalar-secret-token"]) || unref(isSecretFieldCleared)(flow.value, "x-scalar-secret-token") ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
203
218
|
createVNode(unref(DataTableRow_default), null, {
|
|
204
219
|
default: withCtx(() => [createVNode(RequestAuthDataTableInput_default, {
|
|
220
|
+
canReset: "",
|
|
205
221
|
class: "border-r-transparent",
|
|
206
222
|
environment: __props.environment,
|
|
207
223
|
modelValue: flow.value["x-scalar-secret-token"],
|
|
208
224
|
placeholder: "QUxMIFlPVVIgQkFTRSBBUkUgQkVMT05HIFRPIFVT",
|
|
209
225
|
type: "password",
|
|
210
|
-
|
|
226
|
+
onReset: _cache[0] || (_cache[0] = ($event) => handleResetSecret("x-scalar-secret-token")),
|
|
227
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => handleOauth2SecretsUpdate({ "x-scalar-secret-token": v }))
|
|
211
228
|
}, {
|
|
212
229
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.accessToken")), 1)]),
|
|
213
230
|
_: 1
|
|
@@ -220,7 +237,7 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
220
237
|
environment: __props.environment,
|
|
221
238
|
modelValue: flow.value.refreshUrl ?? "",
|
|
222
239
|
placeholder: refreshUrlPlaceholder.value,
|
|
223
|
-
"onUpdate:modelValue": _cache[
|
|
240
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = (v) => handleOauth2Update({ refreshUrl: v }))
|
|
224
241
|
}, {
|
|
225
242
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.refreshUrl")), 1)]),
|
|
226
243
|
_: 1
|
|
@@ -252,7 +269,7 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
252
269
|
variant: "outlined",
|
|
253
270
|
onClick: handleClearAccessTokens
|
|
254
271
|
}, {
|
|
255
|
-
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.
|
|
272
|
+
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.clearTokens")), 1)]),
|
|
256
273
|
_: 1
|
|
257
274
|
}, 8, ["disabled"])])]),
|
|
258
275
|
_: 1
|
|
@@ -262,7 +279,7 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
262
279
|
default: withCtx(() => [createVNode(RequestAuthDataTableInput_default, {
|
|
263
280
|
environment: __props.environment,
|
|
264
281
|
modelValue: flow.value.deviceAuthorizationUrl,
|
|
265
|
-
"onUpdate:modelValue": _cache[
|
|
282
|
+
"onUpdate:modelValue": _cache[3] || (_cache[3] = (v) => handleOauth2Update({ deviceAuthorizationUrl: v }))
|
|
266
283
|
}, {
|
|
267
284
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.deviceAuthorizationUrl")), 1)]),
|
|
268
285
|
_: 1
|
|
@@ -293,25 +310,29 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
293
310
|
createVNode(unref(DataTableRow_default), null, {
|
|
294
311
|
default: withCtx(() => ["authorizationUrl" in flow.value ? (openBlock(), createBlock(RequestAuthDataTableInput_default, {
|
|
295
312
|
key: 0,
|
|
313
|
+
canReset: "",
|
|
296
314
|
containerClass: "border-r-0",
|
|
297
315
|
environment: __props.environment,
|
|
298
316
|
modelValue: flow.value["x-scalar-secret-auth-url"] ?? "",
|
|
299
317
|
placeholder: "https://galaxy.scalar.com/authorize",
|
|
300
|
-
|
|
318
|
+
onReset: _cache[4] || (_cache[4] = ($event) => handleResetSecret("x-scalar-secret-auth-url")),
|
|
319
|
+
"onUpdate:modelValue": _cache[5] || (_cache[5] = (v) => {
|
|
301
320
|
handleOauth2SecretsUpdate({ "x-scalar-secret-auth-url": v });
|
|
302
|
-
handleOauth2Update({ authorizationUrl: v });
|
|
321
|
+
if (v) handleOauth2Update({ authorizationUrl: v });
|
|
303
322
|
})
|
|
304
323
|
}, {
|
|
305
324
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.authUrl")), 1)]),
|
|
306
325
|
_: 1
|
|
307
326
|
}, 8, ["environment", "modelValue"])) : createCommentVNode("", true), "tokenUrl" in flow.value ? (openBlock(), createBlock(RequestAuthDataTableInput_default, {
|
|
308
327
|
key: 1,
|
|
328
|
+
canReset: "",
|
|
309
329
|
environment: __props.environment,
|
|
310
330
|
modelValue: flow.value["x-scalar-secret-token-url"] ?? "",
|
|
311
331
|
placeholder: "https://galaxy.scalar.com/token",
|
|
312
|
-
|
|
332
|
+
onReset: _cache[6] || (_cache[6] = ($event) => handleResetSecret("x-scalar-secret-token-url")),
|
|
333
|
+
"onUpdate:modelValue": _cache[7] || (_cache[7] = (v) => {
|
|
313
334
|
handleOauth2SecretsUpdate({ "x-scalar-secret-token-url": v });
|
|
314
|
-
handleOauth2Update({ tokenUrl: v });
|
|
335
|
+
if (v) handleOauth2Update({ tokenUrl: v });
|
|
315
336
|
})
|
|
316
337
|
}, {
|
|
317
338
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.tokenUrl")), 1)]),
|
|
@@ -321,10 +342,12 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
321
342
|
}),
|
|
322
343
|
"x-scalar-secret-redirect-uri" in flow.value ? (openBlock(), createBlock(unref(DataTableRow_default), { key: 2 }, {
|
|
323
344
|
default: withCtx(() => [createVNode(RequestAuthDataTableInput_default, {
|
|
345
|
+
canReset: "",
|
|
324
346
|
environment: __props.environment,
|
|
325
347
|
modelValue: flow.value["x-scalar-secret-redirect-uri"],
|
|
326
348
|
placeholder: __props.options.captureOAuth2Callback ? `${unref(resolveDefaultOAuth2RedirectUri)(__props.options) || "http://127.0.0.1"} (handled automatically)` : unref(translate)("apiClient.oauth2.optionalRedirectUrl"),
|
|
327
|
-
|
|
349
|
+
onReset: _cache[8] || (_cache[8] = ($event) => handleResetSecret("x-scalar-secret-redirect-uri")),
|
|
350
|
+
"onUpdate:modelValue": _cache[9] || (_cache[9] = (v) => {
|
|
328
351
|
hasHandledRedirectPrefill.value = true;
|
|
329
352
|
handleOauth2SecretsUpdate({ "x-scalar-secret-redirect-uri": v });
|
|
330
353
|
})
|
|
@@ -340,11 +363,13 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
340
363
|
})) : createCommentVNode("", true),
|
|
341
364
|
"x-scalar-secret-username" in flow.value && "x-scalar-secret-password" in flow.value ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [createVNode(unref(DataTableRow_default), null, {
|
|
342
365
|
default: withCtx(() => [createVNode(RequestAuthDataTableInput_default, {
|
|
366
|
+
canReset: "",
|
|
343
367
|
class: "text-c-2",
|
|
344
368
|
environment: __props.environment,
|
|
345
369
|
modelValue: flow.value["x-scalar-secret-username"],
|
|
346
370
|
placeholder: "janedoe",
|
|
347
|
-
|
|
371
|
+
onReset: _cache[10] || (_cache[10] = ($event) => handleResetSecret("x-scalar-secret-username")),
|
|
372
|
+
"onUpdate:modelValue": _cache[11] || (_cache[11] = (v) => handleOauth2SecretsUpdate({ "x-scalar-secret-username": v }))
|
|
348
373
|
}, {
|
|
349
374
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.username")), 1)]),
|
|
350
375
|
_: 1
|
|
@@ -352,11 +377,13 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
352
377
|
_: 1
|
|
353
378
|
}), createVNode(unref(DataTableRow_default), null, {
|
|
354
379
|
default: withCtx(() => [createVNode(RequestAuthDataTableInput_default, {
|
|
380
|
+
canReset: "",
|
|
355
381
|
environment: __props.environment,
|
|
356
382
|
modelValue: flow.value["x-scalar-secret-password"],
|
|
357
383
|
placeholder: "********",
|
|
358
384
|
type: "password",
|
|
359
|
-
|
|
385
|
+
onReset: _cache[12] || (_cache[12] = ($event) => handleResetSecret("x-scalar-secret-password")),
|
|
386
|
+
"onUpdate:modelValue": _cache[13] || (_cache[13] = (v) => handleOauth2SecretsUpdate({ "x-scalar-secret-password": v }))
|
|
360
387
|
}, {
|
|
361
388
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.password")), 1)]),
|
|
362
389
|
_: 1
|
|
@@ -365,10 +392,12 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
365
392
|
})], 64)) : createCommentVNode("", true),
|
|
366
393
|
createVNode(unref(DataTableRow_default), null, {
|
|
367
394
|
default: withCtx(() => [createVNode(RequestAuthDataTableInput_default, {
|
|
395
|
+
canReset: "",
|
|
368
396
|
environment: __props.environment,
|
|
369
397
|
modelValue: flow.value["x-scalar-secret-client-id"],
|
|
370
398
|
placeholder: "12345",
|
|
371
|
-
|
|
399
|
+
onReset: _cache[14] || (_cache[14] = ($event) => handleResetSecret("x-scalar-secret-client-id")),
|
|
400
|
+
"onUpdate:modelValue": _cache[15] || (_cache[15] = (v) => handleOauth2SecretsUpdate({ "x-scalar-secret-client-id": v }))
|
|
372
401
|
}, {
|
|
373
402
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.clientID")), 1)]),
|
|
374
403
|
_: 1
|
|
@@ -377,11 +406,13 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
377
406
|
}),
|
|
378
407
|
showClientSecret.value ? (openBlock(), createBlock(unref(DataTableRow_default), { key: 4 }, {
|
|
379
408
|
default: withCtx(() => [createVNode(RequestAuthDataTableInput_default, {
|
|
409
|
+
canReset: "",
|
|
380
410
|
environment: __props.environment,
|
|
381
411
|
modelValue: clientSecretValue.value,
|
|
382
412
|
placeholder: "XYZ123",
|
|
383
413
|
type: "password",
|
|
384
|
-
|
|
414
|
+
onReset: _cache[16] || (_cache[16] = ($event) => handleResetSecret("x-scalar-secret-client-secret")),
|
|
415
|
+
"onUpdate:modelValue": _cache[17] || (_cache[17] = (v) => handleOauth2SecretsUpdate({ "x-scalar-secret-client-secret": v }))
|
|
385
416
|
}, {
|
|
386
417
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.clientSecret")), 1)]),
|
|
387
418
|
_: 1
|
|
@@ -394,7 +425,7 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
394
425
|
environment: __props.environment,
|
|
395
426
|
modelValue: flow.value["x-usePkce"],
|
|
396
427
|
readOnly: "",
|
|
397
|
-
"onUpdate:modelValue":
|
|
428
|
+
"onUpdate:modelValue": handlePkceUpdate
|
|
398
429
|
}, {
|
|
399
430
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.usePKCE")), 1)]),
|
|
400
431
|
_: 1
|
|
@@ -412,7 +443,7 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
412
443
|
modelValue: flow.value["x-scalar-credentials-location"] || "header",
|
|
413
444
|
placeholder: "header",
|
|
414
445
|
readOnly: "",
|
|
415
|
-
"onUpdate:modelValue": _cache[
|
|
446
|
+
"onUpdate:modelValue": _cache[18] || (_cache[18] = (v) => handleSecretLocationUpdate(v))
|
|
416
447
|
}, {
|
|
417
448
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.credentialsLocation")), 1)]),
|
|
418
449
|
_: 1
|
|
@@ -424,9 +455,9 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
424
455
|
flow: flow.value,
|
|
425
456
|
flowType: __props.type,
|
|
426
457
|
selectedScopes: selectedScopes.value,
|
|
427
|
-
"onDelete:scope": _cache[
|
|
428
|
-
"onUpdate:selectedScopes": _cache[
|
|
429
|
-
"onUpsert:scope": _cache[
|
|
458
|
+
"onDelete:scope": _cache[19] || (_cache[19] = (v) => emits("delete:scope", v)),
|
|
459
|
+
"onUpdate:selectedScopes": _cache[20] || (_cache[20] = (v) => emits("update:selectedScopes", v)),
|
|
460
|
+
"onUpsert:scope": _cache[21] || (_cache[21] = (v) => emits("upsert:scope", v))
|
|
430
461
|
}, null, 8, [
|
|
431
462
|
"flow",
|
|
432
463
|
"flowType",
|
|
@@ -446,7 +477,7 @@ var OAuth2_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
446
477
|
variant: "outlined",
|
|
447
478
|
onClick: clearOauth2Secrets
|
|
448
479
|
}, {
|
|
449
|
-
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.
|
|
480
|
+
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("apiClient.oauth2.resetDiscovery")), 1)]),
|
|
450
481
|
_: 1
|
|
451
482
|
}, 8, ["disabled"])) : createCommentVNode("", true), createVNode(unref(ScalarButton), {
|
|
452
483
|
class: "mr-0.75 p-0 px-2 py-0.5",
|