@scalar/api-client 3.4.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/style.css +4 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +1 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.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/helpers/oauth-callback.d.ts +69 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.d.ts.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.js +75 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.js.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.d.ts +17 -0
- 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 +43 -44
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/app/app-events.js +1 -1
- package/dist/v2/features/app/app-events.js.map +1 -1
- package/dist/v2/features/app/app-state.d.ts.map +1 -1
- package/dist/v2/features/app/app-state.js +1 -1
- package/dist/v2/features/app/app-state.js.map +1 -1
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.d.ts.map +1 -1
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.js +2 -2
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.js.map +1 -1
- package/package.json +14 -13
- package/dist/v2/helpers/slugify.d.ts +0 -9
- package/dist/v2/helpers/slugify.d.ts.map +0 -1
- package/dist/v2/helpers/slugify.js +0 -15
- package/dist/v2/helpers/slugify.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @scalar/api-client
|
|
2
2
|
|
|
3
|
+
## 3.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#9049](https://github.com/scalar/scalar/pull/9049): Flush pending debounced operation edits before executing API client requests.
|
|
8
|
+
- [#9023](https://github.com/scalar/scalar/pull/9023): chore: use homemade slugger
|
|
9
|
+
|
|
10
|
+
## 3.5.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: support version switching for regsitry documents
|
|
15
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: ensure teams can have only one workspace and create default workspace of the team on demand
|
|
16
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): chore: migrate api-client from namespaces to teamSlug and convert team workspaces
|
|
17
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: add empty state for the team workspace
|
|
18
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: make breadcrumbs navigable, add workspace selector fallback, and support team logo slots
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: allow masking all temp urls
|
|
23
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): fix: hide sidebar group label when we have a single group
|
|
24
|
+
- [#9034](https://github.com/scalar/scalar/pull/9034): Surface actual OAuth error messages instead of generic failure messages
|
|
25
|
+
- [#9043](https://github.com/scalar/scalar/pull/9043): chore: move test documents to cdn
|
|
26
|
+
|
|
3
27
|
## 3.4.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OperationBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OperationBlock.vue.d.ts","sourceRoot":"","sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OperationBlock.vue.js","names":[],"sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * OperationBlock\n *\n * Orchestrates the operation view by wiring together the Header, OperationBlock,\n * and ResponseBlock. Forwards user interactions to the workspace event bus and\n * passes through configuration such as auth, servers, plugins, and environment.\n * This component keeps the Operation page lean by centralizing event emission\n * and prop wiring between the blocks.\n *\n * Notable behavior:\n * - Uses operation['x-scalar-method'] and operation['x-scalar-path'] to provide\n * draft overrides for the UI when present.\n */\nexport default {\n name: 'OperationBlock',\n}\n\nexport type OperationBlockProps = {\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Application version */\n appVersion: string\n /** Openapi document */\n document: OpenApiDocument\n /** Openapi document slug */\n documentSlug: string\n /** Workspace cookies */\n workspaceCookies: XScalarCookie[]\n /** Document cookies */\n documentCookies: XScalarCookie[]\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** HTTP clients */\n httpClients: AvailableClients\n /** The history for the operation */\n history?: HistoryEntry[]\n /** Client layout */\n layout: ClientLayout\n /** Currently selected server */\n server: ServerObject | null\n /** Currently selected client */\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** Meta information for the server */\n serverMeta: ServerMeta\n /** Hides the client button on the header */\n hideClientButton?: boolean\n /** Client integration */\n integration?: string | null\n /** Openapi document url for `modal` mode to open the client app */\n documentUrl?: string\n /** Client source */\n source?: 'gitbook' | 'api-reference'\n /** Operation object */\n operation: OperationObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Meta information for the auth update */\n authMeta: AuthMeta\n /** Document defined security schemes */\n securitySchemes: MergedSecuritySchemes\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Environment list */\n environments?: string[]\n /** Currently selected environment */\n activeEnvironment?: string\n /** For environment variables in the inputs */\n environment: XScalarEnvironment\n /** The proxy URL for sending requests */\n proxyUrl: string\n /** Currently selected security */\n selectedSecurity: SelectedSecurity\n /** Currently selected security schemes */\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n /** Security requirements */\n securityRequirements: OpenApiDocument['security']\n /** Default headers */\n defaultHeaders: Record<string, string>\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /** Subset of config options for the modal */\n options?: ModalProps['options']\n}\n</script>\n<script setup lang=\"ts\">\nimport { ERRORS } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport { buildSafeBodyRequest } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { executeHook, type ClientPlugin } from '@scalar/oas-utils/helpers'\nimport {\n AVAILABLE_CLIENTS,\n type AvailableClients,\n} from '@scalar/types/snippetz'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type { HistoryEntry } from '@scalar/workspace-store/entities/history/schema'\nimport type {\n AuthMeta,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n buildRequest,\n createVariablesStoreForRequest,\n getEnvironmentVariables,\n requestFactory,\n type MergedSecuritySchemes,\n type RequestPayload,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/operation'\nimport { computed, onBeforeUnmount, onMounted, ref, toValue, watch } from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport { harToFetchRequest } from '@/v2/blocks/operation-block/helpers/har-to-fetch-request'\nimport { harToFetchResponse } from '@/v2/blocks/operation-block/helpers/har-to-fetch-response'\nimport {\n getOperationExampleKey,\n isStreamingResponse,\n responseCache,\n} from '@/v2/blocks/operation-block/helpers/response-cache'\nimport {\n sendRequest,\n type ResponseInstance,\n} from '@/v2/blocks/operation-block/helpers/send-request'\nimport { validatePathParameters } from '@/v2/blocks/operation-block/helpers/validate-path-parameters'\nimport { generateClientOptions } from '@/v2/blocks/operation-code-sample'\nimport { RequestBlock } from '@/v2/blocks/request-block'\nimport { ResponseBlock } from '@/v2/blocks/response-block'\nimport { type History } from '@/v2/blocks/scalar-address-bar-block'\nimport type { ModalProps } from '@/v2/features/modal/Modal.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport Header from './components/Header.vue'\n\nconst {\n authMeta,\n environment,\n eventBus,\n exampleKey,\n document,\n documentSlug,\n workspaceCookies = [],\n documentCookies = [],\n hideClientButton,\n httpClients = AVAILABLE_CLIENTS,\n history = [],\n method,\n operation,\n path,\n plugins = [],\n proxyUrl,\n requestBodyCompositionSelection,\n securitySchemes,\n selectedClient,\n server,\n environments,\n options,\n activeEnvironment,\n serverMeta,\n selectedSecurity,\n selectedSecuritySchemes,\n securityRequirements,\n defaultHeaders,\n} = defineProps<OperationBlockProps>()\n\n/** Hoist up client generation so it doesn't get re-generated on every operation */\nconst clientOptions = computed(() => generateClientOptions(httpClients))\n\nconst { toast } = useToasts()\n\n// Refs\nconst abortController = ref<AbortController | null>(null)\nconst response = ref<ResponseInstance | null>(null)\nconst requestPayload = ref<RequestPayload | null>(null)\n\n/** Cancel the request */\nconst cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED)\n\n/** Execute the current operation example */\nconst handleExecute = async () => {\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast('Path parameters must have values.', 'error')\n return\n }\n\n const globalCookies = [...workspaceCookies, ...documentCookies]\n\n const { request: requestBuilder } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies,\n method,\n operation,\n path,\n proxyUrl,\n server,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n // Stop any previous streaming response\n if (response.value && 'reader' in response.value) {\n response.value.reader.cancel()\n }\n\n const variablesStore = createVariablesStoreForRequest()\n\n // Execute the beforeRequest hook (plugins receive RequestFactory, not fetch Request)\n await executeHook(\n {\n requestBuilder,\n document,\n operation,\n variablesStore,\n },\n 'beforeRequest',\n plugins,\n )\n\n const envVariables = {\n ...getEnvironmentVariables(environment),\n ...variablesStore.getVariables(),\n }\n\n // Build the fetch Request after hooks may have mutated the factory\n const requestResult = (() => {\n try {\n return {\n ok: true,\n result: buildRequest(requestBuilder, {\n envVariables,\n }),\n } as const\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n ok: false,\n error: message,\n } as const\n }\n })()\n\n if (requestResult.ok === false) {\n toast(requestResult.error, 'error')\n return\n }\n\n // Store the abort controller for cancellation\n abortController.value = requestResult.result.controller\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:sent', {\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n /** Execute the request */\n const [sendError, sendResult] = await sendRequest({\n isUsingProxy: requestResult.result.isUsingProxy,\n requestPayload: requestResult.result.requestPayload,\n plugins,\n customFetch: toValue(options)?.customFetch,\n })\n\n if (sendResult) {\n // Execute the responseReceived hook\n await executeHook(\n {\n response: sendResult.originalResponse.clone(),\n requestBuilder,\n request: buildSafeBodyRequest(...sendResult.requestPayload),\n document,\n operation,\n variablesStore,\n },\n 'responseReceived',\n plugins,\n )\n }\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:complete', {\n payload: sendResult\n ? {\n response: sendResult.originalResponse.clone(),\n requestPayload: sendResult.requestPayload,\n duration: sendResult.response.duration,\n timestamp: sendResult.timestamp,\n }\n : undefined,\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n if (sendError) {\n // clean up the response and request\n response.value = null\n requestPayload.value = null\n abortController.value = null\n\n toast(sendError.message, 'error')\n return\n }\n\n // Store the response\n response.value = sendResult.response\n requestPayload.value = sendResult.requestPayload\n\n // Cache non-streaming responses so they can be restored when navigating back\n if (!isStreamingResponse(sendResult.response)) {\n responseCache.set(getOperationExampleKey(method, path, exampleKey), {\n response: sendResult.response,\n requestPayload: sendResult.requestPayload,\n })\n }\n}\n\nonMounted(() => {\n eventBus.on('operation:send:request:hotkey', handleExecute)\n eventBus.on('operation:cancel:request', cancelRequest)\n})\nonBeforeUnmount(() => {\n eventBus.off('operation:send:request:hotkey', handleExecute)\n eventBus.off('operation:cancel:request', cancelRequest)\n})\n\nconst operationHistory = computed<History[]>(() =>\n history\n .map((entry) => ({\n method: entry.request.method as HttpMethodType,\n path: entry.request.url,\n duration: entry.time,\n status: entry.response.status,\n }))\n .reverse(),\n)\n\nconst handleSelectHistoryItem = ({ index }: { index: number }) => {\n const transformedIndex = (history.length ?? 0) - index - 1\n const historyItem = history[transformedIndex]\n if (!historyItem) {\n return\n }\n\n const navigate = () =>\n eventBus.emit('ui:navigate', {\n page: 'example',\n method,\n path,\n exampleName: 'draft',\n callback: (status) => {\n // Do not replace the response if the navigation was not successful\n if (status !== 'success') {\n return\n }\n // Reconstruct the response\n const fetchResponse = harToFetchResponse({\n harResponse: historyItem.response,\n url: historyItem.request.url,\n method,\n path,\n duration: historyItem.time,\n })\n\n // Reconstruct the request\n const fetchRequest = harToFetchRequest({\n harRequest: historyItem.request,\n })\n\n // Update the response and request\n response.value = fetchResponse\n requestPayload.value = fetchRequest\n },\n })\n\n eventBus.emit('operation:reload:history', {\n meta: {\n path,\n method,\n },\n index: transformedIndex,\n callback: navigate,\n })\n}\n\nconst handleNavigateSettings = () => {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'overview',\n operationPath: path,\n method,\n })\n}\n\n/**\n * When the path, method, or example key changes: save current response to\n * cache (so it can be restored when navigating back), then restore from cache\n * for the new operation or clear if no cached response. Response is only\n * cleared on page refresh or when making a new request for that operation.\n */\nwatch(\n [() => path, () => method, () => exampleKey],\n ([newPath, newMethod, newExampleKey]) => {\n const newKey = getOperationExampleKey(newMethod, newPath, newExampleKey)\n const cached = responseCache.get(newKey)\n if (cached) {\n response.value = cached.response\n requestPayload.value = cached.requestPayload\n } else {\n response.value = null\n requestPayload.value = null\n }\n\n // Cancel any in-flight request\n cancelRequest()\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n // We cancel the request if the component is unmounted\n cancelRequest()\n})\n</script>\n<template>\n <div class=\"bg-b-1 flex h-full flex-col\">\n <div\n class=\"lg:min-h-header flex w-full flex-wrap items-center justify-center p-2 lg:p-0\">\n <!-- Address Bar -->\n <Header\n :activeEnvironment\n :documentSlug\n :documentUrl\n :environment\n :environments\n :eventBus\n :exampleKey\n :hideClientButton\n :history=\"operationHistory\"\n :integration\n :layout\n :method\n :path\n :server\n :serverMeta\n :servers\n :source\n @execute=\"handleExecute\"\n @navigate:settings=\"handleNavigateSettings\"\n @select:history:item=\"handleSelectHistoryItem\" />\n </div>\n\n <ViewLayout class=\"border-t\">\n <ViewLayoutContent class=\"flex-1\">\n <!-- Request Section -->\n <RequestBlock\n :authMeta\n :clientOptions\n :defaultHeaders\n :documentCookies\n :environment\n :eventBus\n :exampleKey\n :layout\n :method\n :operation\n :options=\"toValue(options)\"\n :path\n :plugins\n :proxyUrl\n :requestBodyCompositionSelection\n :securityRequirements\n :securitySchemes\n :selectedClient\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :layout\n :plugins\n :requestPayload\n :response\n :totalPerformedRequests=\"operationHistory.length\" />\n </ViewLayoutContent>\n </ViewLayout>\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"OperationBlock.vue.js","names":[],"sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * OperationBlock\n *\n * Orchestrates the operation view by wiring together the Header, OperationBlock,\n * and ResponseBlock. Forwards user interactions to the workspace event bus and\n * passes through configuration such as auth, servers, plugins, and environment.\n * This component keeps the Operation page lean by centralizing event emission\n * and prop wiring between the blocks.\n *\n * Notable behavior:\n * - Uses operation['x-scalar-method'] and operation['x-scalar-path'] to provide\n * draft overrides for the UI when present.\n */\nexport default {\n name: 'OperationBlock',\n}\n\nexport type OperationBlockProps = {\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Application version */\n appVersion: string\n /** Openapi document */\n document: OpenApiDocument\n /** Openapi document slug */\n documentSlug: string\n /** Workspace cookies */\n workspaceCookies: XScalarCookie[]\n /** Document cookies */\n documentCookies: XScalarCookie[]\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** HTTP clients */\n httpClients: AvailableClients\n /** The history for the operation */\n history?: HistoryEntry[]\n /** Client layout */\n layout: ClientLayout\n /** Currently selected server */\n server: ServerObject | null\n /** Currently selected client */\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** Meta information for the server */\n serverMeta: ServerMeta\n /** Hides the client button on the header */\n hideClientButton?: boolean\n /** Client integration */\n integration?: string | null\n /** Openapi document url for `modal` mode to open the client app */\n documentUrl?: string\n /** Client source */\n source?: 'gitbook' | 'api-reference'\n /** Operation object */\n operation: OperationObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Meta information for the auth update */\n authMeta: AuthMeta\n /** Document defined security schemes */\n securitySchemes: MergedSecuritySchemes\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Environment list */\n environments?: string[]\n /** Currently selected environment */\n activeEnvironment?: string\n /** For environment variables in the inputs */\n environment: XScalarEnvironment\n /** The proxy URL for sending requests */\n proxyUrl: string\n /** Currently selected security */\n selectedSecurity: SelectedSecurity\n /** Currently selected security schemes */\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n /** Security requirements */\n securityRequirements: OpenApiDocument['security']\n /** Default headers */\n defaultHeaders: Record<string, string>\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /** Subset of config options for the modal */\n options?: ModalProps['options']\n}\n</script>\n<script setup lang=\"ts\">\nimport { ERRORS } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport { buildSafeBodyRequest } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { executeHook, type ClientPlugin } from '@scalar/oas-utils/helpers'\nimport {\n AVAILABLE_CLIENTS,\n type AvailableClients,\n} from '@scalar/types/snippetz'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type { HistoryEntry } from '@scalar/workspace-store/entities/history/schema'\nimport type {\n AuthMeta,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n buildRequest,\n createVariablesStoreForRequest,\n getEnvironmentVariables,\n requestFactory,\n type MergedSecuritySchemes,\n type RequestPayload,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/operation'\nimport { computed, onBeforeUnmount, onMounted, ref, toValue, watch } from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport { harToFetchRequest } from '@/v2/blocks/operation-block/helpers/har-to-fetch-request'\nimport { harToFetchResponse } from '@/v2/blocks/operation-block/helpers/har-to-fetch-response'\nimport {\n getOperationExampleKey,\n isStreamingResponse,\n responseCache,\n} from '@/v2/blocks/operation-block/helpers/response-cache'\nimport {\n sendRequest,\n type ResponseInstance,\n} from '@/v2/blocks/operation-block/helpers/send-request'\nimport { validatePathParameters } from '@/v2/blocks/operation-block/helpers/validate-path-parameters'\nimport { generateClientOptions } from '@/v2/blocks/operation-code-sample'\nimport { RequestBlock } from '@/v2/blocks/request-block'\nimport { ResponseBlock } from '@/v2/blocks/response-block'\nimport { type History } from '@/v2/blocks/scalar-address-bar-block'\nimport type { ModalProps } from '@/v2/features/modal/Modal.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport Header from './components/Header.vue'\n\nconst {\n authMeta,\n environment,\n eventBus,\n exampleKey,\n document,\n documentSlug,\n workspaceCookies = [],\n documentCookies = [],\n hideClientButton,\n httpClients = AVAILABLE_CLIENTS,\n history = [],\n method,\n operation,\n path,\n plugins = [],\n proxyUrl,\n requestBodyCompositionSelection,\n securitySchemes,\n selectedClient,\n server,\n environments,\n options,\n activeEnvironment,\n serverMeta,\n selectedSecurity,\n selectedSecuritySchemes,\n securityRequirements,\n defaultHeaders,\n} = defineProps<OperationBlockProps>()\n\n/** Hoist up client generation so it doesn't get re-generated on every operation */\nconst clientOptions = computed(() => generateClientOptions(httpClients))\n\nconst { toast } = useToasts()\n\n// Refs\nconst abortController = ref<AbortController | null>(null)\nconst response = ref<ResponseInstance | null>(null)\nconst requestPayload = ref<RequestPayload | null>(null)\n\n/** Cancel the request */\nconst cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED)\n\n/** Execute the current operation example */\nconst handleExecute = async () => {\n eventBus.flushDebouncedEmits?.()\n\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast('Path parameters must have values.', 'error')\n return\n }\n\n const globalCookies = [...workspaceCookies, ...documentCookies]\n\n const { request: requestBuilder } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies,\n method,\n operation,\n path,\n proxyUrl,\n server,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n // Stop any previous streaming response\n if (response.value && 'reader' in response.value) {\n response.value.reader.cancel()\n }\n\n const variablesStore = createVariablesStoreForRequest()\n\n // Execute the beforeRequest hook (plugins receive RequestFactory, not fetch Request)\n await executeHook(\n {\n requestBuilder,\n document,\n operation,\n variablesStore,\n },\n 'beforeRequest',\n plugins,\n )\n\n const envVariables = {\n ...getEnvironmentVariables(environment),\n ...variablesStore.getVariables(),\n }\n\n // Build the fetch Request after hooks may have mutated the factory\n const requestResult = (() => {\n try {\n return {\n ok: true,\n result: buildRequest(requestBuilder, {\n envVariables,\n }),\n } as const\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n ok: false,\n error: message,\n } as const\n }\n })()\n\n if (requestResult.ok === false) {\n toast(requestResult.error, 'error')\n return\n }\n\n // Store the abort controller for cancellation\n abortController.value = requestResult.result.controller\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:sent', {\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n /** Execute the request */\n const [sendError, sendResult] = await sendRequest({\n isUsingProxy: requestResult.result.isUsingProxy,\n requestPayload: requestResult.result.requestPayload,\n plugins,\n customFetch: toValue(options)?.customFetch,\n })\n\n if (sendResult) {\n // Execute the responseReceived hook\n await executeHook(\n {\n response: sendResult.originalResponse.clone(),\n requestBuilder,\n request: buildSafeBodyRequest(...sendResult.requestPayload),\n document,\n operation,\n variablesStore,\n },\n 'responseReceived',\n plugins,\n )\n }\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:complete', {\n payload: sendResult\n ? {\n response: sendResult.originalResponse.clone(),\n requestPayload: sendResult.requestPayload,\n duration: sendResult.response.duration,\n timestamp: sendResult.timestamp,\n }\n : undefined,\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n if (sendError) {\n // clean up the response and request\n response.value = null\n requestPayload.value = null\n abortController.value = null\n\n toast(sendError.message, 'error')\n return\n }\n\n // Store the response\n response.value = sendResult.response\n requestPayload.value = sendResult.requestPayload\n\n // Cache non-streaming responses so they can be restored when navigating back\n if (!isStreamingResponse(sendResult.response)) {\n responseCache.set(getOperationExampleKey(method, path, exampleKey), {\n response: sendResult.response,\n requestPayload: sendResult.requestPayload,\n })\n }\n}\n\nonMounted(() => {\n eventBus.on('operation:send:request:hotkey', handleExecute)\n eventBus.on('operation:cancel:request', cancelRequest)\n})\nonBeforeUnmount(() => {\n eventBus.off('operation:send:request:hotkey', handleExecute)\n eventBus.off('operation:cancel:request', cancelRequest)\n})\n\nconst operationHistory = computed<History[]>(() =>\n history\n .map((entry) => ({\n method: entry.request.method as HttpMethodType,\n path: entry.request.url,\n duration: entry.time,\n status: entry.response.status,\n }))\n .reverse(),\n)\n\nconst handleSelectHistoryItem = ({ index }: { index: number }) => {\n const transformedIndex = (history.length ?? 0) - index - 1\n const historyItem = history[transformedIndex]\n if (!historyItem) {\n return\n }\n\n const navigate = () =>\n eventBus.emit('ui:navigate', {\n page: 'example',\n method,\n path,\n exampleName: 'draft',\n callback: (status) => {\n // Do not replace the response if the navigation was not successful\n if (status !== 'success') {\n return\n }\n // Reconstruct the response\n const fetchResponse = harToFetchResponse({\n harResponse: historyItem.response,\n url: historyItem.request.url,\n method,\n path,\n duration: historyItem.time,\n })\n\n // Reconstruct the request\n const fetchRequest = harToFetchRequest({\n harRequest: historyItem.request,\n })\n\n // Update the response and request\n response.value = fetchResponse\n requestPayload.value = fetchRequest\n },\n })\n\n eventBus.emit('operation:reload:history', {\n meta: {\n path,\n method,\n },\n index: transformedIndex,\n callback: navigate,\n })\n}\n\nconst handleNavigateSettings = () => {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'overview',\n operationPath: path,\n method,\n })\n}\n\n/**\n * When the path, method, or example key changes: save current response to\n * cache (so it can be restored when navigating back), then restore from cache\n * for the new operation or clear if no cached response. Response is only\n * cleared on page refresh or when making a new request for that operation.\n */\nwatch(\n [() => path, () => method, () => exampleKey],\n ([newPath, newMethod, newExampleKey]) => {\n const newKey = getOperationExampleKey(newMethod, newPath, newExampleKey)\n const cached = responseCache.get(newKey)\n if (cached) {\n response.value = cached.response\n requestPayload.value = cached.requestPayload\n } else {\n response.value = null\n requestPayload.value = null\n }\n\n // Cancel any in-flight request\n cancelRequest()\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n // We cancel the request if the component is unmounted\n cancelRequest()\n})\n</script>\n<template>\n <div class=\"bg-b-1 flex h-full flex-col\">\n <div\n class=\"lg:min-h-header flex w-full flex-wrap items-center justify-center p-2 lg:p-0\">\n <!-- Address Bar -->\n <Header\n :activeEnvironment\n :documentSlug\n :documentUrl\n :environment\n :environments\n :eventBus\n :exampleKey\n :hideClientButton\n :history=\"operationHistory\"\n :integration\n :layout\n :method\n :path\n :server\n :serverMeta\n :servers\n :source\n @execute=\"handleExecute\"\n @navigate:settings=\"handleNavigateSettings\"\n @select:history:item=\"handleSelectHistoryItem\" />\n </div>\n\n <ViewLayout class=\"border-t\">\n <ViewLayoutContent class=\"flex-1\">\n <!-- Request Section -->\n <RequestBlock\n :authMeta\n :clientOptions\n :defaultHeaders\n :documentCookies\n :environment\n :eventBus\n :exampleKey\n :layout\n :method\n :operation\n :options=\"toValue(options)\"\n :path\n :plugins\n :proxyUrl\n :requestBodyCompositionSelection\n :securityRequirements\n :securitySchemes\n :selectedClient\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :layout\n :plugins\n :requestPayload\n :response\n :totalPerformedRequests=\"operationHistory.length\" />\n </ViewLayoutContent>\n </ViewLayout>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -69,6 +69,7 @@ var OperationBlock_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */
|
|
|
69
69
|
const cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED);
|
|
70
70
|
/** Execute the current operation example */
|
|
71
71
|
const handleExecute = async () => {
|
|
72
|
+
__props.eventBus.flushDebouncedEmits?.();
|
|
72
73
|
if (validatePathParameters(__props.operation.parameters ?? [], __props.exampleKey).ok === false) {
|
|
73
74
|
toast("Path parameters must have values.", "error");
|
|
74
75
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OperationBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * OperationBlock\n *\n * Orchestrates the operation view by wiring together the Header, OperationBlock,\n * and ResponseBlock. Forwards user interactions to the workspace event bus and\n * passes through configuration such as auth, servers, plugins, and environment.\n * This component keeps the Operation page lean by centralizing event emission\n * and prop wiring between the blocks.\n *\n * Notable behavior:\n * - Uses operation['x-scalar-method'] and operation['x-scalar-path'] to provide\n * draft overrides for the UI when present.\n */\nexport default {\n name: 'OperationBlock',\n}\n\nexport type OperationBlockProps = {\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Application version */\n appVersion: string\n /** Openapi document */\n document: OpenApiDocument\n /** Openapi document slug */\n documentSlug: string\n /** Workspace cookies */\n workspaceCookies: XScalarCookie[]\n /** Document cookies */\n documentCookies: XScalarCookie[]\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** HTTP clients */\n httpClients: AvailableClients\n /** The history for the operation */\n history?: HistoryEntry[]\n /** Client layout */\n layout: ClientLayout\n /** Currently selected server */\n server: ServerObject | null\n /** Currently selected client */\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** Meta information for the server */\n serverMeta: ServerMeta\n /** Hides the client button on the header */\n hideClientButton?: boolean\n /** Client integration */\n integration?: string | null\n /** Openapi document url for `modal` mode to open the client app */\n documentUrl?: string\n /** Client source */\n source?: 'gitbook' | 'api-reference'\n /** Operation object */\n operation: OperationObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Meta information for the auth update */\n authMeta: AuthMeta\n /** Document defined security schemes */\n securitySchemes: MergedSecuritySchemes\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Environment list */\n environments?: string[]\n /** Currently selected environment */\n activeEnvironment?: string\n /** For environment variables in the inputs */\n environment: XScalarEnvironment\n /** The proxy URL for sending requests */\n proxyUrl: string\n /** Currently selected security */\n selectedSecurity: SelectedSecurity\n /** Currently selected security schemes */\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n /** Security requirements */\n securityRequirements: OpenApiDocument['security']\n /** Default headers */\n defaultHeaders: Record<string, string>\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /** Subset of config options for the modal */\n options?: ModalProps['options']\n}\n</script>\n<script setup lang=\"ts\">\nimport { ERRORS } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport { buildSafeBodyRequest } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { executeHook, type ClientPlugin } from '@scalar/oas-utils/helpers'\nimport {\n AVAILABLE_CLIENTS,\n type AvailableClients,\n} from '@scalar/types/snippetz'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type { HistoryEntry } from '@scalar/workspace-store/entities/history/schema'\nimport type {\n AuthMeta,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n buildRequest,\n createVariablesStoreForRequest,\n getEnvironmentVariables,\n requestFactory,\n type MergedSecuritySchemes,\n type RequestPayload,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/operation'\nimport { computed, onBeforeUnmount, onMounted, ref, toValue, watch } from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport { harToFetchRequest } from '@/v2/blocks/operation-block/helpers/har-to-fetch-request'\nimport { harToFetchResponse } from '@/v2/blocks/operation-block/helpers/har-to-fetch-response'\nimport {\n getOperationExampleKey,\n isStreamingResponse,\n responseCache,\n} from '@/v2/blocks/operation-block/helpers/response-cache'\nimport {\n sendRequest,\n type ResponseInstance,\n} from '@/v2/blocks/operation-block/helpers/send-request'\nimport { validatePathParameters } from '@/v2/blocks/operation-block/helpers/validate-path-parameters'\nimport { generateClientOptions } from '@/v2/blocks/operation-code-sample'\nimport { RequestBlock } from '@/v2/blocks/request-block'\nimport { ResponseBlock } from '@/v2/blocks/response-block'\nimport { type History } from '@/v2/blocks/scalar-address-bar-block'\nimport type { ModalProps } from '@/v2/features/modal/Modal.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport Header from './components/Header.vue'\n\nconst {\n authMeta,\n environment,\n eventBus,\n exampleKey,\n document,\n documentSlug,\n workspaceCookies = [],\n documentCookies = [],\n hideClientButton,\n httpClients = AVAILABLE_CLIENTS,\n history = [],\n method,\n operation,\n path,\n plugins = [],\n proxyUrl,\n requestBodyCompositionSelection,\n securitySchemes,\n selectedClient,\n server,\n environments,\n options,\n activeEnvironment,\n serverMeta,\n selectedSecurity,\n selectedSecuritySchemes,\n securityRequirements,\n defaultHeaders,\n} = defineProps<OperationBlockProps>()\n\n/** Hoist up client generation so it doesn't get re-generated on every operation */\nconst clientOptions = computed(() => generateClientOptions(httpClients))\n\nconst { toast } = useToasts()\n\n// Refs\nconst abortController = ref<AbortController | null>(null)\nconst response = ref<ResponseInstance | null>(null)\nconst requestPayload = ref<RequestPayload | null>(null)\n\n/** Cancel the request */\nconst cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED)\n\n/** Execute the current operation example */\nconst handleExecute = async () => {\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast('Path parameters must have values.', 'error')\n return\n }\n\n const globalCookies = [...workspaceCookies, ...documentCookies]\n\n const { request: requestBuilder } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies,\n method,\n operation,\n path,\n proxyUrl,\n server,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n // Stop any previous streaming response\n if (response.value && 'reader' in response.value) {\n response.value.reader.cancel()\n }\n\n const variablesStore = createVariablesStoreForRequest()\n\n // Execute the beforeRequest hook (plugins receive RequestFactory, not fetch Request)\n await executeHook(\n {\n requestBuilder,\n document,\n operation,\n variablesStore,\n },\n 'beforeRequest',\n plugins,\n )\n\n const envVariables = {\n ...getEnvironmentVariables(environment),\n ...variablesStore.getVariables(),\n }\n\n // Build the fetch Request after hooks may have mutated the factory\n const requestResult = (() => {\n try {\n return {\n ok: true,\n result: buildRequest(requestBuilder, {\n envVariables,\n }),\n } as const\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n ok: false,\n error: message,\n } as const\n }\n })()\n\n if (requestResult.ok === false) {\n toast(requestResult.error, 'error')\n return\n }\n\n // Store the abort controller for cancellation\n abortController.value = requestResult.result.controller\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:sent', {\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n /** Execute the request */\n const [sendError, sendResult] = await sendRequest({\n isUsingProxy: requestResult.result.isUsingProxy,\n requestPayload: requestResult.result.requestPayload,\n plugins,\n customFetch: toValue(options)?.customFetch,\n })\n\n if (sendResult) {\n // Execute the responseReceived hook\n await executeHook(\n {\n response: sendResult.originalResponse.clone(),\n requestBuilder,\n request: buildSafeBodyRequest(...sendResult.requestPayload),\n document,\n operation,\n variablesStore,\n },\n 'responseReceived',\n plugins,\n )\n }\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:complete', {\n payload: sendResult\n ? {\n response: sendResult.originalResponse.clone(),\n requestPayload: sendResult.requestPayload,\n duration: sendResult.response.duration,\n timestamp: sendResult.timestamp,\n }\n : undefined,\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n if (sendError) {\n // clean up the response and request\n response.value = null\n requestPayload.value = null\n abortController.value = null\n\n toast(sendError.message, 'error')\n return\n }\n\n // Store the response\n response.value = sendResult.response\n requestPayload.value = sendResult.requestPayload\n\n // Cache non-streaming responses so they can be restored when navigating back\n if (!isStreamingResponse(sendResult.response)) {\n responseCache.set(getOperationExampleKey(method, path, exampleKey), {\n response: sendResult.response,\n requestPayload: sendResult.requestPayload,\n })\n }\n}\n\nonMounted(() => {\n eventBus.on('operation:send:request:hotkey', handleExecute)\n eventBus.on('operation:cancel:request', cancelRequest)\n})\nonBeforeUnmount(() => {\n eventBus.off('operation:send:request:hotkey', handleExecute)\n eventBus.off('operation:cancel:request', cancelRequest)\n})\n\nconst operationHistory = computed<History[]>(() =>\n history\n .map((entry) => ({\n method: entry.request.method as HttpMethodType,\n path: entry.request.url,\n duration: entry.time,\n status: entry.response.status,\n }))\n .reverse(),\n)\n\nconst handleSelectHistoryItem = ({ index }: { index: number }) => {\n const transformedIndex = (history.length ?? 0) - index - 1\n const historyItem = history[transformedIndex]\n if (!historyItem) {\n return\n }\n\n const navigate = () =>\n eventBus.emit('ui:navigate', {\n page: 'example',\n method,\n path,\n exampleName: 'draft',\n callback: (status) => {\n // Do not replace the response if the navigation was not successful\n if (status !== 'success') {\n return\n }\n // Reconstruct the response\n const fetchResponse = harToFetchResponse({\n harResponse: historyItem.response,\n url: historyItem.request.url,\n method,\n path,\n duration: historyItem.time,\n })\n\n // Reconstruct the request\n const fetchRequest = harToFetchRequest({\n harRequest: historyItem.request,\n })\n\n // Update the response and request\n response.value = fetchResponse\n requestPayload.value = fetchRequest\n },\n })\n\n eventBus.emit('operation:reload:history', {\n meta: {\n path,\n method,\n },\n index: transformedIndex,\n callback: navigate,\n })\n}\n\nconst handleNavigateSettings = () => {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'overview',\n operationPath: path,\n method,\n })\n}\n\n/**\n * When the path, method, or example key changes: save current response to\n * cache (so it can be restored when navigating back), then restore from cache\n * for the new operation or clear if no cached response. Response is only\n * cleared on page refresh or when making a new request for that operation.\n */\nwatch(\n [() => path, () => method, () => exampleKey],\n ([newPath, newMethod, newExampleKey]) => {\n const newKey = getOperationExampleKey(newMethod, newPath, newExampleKey)\n const cached = responseCache.get(newKey)\n if (cached) {\n response.value = cached.response\n requestPayload.value = cached.requestPayload\n } else {\n response.value = null\n requestPayload.value = null\n }\n\n // Cancel any in-flight request\n cancelRequest()\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n // We cancel the request if the component is unmounted\n cancelRequest()\n})\n</script>\n<template>\n <div class=\"bg-b-1 flex h-full flex-col\">\n <div\n class=\"lg:min-h-header flex w-full flex-wrap items-center justify-center p-2 lg:p-0\">\n <!-- Address Bar -->\n <Header\n :activeEnvironment\n :documentSlug\n :documentUrl\n :environment\n :environments\n :eventBus\n :exampleKey\n :hideClientButton\n :history=\"operationHistory\"\n :integration\n :layout\n :method\n :path\n :server\n :serverMeta\n :servers\n :source\n @execute=\"handleExecute\"\n @navigate:settings=\"handleNavigateSettings\"\n @select:history:item=\"handleSelectHistoryItem\" />\n </div>\n\n <ViewLayout class=\"border-t\">\n <ViewLayoutContent class=\"flex-1\">\n <!-- Request Section -->\n <RequestBlock\n :authMeta\n :clientOptions\n :defaultHeaders\n :documentCookies\n :environment\n :eventBus\n :exampleKey\n :layout\n :method\n :operation\n :options=\"toValue(options)\"\n :path\n :plugins\n :proxyUrl\n :requestBodyCompositionSelection\n :securityRequirements\n :securitySchemes\n :selectedClient\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :layout\n :plugins\n :requestPayload\n :response\n :totalPerformedRequests=\"operationHistory.length\" />\n </ViewLayoutContent>\n </ViewLayout>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OperationBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/operation-block/OperationBlock.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * OperationBlock\n *\n * Orchestrates the operation view by wiring together the Header, OperationBlock,\n * and ResponseBlock. Forwards user interactions to the workspace event bus and\n * passes through configuration such as auth, servers, plugins, and environment.\n * This component keeps the Operation page lean by centralizing event emission\n * and prop wiring between the blocks.\n *\n * Notable behavior:\n * - Uses operation['x-scalar-method'] and operation['x-scalar-path'] to provide\n * draft overrides for the UI when present.\n */\nexport default {\n name: 'OperationBlock',\n}\n\nexport type OperationBlockProps = {\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Application version */\n appVersion: string\n /** Openapi document */\n document: OpenApiDocument\n /** Openapi document slug */\n documentSlug: string\n /** Workspace cookies */\n workspaceCookies: XScalarCookie[]\n /** Document cookies */\n documentCookies: XScalarCookie[]\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** HTTP clients */\n httpClients: AvailableClients\n /** The history for the operation */\n history?: HistoryEntry[]\n /** Client layout */\n layout: ClientLayout\n /** Currently selected server */\n server: ServerObject | null\n /** Currently selected client */\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** Meta information for the server */\n serverMeta: ServerMeta\n /** Hides the client button on the header */\n hideClientButton?: boolean\n /** Client integration */\n integration?: string | null\n /** Openapi document url for `modal` mode to open the client app */\n documentUrl?: string\n /** Client source */\n source?: 'gitbook' | 'api-reference'\n /** Operation object */\n operation: OperationObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Meta information for the auth update */\n authMeta: AuthMeta\n /** Document defined security schemes */\n securitySchemes: MergedSecuritySchemes\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Environment list */\n environments?: string[]\n /** Currently selected environment */\n activeEnvironment?: string\n /** For environment variables in the inputs */\n environment: XScalarEnvironment\n /** The proxy URL for sending requests */\n proxyUrl: string\n /** Currently selected security */\n selectedSecurity: SelectedSecurity\n /** Currently selected security schemes */\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n /** Security requirements */\n securityRequirements: OpenApiDocument['security']\n /** Default headers */\n defaultHeaders: Record<string, string>\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /** Subset of config options for the modal */\n options?: ModalProps['options']\n}\n</script>\n<script setup lang=\"ts\">\nimport { ERRORS } from '@scalar/helpers/errors/normalize-error'\nimport { isElectron } from '@scalar/helpers/general/is-electron'\nimport { buildSafeBodyRequest } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { executeHook, type ClientPlugin } from '@scalar/oas-utils/helpers'\nimport {\n AVAILABLE_CLIENTS,\n type AvailableClients,\n} from '@scalar/types/snippetz'\nimport { useToasts } from '@scalar/use-toasts'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type { HistoryEntry } from '@scalar/workspace-store/entities/history/schema'\nimport type {\n AuthMeta,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n buildRequest,\n createVariablesStoreForRequest,\n getEnvironmentVariables,\n requestFactory,\n type MergedSecuritySchemes,\n type RequestPayload,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport type { OperationObject } from '@scalar/workspace-store/schemas/v3.1/strict/operation'\nimport { computed, onBeforeUnmount, onMounted, ref, toValue, watch } from 'vue'\n\nimport ViewLayout from '@/components/ViewLayout/ViewLayout.vue'\nimport ViewLayoutContent from '@/components/ViewLayout/ViewLayoutContent.vue'\nimport { harToFetchRequest } from '@/v2/blocks/operation-block/helpers/har-to-fetch-request'\nimport { harToFetchResponse } from '@/v2/blocks/operation-block/helpers/har-to-fetch-response'\nimport {\n getOperationExampleKey,\n isStreamingResponse,\n responseCache,\n} from '@/v2/blocks/operation-block/helpers/response-cache'\nimport {\n sendRequest,\n type ResponseInstance,\n} from '@/v2/blocks/operation-block/helpers/send-request'\nimport { validatePathParameters } from '@/v2/blocks/operation-block/helpers/validate-path-parameters'\nimport { generateClientOptions } from '@/v2/blocks/operation-code-sample'\nimport { RequestBlock } from '@/v2/blocks/request-block'\nimport { ResponseBlock } from '@/v2/blocks/response-block'\nimport { type History } from '@/v2/blocks/scalar-address-bar-block'\nimport type { ModalProps } from '@/v2/features/modal/Modal.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport Header from './components/Header.vue'\n\nconst {\n authMeta,\n environment,\n eventBus,\n exampleKey,\n document,\n documentSlug,\n workspaceCookies = [],\n documentCookies = [],\n hideClientButton,\n httpClients = AVAILABLE_CLIENTS,\n history = [],\n method,\n operation,\n path,\n plugins = [],\n proxyUrl,\n requestBodyCompositionSelection,\n securitySchemes,\n selectedClient,\n server,\n environments,\n options,\n activeEnvironment,\n serverMeta,\n selectedSecurity,\n selectedSecuritySchemes,\n securityRequirements,\n defaultHeaders,\n} = defineProps<OperationBlockProps>()\n\n/** Hoist up client generation so it doesn't get re-generated on every operation */\nconst clientOptions = computed(() => generateClientOptions(httpClients))\n\nconst { toast } = useToasts()\n\n// Refs\nconst abortController = ref<AbortController | null>(null)\nconst response = ref<ResponseInstance | null>(null)\nconst requestPayload = ref<RequestPayload | null>(null)\n\n/** Cancel the request */\nconst cancelRequest = () => abortController.value?.abort(ERRORS.REQUEST_ABORTED)\n\n/** Execute the current operation example */\nconst handleExecute = async () => {\n eventBus.flushDebouncedEmits?.()\n\n const pathValidation = validatePathParameters(\n operation.parameters ?? [],\n exampleKey,\n )\n if (pathValidation.ok === false) {\n toast('Path parameters must have values.', 'error')\n return\n }\n\n const globalCookies = [...workspaceCookies, ...documentCookies]\n\n const { request: requestBuilder } = requestFactory({\n defaultHeaders,\n environment,\n exampleName: exampleKey,\n globalCookies,\n method,\n operation,\n path,\n proxyUrl,\n server,\n selectedSecuritySchemes,\n isElectron: isElectron(),\n requestBodyCompositionSelection,\n })\n\n // Stop any previous streaming response\n if (response.value && 'reader' in response.value) {\n response.value.reader.cancel()\n }\n\n const variablesStore = createVariablesStoreForRequest()\n\n // Execute the beforeRequest hook (plugins receive RequestFactory, not fetch Request)\n await executeHook(\n {\n requestBuilder,\n document,\n operation,\n variablesStore,\n },\n 'beforeRequest',\n plugins,\n )\n\n const envVariables = {\n ...getEnvironmentVariables(environment),\n ...variablesStore.getVariables(),\n }\n\n // Build the fetch Request after hooks may have mutated the factory\n const requestResult = (() => {\n try {\n return {\n ok: true,\n result: buildRequest(requestBuilder, {\n envVariables,\n }),\n } as const\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n ok: false,\n error: message,\n } as const\n }\n })()\n\n if (requestResult.ok === false) {\n toast(requestResult.error, 'error')\n return\n }\n\n // Store the abort controller for cancellation\n abortController.value = requestResult.result.controller\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:sent', {\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n /** Execute the request */\n const [sendError, sendResult] = await sendRequest({\n isUsingProxy: requestResult.result.isUsingProxy,\n requestPayload: requestResult.result.requestPayload,\n plugins,\n customFetch: toValue(options)?.customFetch,\n })\n\n if (sendResult) {\n // Execute the responseReceived hook\n await executeHook(\n {\n response: sendResult.originalResponse.clone(),\n requestBuilder,\n request: buildSafeBodyRequest(...sendResult.requestPayload),\n document,\n operation,\n variablesStore,\n },\n 'responseReceived',\n plugins,\n )\n }\n\n // Execute the hooks\n eventBus.emit('hooks:on:request:complete', {\n payload: sendResult\n ? {\n response: sendResult.originalResponse.clone(),\n requestPayload: sendResult.requestPayload,\n duration: sendResult.response.duration,\n timestamp: sendResult.timestamp,\n }\n : undefined,\n meta: {\n method,\n path,\n exampleKey,\n },\n })\n\n if (sendError) {\n // clean up the response and request\n response.value = null\n requestPayload.value = null\n abortController.value = null\n\n toast(sendError.message, 'error')\n return\n }\n\n // Store the response\n response.value = sendResult.response\n requestPayload.value = sendResult.requestPayload\n\n // Cache non-streaming responses so they can be restored when navigating back\n if (!isStreamingResponse(sendResult.response)) {\n responseCache.set(getOperationExampleKey(method, path, exampleKey), {\n response: sendResult.response,\n requestPayload: sendResult.requestPayload,\n })\n }\n}\n\nonMounted(() => {\n eventBus.on('operation:send:request:hotkey', handleExecute)\n eventBus.on('operation:cancel:request', cancelRequest)\n})\nonBeforeUnmount(() => {\n eventBus.off('operation:send:request:hotkey', handleExecute)\n eventBus.off('operation:cancel:request', cancelRequest)\n})\n\nconst operationHistory = computed<History[]>(() =>\n history\n .map((entry) => ({\n method: entry.request.method as HttpMethodType,\n path: entry.request.url,\n duration: entry.time,\n status: entry.response.status,\n }))\n .reverse(),\n)\n\nconst handleSelectHistoryItem = ({ index }: { index: number }) => {\n const transformedIndex = (history.length ?? 0) - index - 1\n const historyItem = history[transformedIndex]\n if (!historyItem) {\n return\n }\n\n const navigate = () =>\n eventBus.emit('ui:navigate', {\n page: 'example',\n method,\n path,\n exampleName: 'draft',\n callback: (status) => {\n // Do not replace the response if the navigation was not successful\n if (status !== 'success') {\n return\n }\n // Reconstruct the response\n const fetchResponse = harToFetchResponse({\n harResponse: historyItem.response,\n url: historyItem.request.url,\n method,\n path,\n duration: historyItem.time,\n })\n\n // Reconstruct the request\n const fetchRequest = harToFetchRequest({\n harRequest: historyItem.request,\n })\n\n // Update the response and request\n response.value = fetchResponse\n requestPayload.value = fetchRequest\n },\n })\n\n eventBus.emit('operation:reload:history', {\n meta: {\n path,\n method,\n },\n index: transformedIndex,\n callback: navigate,\n })\n}\n\nconst handleNavigateSettings = () => {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'overview',\n operationPath: path,\n method,\n })\n}\n\n/**\n * When the path, method, or example key changes: save current response to\n * cache (so it can be restored when navigating back), then restore from cache\n * for the new operation or clear if no cached response. Response is only\n * cleared on page refresh or when making a new request for that operation.\n */\nwatch(\n [() => path, () => method, () => exampleKey],\n ([newPath, newMethod, newExampleKey]) => {\n const newKey = getOperationExampleKey(newMethod, newPath, newExampleKey)\n const cached = responseCache.get(newKey)\n if (cached) {\n response.value = cached.response\n requestPayload.value = cached.requestPayload\n } else {\n response.value = null\n requestPayload.value = null\n }\n\n // Cancel any in-flight request\n cancelRequest()\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n // We cancel the request if the component is unmounted\n cancelRequest()\n})\n</script>\n<template>\n <div class=\"bg-b-1 flex h-full flex-col\">\n <div\n class=\"lg:min-h-header flex w-full flex-wrap items-center justify-center p-2 lg:p-0\">\n <!-- Address Bar -->\n <Header\n :activeEnvironment\n :documentSlug\n :documentUrl\n :environment\n :environments\n :eventBus\n :exampleKey\n :hideClientButton\n :history=\"operationHistory\"\n :integration\n :layout\n :method\n :path\n :server\n :serverMeta\n :servers\n :source\n @execute=\"handleExecute\"\n @navigate:settings=\"handleNavigateSettings\"\n @select:history:item=\"handleSelectHistoryItem\" />\n </div>\n\n <ViewLayout class=\"border-t\">\n <ViewLayoutContent class=\"flex-1\">\n <!-- Request Section -->\n <RequestBlock\n :authMeta\n :clientOptions\n :defaultHeaders\n :documentCookies\n :environment\n :eventBus\n :exampleKey\n :layout\n :method\n :operation\n :options=\"toValue(options)\"\n :path\n :plugins\n :proxyUrl\n :requestBodyCompositionSelection\n :securityRequirements\n :securitySchemes\n :selectedClient\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n :workspaceCookies />\n\n <!-- Response Section -->\n <ResponseBlock\n :appVersion\n :eventBus\n :layout\n :plugins\n :requestPayload\n :response\n :totalPerformedRequests=\"operationHistory.length\" />\n </ViewLayoutContent>\n </ViewLayout>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/request-block/RequestBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components'\nimport { canMethodHaveBody } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { REGEX } from '@scalar/helpers/regex/regex-helpers'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n ApiReferenceEvents,\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 filterGlobalCookie,\n getEnvironmentVariables,\n getExample,\n getResolvedUrl,\n type MergedSecuritySchemes,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n OperationObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, useId, watch } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ClientOptionGroup } from '@/v2/blocks/operation-code-sample'\nimport RequestBody from '@/v2/blocks/request-block/components/RequestBody.vue'\nimport RequestCodeSnippet from '@/v2/blocks/request-block/components/RequestCodeSnippet.vue'\nimport RequestParams from '@/v2/blocks/request-block/components/RequestParams.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport { createParameterHandlers } from '@/v2/blocks/request-block/helpers/create-parameter-handlers'\nimport { getParameterSchema } from '@/v2/blocks/request-block/helpers/get-parameter-schema'\nimport { groupBy } from '@/v2/blocks/request-block/helpers/group-by'\nimport { isParamDisabled } from '@/v2/blocks/request-block/helpers/is-param-disabled'\nimport { AuthSelector } from '@/v2/blocks/scalar-auth-selector-block'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\ntype Filter =\n | 'All'\n | 'Auth'\n | 'Variables'\n | 'Cookies'\n | 'Headers'\n | 'Query'\n | 'Body'\n\nexport type RequestBlockProps = {\n authMeta: AuthMeta\n clientOptions: ClientOptionGroup[]\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n exampleKey: string\n workspaceCookies: XScalarCookie[]\n documentCookies: XScalarCookie[]\n layout: ClientLayout\n method: HttpMethod\n operation: OperationObject\n path: string\n plugins: ClientPlugin[]\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n requestBodyCompositionSelection?: Record<string, number>\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n selectedSecurity: SelectedSecurity\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n server: ServerObject | null\n defaultHeaders: Record<string, string>\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n}\n\nconst {\n authMeta = { type: 'document' },\n clientOptions,\n environment,\n eventBus,\n exampleKey,\n workspaceCookies,\n documentCookies,\n layout,\n method,\n operation,\n path,\n plugins,\n proxyUrl,\n requestBodyCompositionSelection,\n securityRequirements,\n securitySchemes,\n selectedClient,\n selectedSecurity,\n selectedSecuritySchemes,\n server,\n defaultHeaders: autoGeneratedHeaders,\n} = defineProps<RequestBlockProps>()\n\n/** Operation metadata used across event emissions */\nconst meta = computed(() => ({\n method,\n path,\n exampleKey,\n}))\n\n/** Parameters grouped by type (path, query, header, cookie) */\nconst sections = computed(() =>\n groupBy(\n operation.parameters?.map((param) => getResolvedRef(param)) ?? [],\n 'in',\n (param) => {\n const example = getExample(param, exampleKey, undefined)\n\n return {\n name: param.name,\n value: example?.value ?? '',\n description: param.description,\n schema: getParameterSchema(param),\n isRequired: param.required,\n isDisabled: isParamDisabled(param, example),\n originalParameter: param,\n } as TableRow\n },\n ),\n)\n\n// Generate a reverse map for fast lookup of headers by the name\nconst headersMap = computed(() =>\n groupBy(\n sections.value.header?.map((it) => ({\n ...it,\n name: it.name.toLowerCase(),\n })) ?? [],\n 'name',\n ),\n)\n\nconst defaultHeaders = computed(() => {\n const disableParameters =\n operation['x-scalar-disable-parameters']?.['default-headers']?.[\n exampleKey\n ] ?? {}\n\n const isOverridden = (name: string) => {\n const header = headersMap.value[name.toLowerCase()]?.[0]\n\n if (!header) {\n return false\n }\n\n return !header.isDisabled\n }\n\n return Object.entries(autoGeneratedHeaders).map(([name, value]) => {\n return {\n name,\n value,\n schema: undefined,\n isOverridden: isOverridden(name),\n isReadonly: true,\n isDisabled: disableParameters[name.toLowerCase()] ?? false,\n } satisfies TableRow\n })\n})\n\nconst headers = computed(() => [\n ...defaultHeaders.value,\n ...(sections.value.header ?? []),\n])\n\nconst defaultCookies = computed(() => {\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrl = getResolvedUrl({\n server,\n path,\n })\n const url = replaceEnvVariables(resolvedUrl, environmentVariables)\n\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[\n exampleKey\n ] ?? {}\n\n const transform = (\n cookie: XScalarCookie,\n location: 'document' | 'workspace',\n ) => {\n return {\n name: cookie.name,\n value: cookie.value,\n globalRoute: { page: location, path: 'cookies' } as const,\n isReadonly: true,\n isDisabled: disabledGlobalCookies[cookie.name.toLowerCase()] ?? false,\n } satisfies TableRow\n }\n\n const globalCookies = [\n {\n location: 'workspace',\n cookies: workspaceCookies,\n },\n {\n location: 'document',\n cookies: documentCookies,\n },\n ] as const\n\n return globalCookies.flatMap(({ location, cookies }) => {\n return cookies\n .filter((cookie) =>\n filterGlobalCookie({\n cookie,\n url,\n disabledGlobalCookies: {},\n }),\n )\n .map((cookie) => transform(cookie, location))\n })\n})\n\nconst cookies = computed(() => [\n ...(defaultCookies.value ?? []),\n ...(sections.value.cookie ?? []),\n])\n\n/** Currently selected filter for the request sections */\nconst selectedFilter = ref<Filter>('All')\n\n/** Available operation sections */\nconst OPERATION_SECTIONS: readonly Filter[] = [\n 'Auth',\n 'Variables',\n 'Cookies',\n 'Headers',\n 'Query',\n 'Body',\n] as const\n\n/**\n * Pre-generated stable IDs for all possible filter sections.\n * These are created once at setup time to avoid regenerating IDs on re-render.\n */\nconst sectionIds: Record<Filter, string> = {\n All: useId(),\n Auth: useId(),\n Variables: useId(),\n Cookies: useId(),\n Headers: useId(),\n Query: useId(),\n Body: useId(),\n}\n\n/** Filters available based on operation state */\nconst filters = computed<Filter[]>(() => {\n const availableFilters = new Set<Filter>(['All', ...OPERATION_SECTIONS])\n\n if (!sections.value.path?.length) {\n availableFilters.delete('Variables')\n }\n if (!canMethodHaveBody(method)) {\n availableFilters.delete('Body')\n }\n if (isAuthHidden.value) {\n availableFilters.delete('Auth')\n }\n\n return [...availableFilters]\n})\n\n/**\n * Map available filters to their pre-generated stable IDs.\n * Only includes IDs for filters that are currently available.\n */\nconst filterIds = computed(\n () =>\n Object.fromEntries(\n filters.value.map((section) => [section, sectionIds[section]]),\n ) as Record<Filter, string>,\n)\n\n/**\n * Hide auth selector in readonly mode when no security schemes are defined.\n * This keeps the UI clean when there are no authentication options available.\n */\nconst isAuthHidden = computed(\n () => layout === 'modal' && !Object.keys(securitySchemes ?? {}).length,\n)\n\n/**\n * Keep auth available for unauthenticated operations, but collapse it by default\n * in readonly modal layouts unless a requirement or manual selection exists.\n */\nconst isAuthDefaultOpen = computed(\n () =>\n layout !== 'modal' ||\n Boolean(\n securityRequirements?.length || selectedSecurity.selectedSchemes.length,\n ),\n)\n\n/** Get a sensible placeholder for the request name input */\nconst requestNamePlaceholder = computed(() => {\n if (operation.summary) {\n return operation.summary\n }\n const cleanPath = path.replace(REGEX.PROTOCOL, '')\n return cleanPath || 'Request Name'\n})\n\n/** Check if the section should be shown based on the selected filter */\nconst isSectionVisible = (section: Filter): boolean => {\n return selectedFilter.value === 'All' || selectedFilter.value === section\n}\n\n/**\n * Reset filter to 'All' if Body filter is selected but method changes to one that cannot have a body.\n * This prevents showing an empty Body section when switching methods.\n */\nwatch(\n () => method,\n (newMethod) => {\n if (selectedFilter.value === 'Body' && !canMethodHaveBody(newMethod)) {\n selectedFilter.value = 'All'\n }\n },\n)\n\n/** Handle operation summary updates */\nconst handleSummaryUpdate = (event: Event): void => {\n const summary = (event.target as HTMLInputElement).value\n eventBus.emit('operation:update:meta', {\n meta: meta.value,\n payload: { summary: summary.trim() },\n })\n}\n\n/** Parameter handlers */\nconst parameterHandlers = computed(() => ({\n path: createParameterHandlers('path', eventBus, meta.value, {\n context: sections.value.path ?? [],\n }),\n cookie: createParameterHandlers('cookie', eventBus, meta.value, {\n context: cookies.value ?? [],\n globalParameters: defaultCookies.value.length,\n }),\n header: createParameterHandlers('header', eventBus, meta.value, {\n context: headers.value,\n defaultParameters: defaultHeaders.value.length,\n }),\n query: createParameterHandlers('query', eventBus, meta.value, {\n context: sections.value.query ?? [],\n }),\n}))\n\n/** Handle request body content type update */\nconst handleUpdateContentType = (payload: { value: string }): void =>\n eventBus.emit('operation:update:requestBody:contentType', {\n payload: { contentType: payload.value },\n meta: meta.value,\n })\n\n/** Handle request body value update */\nconst handleUpdateBodyValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey =\n typeof payload === 'string'\n ? `update:requestBody:value-${contentType}`\n : undefined\n\n eventBus.emit(\n 'operation:update:requestBody:value',\n {\n payload,\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\n/** Handle request body value update */\nconst handleUpdateBodyFormValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey = `update:requestBody:${contentType}-form-value`\n\n eventBus.emit(\n 'operation:update:requestBody:formValue',\n {\n payload: payload.map((row) => unpackProxyObject(row, { depth: 1 })),\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\nconst labelRequestNameId = useId()\n\nconst globalCookies = computed(() => [...workspaceCookies, ...documentCookies])\n\n/** Allow updating the operation extensions for the plugins */\nconst updateOperationExtension = (\n payload: ApiReferenceEvents['operation:update:extension']['payload'],\n): void =>\n eventBus.emit('operation:update:extension', { payload, meta: meta.value })\n</script>\n<template>\n <ViewLayoutSection :aria-label=\"`Request: ${operation.summary}`\">\n <template #title>\n <div\n class=\"group pointer-events-none flex flex-1 items-center gap-1 lg:pr-24\">\n <label\n v-if=\"layout !== 'modal'\"\n class=\"pointer-events-auto absolute top-0 left-0 h-full w-full cursor-text opacity-0\"\n :for=\"labelRequestNameId\" />\n <input\n v-if=\"layout !== 'modal'\"\n :id=\"labelRequestNameId\"\n class=\"text-c-1 group-hover-input pointer-events-auto relative z-10 -ml-0.5 h-8 w-full rounded pl-1.25 has-[:focus-visible]:outline md:-ml-1.25\"\n :placeholder=\"requestNamePlaceholder\"\n :value=\"operation.summary\"\n @blur=\"handleSummaryUpdate\" />\n <span\n v-else\n class=\"text-c-1 flex h-8 items-center\">\n {{ operation.summary }}\n </span>\n </div>\n <SectionFilter\n v-model=\"selectedFilter\"\n :filterIds=\"filterIds\"\n :filters=\"filters\" />\n </template>\n\n <div\n :id=\"filterIds.All\"\n class=\"request-section-content custom-scroll relative flex flex-1 flex-col\"\n :role=\"selectedFilter === 'All' ? 'tabpanel' : 'none'\">\n <!-- Auth Selector -->\n <AuthSelector\n v-show=\"isSectionVisible('Auth') && !isAuthHidden\"\n :id=\"filterIds.Auth\"\n :createAnySecurityScheme=\"layout !== 'modal'\"\n :defaultOpen=\"isAuthDefaultOpen\"\n :environment\n :eventBus\n :meta=\"authMeta\"\n :options\n :proxyUrl\n :securityRequirements\n :securitySchemes\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n title=\"Authentication\" />\n\n <!-- Variables (Path Parameters) -->\n <RequestParams\n v-show=\"isSectionVisible('Variables') && sections.path?.length\"\n :id=\"filterIds.Variables\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.path ?? []\"\n :showAddRowPlaceholder=\"false\"\n title=\"Variables\"\n v-on=\"parameterHandlers.path\" />\n\n <!-- Cookies -->\n <RequestParams\n v-show=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"cookies ?? []\"\n :showAddRowPlaceholder=\"true\"\n title=\"Cookies\"\n v-on=\"parameterHandlers.cookie\" />\n\n <!-- Headers -->\n <RequestParams\n v-show=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"headers ?? []\"\n title=\"Headers\"\n v-on=\"parameterHandlers.header\" />\n\n <!-- Query Parameters -->\n <RequestParams\n v-show=\"isSectionVisible('Query')\"\n :id=\"filterIds.Query\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.query ?? []\"\n title=\"Query Parameters\"\n v-on=\"parameterHandlers.query\" />\n\n <!-- Request Body -->\n <RequestBody\n v-show=\"isSectionVisible('Body') && canMethodHaveBody(method)\"\n :id=\"filterIds.Body\"\n :environment\n :exampleKey\n :requestBody=\"getResolvedRef(operation.requestBody)\"\n :requestBodyCompositionSelection\n title=\"Request Body\"\n @update:contentType=\"handleUpdateContentType\"\n @update:formValue=\"handleUpdateBodyFormValue\"\n @update:value=\"handleUpdateBodyValue\" />\n\n <!-- Inject request section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.request.component\"\n v-if=\"plugin?.components?.request\"\n v-show=\"selectedFilter === 'All'\"\n :operation\n v-bind=\"plugin.components.request.additionalProps\"\n @operation:update:extension=\"updateOperationExtension\" />\n </ScalarErrorBoundary>\n\n <!-- Spacer -->\n <div class=\"flex grow\" />\n <!-- Code Snippet -->\n <RequestCodeSnippet\n v-show=\"selectedFilter === 'All'\"\n :clientOptions\n :eventBus\n :globalCookies\n integration=\"client\"\n :method\n :operation\n :path\n :securitySchemes=\"selectedSecuritySchemes\"\n :selectedClient\n :selectedContentType=\"\n getResolvedRef(operation.requestBody)?.[\n 'x-scalar-selected-content-type'\n ]?.[exampleKey]\n \"\n :selectedServer=\"server ?? undefined\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.request-section-content {\n --scalar-border-width: 0.5px;\n}\n.request-section-content-filter {\n box-shadow: 0 -10px 0 10px var(--scalar-background-1);\n}\n.request-item:focus-within .request-meta-buttons {\n opacity: 1;\n}\n.group-hover-input {\n border-width: var(--scalar-border-width);\n border-color: transparent;\n}\n.group:hover .group-hover-input {\n background: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n border-color: var(--scalar-border-color);\n}\n.group-hover-input:focus {\n background: transparent !important;\n border-color: var(--scalar-border-color) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RequestBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/request-block/RequestBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components'\nimport { canMethodHaveBody } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { REGEX } from '@scalar/helpers/regex/regex-helpers'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n ApiReferenceEvents,\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 filterGlobalCookie,\n getEnvironmentVariables,\n getExample,\n getResolvedUrl,\n type MergedSecuritySchemes,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n OperationObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, useId, watch } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ClientOptionGroup } from '@/v2/blocks/operation-code-sample'\nimport RequestBody from '@/v2/blocks/request-block/components/RequestBody.vue'\nimport RequestCodeSnippet from '@/v2/blocks/request-block/components/RequestCodeSnippet.vue'\nimport RequestParams from '@/v2/blocks/request-block/components/RequestParams.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport { createParameterHandlers } from '@/v2/blocks/request-block/helpers/create-parameter-handlers'\nimport { getParameterSchema } from '@/v2/blocks/request-block/helpers/get-parameter-schema'\nimport { groupBy } from '@/v2/blocks/request-block/helpers/group-by'\nimport { isParamDisabled } from '@/v2/blocks/request-block/helpers/is-param-disabled'\nimport { AuthSelector } from '@/v2/blocks/scalar-auth-selector-block'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\ntype Filter =\n | 'All'\n | 'Auth'\n | 'Variables'\n | 'Cookies'\n | 'Headers'\n | 'Query'\n | 'Body'\n\nexport type RequestBlockProps = {\n authMeta: AuthMeta\n clientOptions: ClientOptionGroup[]\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n exampleKey: string\n workspaceCookies: XScalarCookie[]\n documentCookies: XScalarCookie[]\n layout: ClientLayout\n method: HttpMethod\n operation: OperationObject\n path: string\n plugins: ClientPlugin[]\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n requestBodyCompositionSelection?: Record<string, number>\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n selectedSecurity: SelectedSecurity\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n server: ServerObject | null\n defaultHeaders: Record<string, string>\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n}\n\nconst {\n authMeta = { type: 'document' },\n clientOptions,\n environment,\n eventBus,\n exampleKey,\n workspaceCookies,\n documentCookies,\n layout,\n method,\n operation,\n path,\n plugins,\n proxyUrl,\n requestBodyCompositionSelection,\n securityRequirements,\n securitySchemes,\n selectedClient,\n selectedSecurity,\n selectedSecuritySchemes,\n server,\n defaultHeaders: autoGeneratedHeaders,\n} = defineProps<RequestBlockProps>()\n\n/** Operation metadata used across event emissions */\nconst meta = computed(() => ({\n method,\n path,\n exampleKey,\n}))\n\n/** Parameters grouped by type (path, query, header, cookie) */\nconst sections = computed(() =>\n groupBy(\n operation.parameters?.map((param) => getResolvedRef(param)) ?? [],\n 'in',\n (param) => {\n const example = getExample(param, exampleKey, undefined)\n\n return {\n name: param.name,\n value: example?.value ?? '',\n description: param.description,\n schema: getParameterSchema(param),\n isRequired: param.required,\n isDisabled: isParamDisabled(param, example),\n originalParameter: param,\n } as TableRow\n },\n ),\n)\n\n// Generate a reverse map for fast lookup of headers by the name\nconst headersMap = computed(() =>\n groupBy(\n sections.value.header?.map((it) => ({\n ...it,\n name: it.name.toLowerCase(),\n })) ?? [],\n 'name',\n ),\n)\n\nconst defaultHeaders = computed(() => {\n const disableParameters =\n operation['x-scalar-disable-parameters']?.['default-headers']?.[\n exampleKey\n ] ?? {}\n\n const isOverridden = (name: string) => {\n const header = headersMap.value[name.toLowerCase()]?.[0]\n\n if (!header) {\n return false\n }\n\n return !header.isDisabled\n }\n\n return Object.entries(autoGeneratedHeaders).map(([name, value]) => {\n return {\n name,\n value,\n schema: undefined,\n isOverridden: isOverridden(name),\n isReadonly: true,\n isDisabled: disableParameters[name.toLowerCase()] ?? false,\n } satisfies TableRow\n })\n})\n\nconst headers = computed(() => [\n ...defaultHeaders.value,\n ...(sections.value.header ?? []),\n])\n\nconst defaultCookies = computed(() => {\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrl = getResolvedUrl({\n server,\n path,\n })\n const url = replaceEnvVariables(resolvedUrl, environmentVariables)\n\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[\n exampleKey\n ] ?? {}\n\n const transform = (\n cookie: XScalarCookie,\n location: 'document' | 'workspace',\n ) => {\n return {\n name: cookie.name,\n value: cookie.value,\n globalRoute: { page: location, path: 'cookies' } as const,\n isReadonly: true,\n isDisabled: disabledGlobalCookies[cookie.name.toLowerCase()] ?? false,\n } satisfies TableRow\n }\n\n const globalCookies = [\n {\n location: 'workspace',\n cookies: workspaceCookies,\n },\n {\n location: 'document',\n cookies: documentCookies,\n },\n ] as const\n\n return globalCookies.flatMap(({ location, cookies }) => {\n return cookies\n .filter((cookie) =>\n filterGlobalCookie({\n cookie,\n url,\n disabledGlobalCookies: {},\n }),\n )\n .map((cookie) => transform(cookie, location))\n })\n})\n\nconst cookies = computed(() => [\n ...(defaultCookies.value ?? []),\n ...(sections.value.cookie ?? []),\n])\n\n/** Currently selected filter for the request sections */\nconst selectedFilter = ref<Filter>('All')\n\n/** Available operation sections */\nconst OPERATION_SECTIONS: readonly Filter[] = [\n 'Auth',\n 'Variables',\n 'Cookies',\n 'Headers',\n 'Query',\n 'Body',\n] as const\n\n/**\n * Pre-generated stable IDs for all possible filter sections.\n * These are created once at setup time to avoid regenerating IDs on re-render.\n */\nconst sectionIds: Record<Filter, string> = {\n All: useId(),\n Auth: useId(),\n Variables: useId(),\n Cookies: useId(),\n Headers: useId(),\n Query: useId(),\n Body: useId(),\n}\n\n/** Filters available based on operation state */\nconst filters = computed<Filter[]>(() => {\n const availableFilters = new Set<Filter>(['All', ...OPERATION_SECTIONS])\n\n if (!sections.value.path?.length) {\n availableFilters.delete('Variables')\n }\n if (!canMethodHaveBody(method)) {\n availableFilters.delete('Body')\n }\n if (isAuthHidden.value) {\n availableFilters.delete('Auth')\n }\n\n return [...availableFilters]\n})\n\n/**\n * Map available filters to their pre-generated stable IDs.\n * Only includes IDs for filters that are currently available.\n */\nconst filterIds = computed(\n () =>\n Object.fromEntries(\n filters.value.map((section) => [section, sectionIds[section]]),\n ) as Record<Filter, string>,\n)\n\n/**\n * Hide auth selector in readonly mode when no security schemes are defined.\n * This keeps the UI clean when there are no authentication options available.\n */\nconst isAuthHidden = computed(\n () => layout === 'modal' && !Object.keys(securitySchemes ?? {}).length,\n)\n\n/**\n * Keep auth available for unauthenticated operations, but collapse it by default\n * in readonly modal layouts unless a requirement or manual selection exists.\n */\nconst isAuthDefaultOpen = computed(\n () =>\n layout !== 'modal' ||\n Boolean(\n securityRequirements?.length || selectedSecurity.selectedSchemes.length,\n ),\n)\n\n/** Get a sensible placeholder for the request name input */\nconst requestNamePlaceholder = computed(() => {\n if (operation.summary) {\n return operation.summary\n }\n const cleanPath = path.replace(REGEX.PROTOCOL, '')\n return cleanPath || 'Request Name'\n})\n\n/** Check if the section should be shown based on the selected filter */\nconst isSectionVisible = (section: Filter): boolean => {\n return selectedFilter.value === 'All' || selectedFilter.value === section\n}\n\n/**\n * Reset filter to 'All' if Body filter is selected but method changes to one that cannot have a body.\n * This prevents showing an empty Body section when switching methods.\n */\nwatch(\n () => method,\n (newMethod) => {\n if (selectedFilter.value === 'Body' && !canMethodHaveBody(newMethod)) {\n selectedFilter.value = 'All'\n }\n },\n)\n\n/** Handle operation summary updates */\nconst handleSummaryUpdate = (event: Event): void => {\n const summary = (event.target as HTMLInputElement).value\n eventBus.emit('operation:update:meta', {\n meta: meta.value,\n payload: { summary: summary.trim() },\n })\n}\n\n/** Parameter handlers */\nconst parameterHandlers = computed(() => ({\n path: createParameterHandlers('path', eventBus, meta.value, {\n context: sections.value.path ?? [],\n }),\n cookie: createParameterHandlers('cookie', eventBus, meta.value, {\n context: cookies.value ?? [],\n globalParameters: defaultCookies.value.length,\n }),\n header: createParameterHandlers('header', eventBus, meta.value, {\n context: headers.value,\n defaultParameters: defaultHeaders.value.length,\n }),\n query: createParameterHandlers('query', eventBus, meta.value, {\n context: sections.value.query ?? [],\n }),\n}))\n\n/** Handle request body content type update */\nconst handleUpdateContentType = (payload: { value: string }): void =>\n eventBus.emit('operation:update:requestBody:contentType', {\n payload: { contentType: payload.value },\n meta: meta.value,\n })\n\n/** Handle request body value update */\nconst handleUpdateBodyValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey =\n typeof payload === 'string'\n ? `update:requestBody:value-${contentType}`\n : undefined\n\n eventBus.emit(\n 'operation:update:requestBody:value',\n {\n payload,\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\n/** Handle request body value update */\nconst handleUpdateBodyFormValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey = `update:requestBody:${contentType}-form-value`\n\n eventBus.emit(\n 'operation:update:requestBody:formValue',\n {\n payload: payload.map((row) => unpackProxyObject(row, { depth: 1 })),\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\nconst labelRequestNameId = useId()\n\nconst globalCookies = computed(() => [...workspaceCookies, ...documentCookies])\n\n/** Allow updating the operation extensions for the plugins */\nconst updateOperationExtension = (\n payload: ApiReferenceEvents['operation:update:extension']['payload'],\n): void =>\n eventBus.emit('operation:update:extension', { payload, meta: meta.value })\n</script>\n<template>\n <ViewLayoutSection :aria-label=\"`Request: ${operation.summary}`\">\n <template #title>\n <div\n class=\"group pointer-events-none flex flex-1 items-center gap-1 lg:pr-24\">\n <label\n v-if=\"layout !== 'modal'\"\n class=\"pointer-events-auto absolute top-0 left-0 h-full w-full cursor-text opacity-0\"\n :for=\"labelRequestNameId\" />\n <input\n v-if=\"layout !== 'modal'\"\n :id=\"labelRequestNameId\"\n class=\"text-c-1 group-hover-input pointer-events-auto relative z-10 -ml-0.5 h-8 w-full rounded pl-1.25 has-[:focus-visible]:outline md:-ml-1.25\"\n :placeholder=\"requestNamePlaceholder\"\n :value=\"operation.summary\"\n @blur=\"handleSummaryUpdate\" />\n <span\n v-else\n class=\"text-c-1 flex h-8 items-center\">\n {{ operation.summary }}\n </span>\n </div>\n <SectionFilter\n v-model=\"selectedFilter\"\n :filterIds=\"filterIds\"\n :filters=\"filters\" />\n </template>\n\n <div\n :id=\"filterIds.All\"\n class=\"request-section-content custom-scroll relative flex flex-1 flex-col\"\n :role=\"selectedFilter === 'All' ? 'tabpanel' : 'none'\">\n <!-- Auth Selector -->\n <AuthSelector\n v-show=\"isSectionVisible('Auth') && !isAuthHidden\"\n :id=\"filterIds.Auth\"\n :createAnySecurityScheme=\"layout !== 'modal'\"\n :defaultOpen=\"isAuthDefaultOpen\"\n :environment\n :eventBus\n :meta=\"authMeta\"\n :options\n :proxyUrl\n :securityRequirements\n :securitySchemes\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n title=\"Authentication\" />\n\n <!-- Variables (Path Parameters) -->\n <RequestParams\n v-show=\"isSectionVisible('Variables') && sections.path?.length\"\n :id=\"filterIds.Variables\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.path ?? []\"\n :showAddRowPlaceholder=\"false\"\n title=\"Variables\"\n v-on=\"parameterHandlers.path\" />\n\n <!-- Cookies -->\n <RequestParams\n v-show=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"cookies ?? []\"\n :showAddRowPlaceholder=\"true\"\n title=\"Cookies\"\n v-on=\"parameterHandlers.cookie\" />\n\n <!-- Headers -->\n <RequestParams\n v-show=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"headers ?? []\"\n title=\"Headers\"\n v-on=\"parameterHandlers.header\" />\n\n <!-- Query Parameters -->\n <RequestParams\n v-show=\"isSectionVisible('Query')\"\n :id=\"filterIds.Query\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.query ?? []\"\n title=\"Query Parameters\"\n v-on=\"parameterHandlers.query\" />\n\n <!-- Request Body -->\n <RequestBody\n v-show=\"isSectionVisible('Body') && canMethodHaveBody(method)\"\n :id=\"filterIds.Body\"\n :environment\n :exampleKey\n :requestBody=\"getResolvedRef(operation.requestBody)\"\n :requestBodyCompositionSelection\n title=\"Request Body\"\n @update:contentType=\"handleUpdateContentType\"\n @update:formValue=\"handleUpdateBodyFormValue\"\n @update:value=\"handleUpdateBodyValue\" />\n\n <!-- Inject request section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.request.component\"\n v-if=\"plugin?.components?.request\"\n v-show=\"selectedFilter === 'All'\"\n :operation\n v-bind=\"plugin.components.request.additionalProps\"\n @operation:update:extension=\"updateOperationExtension\" />\n </ScalarErrorBoundary>\n\n <!-- Spacer -->\n <div class=\"flex grow\" />\n <!-- Code Snippet -->\n <RequestCodeSnippet\n v-show=\"selectedFilter === 'All'\"\n :clientOptions\n :eventBus\n :globalCookies\n integration=\"client\"\n :method\n :operation\n :path\n :securitySchemes=\"selectedSecuritySchemes\"\n :selectedClient\n :selectedContentType=\"\n getResolvedRef(operation.requestBody)?.[\n 'x-scalar-selected-content-type'\n ]?.[exampleKey]\n \"\n :selectedServer=\"server ?? undefined\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.request-section-content {\n --scalar-border-width: 0.5px;\n}\n.request-section-content-filter {\n box-shadow: 0 -10px 0 10px var(--scalar-background-1);\n}\n.request-item:focus-within .request-meta-buttons {\n opacity: 1;\n}\n.group-hover-input {\n border-width: var(--scalar-border-width);\n border-color: transparent;\n}\n.group:hover .group-hover-input {\n background: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n border-color: var(--scalar-border-color);\n}\n.group-hover-input:focus {\n background: transparent !important;\n border-color: var(--scalar-border-color) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -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 ScalarComboboxMultiselect,\n ScalarIconButton,\n ScalarListboxCheckbox,\n useModal,\n type Icon,\n type ScalarButton as ScalarButtonType,\n} from '@scalar/components'\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.1/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'\n\nimport RequestAuthDataTable from './RequestAuthDataTable.vue'\n\nconst {\n environment,\n eventBus,\n createAnySecurityScheme = false,\n defaultOpen = true,\n isStatic = false,\n meta,\n proxyUrl,\n securityRequirements,\n securitySchemes,\n selectedSecurity,\n server,\n title,\n options,\n} = defineProps<{\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n /** Allows adding authentication which is not in the document */\n createAnySecurityScheme?: boolean\n /** Whether the authentication disclosure should start expanded */\n defaultOpen?: 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}>()\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 ? 'Optional' : '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 ),\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/**\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 (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 :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 hover:bg-b-3 hover:text-c-1 -my-0.5 -mr-1 cursor-pointer rounded px-1 py-0.5 leading-[normal] font-normal\"\n :class=\"{ 'text-c-1': authIndicator.text === 'Required' }\"\n data-testid=\"auth-indicator\"\n @click=\"handleAuthIndicatorClick\">\n {{ authIndicator.text }}\n </span>\n </div>\n </template>\n\n <!-- Auth Dropdown (hidden when only one scheme is available) -->\n <template #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 <span class=\"sr-only\">Selected Auth Type:</span>\n {{ activeSchemeOptions[0]?.label }}\n </template>\n\n <!-- Multiple auth schemes selected -->\n <template v-else-if=\"activeSchemeOptions.length > 1\">\n Multiple\n <span class=\"sr-only\">Auth Types Selected</span>\n </template>\n\n <!-- No auth schemes selected -->\n <template v-else>\n <span class=\"sr-only\">Select</span>\n Auth Type\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\"\n class=\"-m-0.5 shrink-0 p-0.5 opacity-0 group-hover/item:opacity-100\"\n :icon=\"ScalarIconTrash\"\n :label=\"`Delete ${option.label}`\"\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 :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 ScalarComboboxMultiselect,\n ScalarIconButton,\n ScalarListboxCheckbox,\n useModal,\n type Icon,\n type ScalarButton as ScalarButtonType,\n} from '@scalar/components'\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.1/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'\n\nimport RequestAuthDataTable from './RequestAuthDataTable.vue'\n\nconst {\n environment,\n eventBus,\n createAnySecurityScheme = false,\n defaultOpen = true,\n isStatic = false,\n meta,\n proxyUrl,\n securityRequirements,\n securitySchemes,\n selectedSecurity,\n server,\n title,\n options,\n} = defineProps<{\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n /** Allows adding authentication which is not in the document */\n createAnySecurityScheme?: boolean\n /** Whether the authentication disclosure should start expanded */\n defaultOpen?: 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}>()\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 ? 'Optional' : '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 ),\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/**\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 (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 :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 hover:bg-b-3 hover:text-c-1 -my-0.5 -mr-1 cursor-pointer rounded px-1 py-0.5 leading-[normal] font-normal\"\n :class=\"{ 'text-c-1': authIndicator.text === 'Required' }\"\n data-testid=\"auth-indicator\"\n @click=\"handleAuthIndicatorClick\">\n {{ authIndicator.text }}\n </span>\n </div>\n </template>\n\n <!-- Auth Dropdown (hidden when only one scheme is available) -->\n <template #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 <span class=\"sr-only\">Selected Auth Type:</span>\n {{ activeSchemeOptions[0]?.label }}\n </template>\n\n <!-- Multiple auth schemes selected -->\n <template v-else-if=\"activeSchemeOptions.length > 1\">\n Multiple\n <span class=\"sr-only\">Auth Types Selected</span>\n </template>\n\n <!-- No auth schemes selected -->\n <template v-else>\n <span class=\"sr-only\">Select</span>\n Auth Type\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\"\n class=\"-m-0.5 shrink-0 p-0.5 opacity-0 group-hover/item:opacity-100\"\n :icon=\"ScalarIconTrash\"\n :label=\"`Delete ${option.label}`\"\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 :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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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps a callback parameter paired with the URL component it was read from.
|
|
3
|
+
* State validation depends on this source so query and hash values cannot be
|
|
4
|
+
* mixed across different OAuth callback shapes.
|
|
5
|
+
*/
|
|
6
|
+
type OAuthCallbackParamResult = {
|
|
7
|
+
/** The query or hash parameters that contained the value. */
|
|
8
|
+
params: URLSearchParams | null;
|
|
9
|
+
/** The callback parameter value, or null when the parameter is missing. */
|
|
10
|
+
value: string | null;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Normalized OAuth callback data used after the popup returns to a readable
|
|
14
|
+
* redirect URL. Credential params are retained so callers can validate state
|
|
15
|
+
* from the same URL component as the returned credential.
|
|
16
|
+
*/
|
|
17
|
+
type OAuthCallbackData = {
|
|
18
|
+
/** Access token returned by implicit or token-based flows. */
|
|
19
|
+
accessToken: string | null;
|
|
20
|
+
/** The URL component that contained the access token. */
|
|
21
|
+
accessTokenParams: URLSearchParams | null;
|
|
22
|
+
/** Authorization code returned by authorization-code flows. */
|
|
23
|
+
code: string | null;
|
|
24
|
+
/** The URL component that contained the authorization code. */
|
|
25
|
+
codeParams: URLSearchParams | null;
|
|
26
|
+
/** OAuth error code returned by the provider. */
|
|
27
|
+
error: string | null;
|
|
28
|
+
/** Provider-supplied details for the OAuth error. */
|
|
29
|
+
errorDescription: string | null;
|
|
30
|
+
/** Refresh token returned by providers that include one in the callback. */
|
|
31
|
+
refreshToken: string | null;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Splits an OAuth redirect URL into query and hash parameters.
|
|
35
|
+
*
|
|
36
|
+
* OAuth providers are inconsistent about where they return callback data:
|
|
37
|
+
* authorization-code flows usually use the query string, while implicit flows
|
|
38
|
+
* commonly use the URL fragment. Keeping both sets separate lets callers decide
|
|
39
|
+
* which source should win when a provider sends duplicate keys.
|
|
40
|
+
*/
|
|
41
|
+
export declare const getOAuthCallbackParams: (callbackUrl: string) => {
|
|
42
|
+
searchParams: URLSearchParams;
|
|
43
|
+
hashParams: URLSearchParams;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Reads a callback parameter from the query string first, then the hash fragment.
|
|
47
|
+
*
|
|
48
|
+
* Query-string values intentionally take precedence because they are the
|
|
49
|
+
* standard location for authorization-code callbacks. The hash lookup keeps
|
|
50
|
+
* implicit-flow callbacks and non-standard providers working.
|
|
51
|
+
*/
|
|
52
|
+
export declare const getOAuthCallbackParam: (callbackUrl: string, paramName: string) => string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Reads a callback parameter and returns the URL component it came from.
|
|
55
|
+
*
|
|
56
|
+
* OAuth state must be validated from the same component as the credential
|
|
57
|
+
* (`code` or `access_token`) so mixed query/hash callbacks cannot pair a
|
|
58
|
+
* trusted state with an untrusted credential.
|
|
59
|
+
*/
|
|
60
|
+
export declare const getOAuthCallbackParamWithSource: (searchParams: URLSearchParams, hashParams: URLSearchParams, paramName: string) => OAuthCallbackParamResult;
|
|
61
|
+
/**
|
|
62
|
+
* Safely reads the OAuth popup callback data.
|
|
63
|
+
*
|
|
64
|
+
* Accessing the popup URL can throw while it is still on another origin, so
|
|
65
|
+
* callers get null values until the popup returns to a readable callback URL.
|
|
66
|
+
*/
|
|
67
|
+
export declare const getOAuthCallbackData: (getCallbackUrl: () => string, tokenName?: string) => OAuthCallbackData;
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=oauth-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-callback.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,KAAK,wBAAwB,GAAG;IAC9B,6DAA6D;IAC7D,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAAA;AAED;;;;GAIG;AACH,KAAK,iBAAiB,GAAG;IACvB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,yDAAyD;IACzD,iBAAiB,EAAE,eAAe,GAAG,IAAI,CAAA;IACzC,+DAA+D;IAC/D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,+DAA+D;IAC/D,UAAU,EAAE,eAAe,GAAG,IAAI,CAAA;IAClC,iDAAiD;IACjD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,4EAA4E;IAC5E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,MAAM,KAClB;IAAE,YAAY,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,eAAe,CAAA;CAO9D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,MAAM,EAAE,WAAW,MAAM,KAAG,MAAM,GAAG,IAGvF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,GAC1C,cAAc,eAAe,EAC7B,YAAY,eAAe,EAC3B,WAAW,MAAM,KAChB,wBAYF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,gBAAgB,MAAM,MAAM,EAAE,kBAA0B,KAAG,iBA2B/F,CAAA"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//#region src/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.ts
|
|
2
|
+
/**
|
|
3
|
+
* Splits an OAuth redirect URL into query and hash parameters.
|
|
4
|
+
*
|
|
5
|
+
* OAuth providers are inconsistent about where they return callback data:
|
|
6
|
+
* authorization-code flows usually use the query string, while implicit flows
|
|
7
|
+
* commonly use the URL fragment. Keeping both sets separate lets callers decide
|
|
8
|
+
* which source should win when a provider sends duplicate keys.
|
|
9
|
+
*/
|
|
10
|
+
var getOAuthCallbackParams = (callbackUrl) => {
|
|
11
|
+
const parsedUrl = new URL(callbackUrl);
|
|
12
|
+
return {
|
|
13
|
+
searchParams: parsedUrl.searchParams,
|
|
14
|
+
hashParams: new URLSearchParams(parsedUrl.hash.slice(1))
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Reads a callback parameter and returns the URL component it came from.
|
|
19
|
+
*
|
|
20
|
+
* OAuth state must be validated from the same component as the credential
|
|
21
|
+
* (`code` or `access_token`) so mixed query/hash callbacks cannot pair a
|
|
22
|
+
* trusted state with an untrusted credential.
|
|
23
|
+
*/
|
|
24
|
+
var getOAuthCallbackParamWithSource = (searchParams, hashParams, paramName) => {
|
|
25
|
+
const searchValue = searchParams.get(paramName);
|
|
26
|
+
if (searchValue !== null) return {
|
|
27
|
+
params: searchParams,
|
|
28
|
+
value: searchValue
|
|
29
|
+
};
|
|
30
|
+
const hashValue = hashParams.get(paramName);
|
|
31
|
+
if (hashValue !== null) return {
|
|
32
|
+
params: hashParams,
|
|
33
|
+
value: hashValue
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
params: null,
|
|
37
|
+
value: null
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Safely reads the OAuth popup callback data.
|
|
42
|
+
*
|
|
43
|
+
* Accessing the popup URL can throw while it is still on another origin, so
|
|
44
|
+
* callers get null values until the popup returns to a readable callback URL.
|
|
45
|
+
*/
|
|
46
|
+
var getOAuthCallbackData = (getCallbackUrl, tokenName = "access_token") => {
|
|
47
|
+
try {
|
|
48
|
+
const { searchParams, hashParams } = getOAuthCallbackParams(getCallbackUrl());
|
|
49
|
+
const accessTokenResult = getOAuthCallbackParamWithSource(searchParams, hashParams, tokenName);
|
|
50
|
+
const codeResult = getOAuthCallbackParamWithSource(searchParams, hashParams, "code");
|
|
51
|
+
return {
|
|
52
|
+
accessToken: accessTokenResult.value,
|
|
53
|
+
accessTokenParams: accessTokenResult.params,
|
|
54
|
+
code: codeResult.value,
|
|
55
|
+
codeParams: codeResult.params,
|
|
56
|
+
error: searchParams.get("error") ?? hashParams.get("error"),
|
|
57
|
+
errorDescription: searchParams.get("error_description") ?? hashParams.get("error_description"),
|
|
58
|
+
refreshToken: searchParams.get("refresh_token") ?? hashParams.get("refresh_token")
|
|
59
|
+
};
|
|
60
|
+
} catch (_e) {
|
|
61
|
+
return {
|
|
62
|
+
accessToken: null,
|
|
63
|
+
accessTokenParams: null,
|
|
64
|
+
code: null,
|
|
65
|
+
codeParams: null,
|
|
66
|
+
error: null,
|
|
67
|
+
errorDescription: null,
|
|
68
|
+
refreshToken: null
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
export { getOAuthCallbackData };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=oauth-callback.js.map
|