@scalar/api-client 3.17.0 → 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/style.css +7 -7
  3. package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
  4. package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
  5. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +2 -0
  6. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
  7. package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts +2 -0
  8. package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts.map +1 -1
  9. package/dist/v2/blocks/request-block/RequestBlock.vue.js +1 -1
  10. package/dist/v2/blocks/request-block/RequestBlock.vue.js.map +1 -1
  11. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js +3 -0
  12. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
  13. package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts +6 -0
  14. package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts.map +1 -1
  15. package/dist/v2/blocks/request-block/components/RequestBody.vue.js +1 -1
  16. package/dist/v2/blocks/request-block/components/RequestBody.vue.js.map +1 -1
  17. package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js +4 -3
  18. package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js.map +1 -1
  19. package/dist/v2/blocks/request-block/helpers/get-form-body-rows.d.ts.map +1 -1
  20. package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js +3 -2
  21. package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js.map +1 -1
  22. package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
  23. package/dist/v2/constants.js +1 -1
  24. package/dist/vue-styles.css +7 -7
  25. package/package.json +10 -10
@@ -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 type { ClientOptionGroup } from '@scalar/blocks/code-example'\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\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 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 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 { createParameterRows } from '@/v2/blocks/request-block/helpers/create-parameter-rows'\nimport { groupBy } from '@/v2/blocks/request-block/helpers/group-by'\nimport { groupGlobalCookies } from '@/v2/blocks/request-block/helpers/group-global-cookies'\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\nconst deletedExpandedParameterPaths = ref<Record<string, string[][]>>({})\n\n/** Renamed expanded rows, tracked as old-schema-path -> typed-value-path per parameter. */\nconst renamedExpandedParameterPaths = ref<\n Record<string, { from: string[]; to: string[] }[]>\n>({})\n\nconst getExpandedParameterKey = (parameter: {\n in: string\n name: string\n}): string => `${parameter.in}:${parameter.name}`\n\nconst getHiddenValuePaths = (parameter: {\n in: string\n name: string\n}): string[][] =>\n deletedExpandedParameterPaths.value[getExpandedParameterKey(parameter)] ?? []\n\nconst getRenamedValuePaths = (parameter: {\n in: string\n name: string\n}): { from: string[]; to: string[] }[] =>\n renamedExpandedParameterPaths.value[getExpandedParameterKey(parameter)] ?? []\n\nconst pathsEqual = (a: string[], b: string[]): boolean =>\n a.length === b.length && a.every((segment, index) => segment === b[index])\n\n/**\n * Hides the schema path of an expanded row so it does not reappear as an empty suggestion. Used\n * both when a row is deleted and when its key is renamed (the old key should not pop back up).\n */\nconst hideExpandedRowPath = (row: TableRow): void => {\n if (!row.originalParameter || !row.sourceParameterValuePath) {\n return\n }\n\n const key = getExpandedParameterKey(row.originalParameter)\n deletedExpandedParameterPaths.value = {\n ...deletedExpandedParameterPaths.value,\n [key]: [\n ...(deletedExpandedParameterPaths.value[key] ?? []),\n row.sourceParameterValuePath,\n ],\n }\n}\n\n/**\n * Records that an expanded row was renamed from its current value path to the typed one, so the\n * read side can render the renamed key in the original property's slot instead of appending it to\n * the end of the table. Repeated renames of the same row stay anchored to the original schema path.\n */\nconst recordExpandedRowRename = (\n row: TableRow,\n newValuePath: string[],\n): void => {\n if (!row.originalParameter || !row.sourceParameterValuePath) {\n return\n }\n\n const from = row.sourceParameterValuePath\n const key = getExpandedParameterKey(row.originalParameter)\n const existing = renamedExpandedParameterPaths.value[key] ?? []\n\n // If this row was already renamed, the row's current path is the previous target. Re-anchor that\n // entry to the new target so a second rename does not create an orphaned mapping.\n const chained = existing.find((entry) => pathsEqual(entry.to, from))\n const next = chained\n ? existing.map((entry) =>\n entry === chained ? { from: entry.from, to: newValuePath } : entry,\n )\n : [...existing, { from, to: newValuePath }]\n\n renamedExpandedParameterPaths.value = {\n ...renamedExpandedParameterPaths.value,\n [key]: next,\n }\n}\n\n/** Parameters grouped by type (path, query, header, cookie) */\nconst sections = computed(() =>\n groupBy(\n operation.parameters\n ?.map((param) => getResolvedRef(param))\n .flatMap((param) =>\n createParameterRows(param, exampleKey, {\n hiddenValuePaths:\n param.in === 'query' ? getHiddenValuePaths(param) : [],\n renamedValuePaths:\n param.in === 'query' ? getRenamedValuePaths(param) : [],\n }).map((row) => ({\n ...row,\n in: param.in,\n })),\n ) ?? [],\n 'in',\n ({ in: _in, ...row }) => {\n return row 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\n/** Resolved request URL, used to filter global cookies by their domain and path. */\nconst requestCookieUrl = computed(() => {\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrl = getResolvedUrl({ server, path })\n return replaceEnvVariables(resolvedUrl, environmentVariables)\n})\n\n/**\n * Global cookies grouped by name. Same-named cookies collapse into a single logical cookie so\n * duplicate names no longer share one disabled state (which made toggling one toggle them all).\n * A name with multiple values becomes a preset the user can switch between.\n */\nconst globalCookieGroups = computed(() =>\n groupGlobalCookies({\n sources: [\n { location: 'workspace', cookies: workspaceCookies },\n { location: 'document', cookies: documentCookies },\n ],\n // Keep domain/path filtering but ignore the disabled flag, so a disabled preset value stays\n // selectable in the switcher (it is only hidden from the request, not from the dropdown).\n shouldInclude: (cookie) =>\n filterGlobalCookie({\n cookie: { ...cookie, isDisabled: false },\n url: requestCookieUrl.value,\n disabledGlobalCookies: {},\n }),\n }),\n)\n\nconst defaultCookies = computed<TableRow[]>(() => {\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[\n exampleKey\n ] ?? {}\n\n return globalCookieGroups.value.map((group) => ({\n name: group.name,\n value: group.selectedValue,\n globalRoute: { page: group.location, path: 'cookies' } as const,\n isReadonly: true,\n isDisabled: disabledGlobalCookies[group.name.toLowerCase()] ?? false,\n presetOptions: group.isPreset ? group.options : undefined,\n }))\n})\n\n/**\n * Switch a grouped global cookie preset to the chosen value by enabling the matching sibling and\n * disabling the rest. Selection is written back to the source `x-scalar-cookies` list so it\n * persists, and the send path drops disabled cookies, so only the selected value is sent.\n */\nconst handleSelectCookiePreset = (index: number, value: string): void => {\n const group = globalCookieGroups.value[index]\n if (!group) {\n return\n }\n\n for (const sibling of group.siblings) {\n eventBus.emit('cookie:upsert:cookie', {\n collectionType: sibling.location,\n index: sibling.index,\n payload: { isDisabled: sibling.value !== value },\n })\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// Expanded-row bookkeeping is keyed by `in:name`, which is not unique across operations or\n// examples. Reset both maps when the operation or example changes so stale delete/rename mappings\n// do not leak onto another request that happens to share a query parameter name.\nwatch(\n () => [method, path, exampleKey],\n () => {\n deletedExpandedParameterPaths.value = {}\n renamedExpandedParameterPaths.value = {}\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: {\n ...createParameterHandlers('cookie', eventBus, meta.value, {\n context: cookies.value ?? [],\n globalParameters: defaultCookies.value.length,\n }),\n selectPreset: handleSelectCookiePreset,\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 onDeleteExpandedRow: hideExpandedRowPath,\n onRenameExpandedRow: recordExpandedRowRename,\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 type { ClientOptionGroup } from '@scalar/blocks/code-example'\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\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 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 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 { createParameterRows } from '@/v2/blocks/request-block/helpers/create-parameter-rows'\nimport { groupBy } from '@/v2/blocks/request-block/helpers/group-by'\nimport { groupGlobalCookies } from '@/v2/blocks/request-block/helpers/group-global-cookies'\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 /** Initial view for structured request bodies, from the `x-scalar-default-request-body-view` document extension */\n defaultRequestBodyView?: 'form' | 'raw'\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 defaultRequestBodyView = 'raw',\n} = defineProps<RequestBlockProps>()\n\n/** Operation metadata used across event emissions */\nconst meta = computed(() => ({\n method,\n path,\n exampleKey,\n}))\n\nconst deletedExpandedParameterPaths = ref<Record<string, string[][]>>({})\n\n/** Renamed expanded rows, tracked as old-schema-path -> typed-value-path per parameter. */\nconst renamedExpandedParameterPaths = ref<\n Record<string, { from: string[]; to: string[] }[]>\n>({})\n\nconst getExpandedParameterKey = (parameter: {\n in: string\n name: string\n}): string => `${parameter.in}:${parameter.name}`\n\nconst getHiddenValuePaths = (parameter: {\n in: string\n name: string\n}): string[][] =>\n deletedExpandedParameterPaths.value[getExpandedParameterKey(parameter)] ?? []\n\nconst getRenamedValuePaths = (parameter: {\n in: string\n name: string\n}): { from: string[]; to: string[] }[] =>\n renamedExpandedParameterPaths.value[getExpandedParameterKey(parameter)] ?? []\n\nconst pathsEqual = (a: string[], b: string[]): boolean =>\n a.length === b.length && a.every((segment, index) => segment === b[index])\n\n/**\n * Hides the schema path of an expanded row so it does not reappear as an empty suggestion. Used\n * both when a row is deleted and when its key is renamed (the old key should not pop back up).\n */\nconst hideExpandedRowPath = (row: TableRow): void => {\n if (!row.originalParameter || !row.sourceParameterValuePath) {\n return\n }\n\n const key = getExpandedParameterKey(row.originalParameter)\n deletedExpandedParameterPaths.value = {\n ...deletedExpandedParameterPaths.value,\n [key]: [\n ...(deletedExpandedParameterPaths.value[key] ?? []),\n row.sourceParameterValuePath,\n ],\n }\n}\n\n/**\n * Records that an expanded row was renamed from its current value path to the typed one, so the\n * read side can render the renamed key in the original property's slot instead of appending it to\n * the end of the table. Repeated renames of the same row stay anchored to the original schema path.\n */\nconst recordExpandedRowRename = (\n row: TableRow,\n newValuePath: string[],\n): void => {\n if (!row.originalParameter || !row.sourceParameterValuePath) {\n return\n }\n\n const from = row.sourceParameterValuePath\n const key = getExpandedParameterKey(row.originalParameter)\n const existing = renamedExpandedParameterPaths.value[key] ?? []\n\n // If this row was already renamed, the row's current path is the previous target. Re-anchor that\n // entry to the new target so a second rename does not create an orphaned mapping.\n const chained = existing.find((entry) => pathsEqual(entry.to, from))\n const next = chained\n ? existing.map((entry) =>\n entry === chained ? { from: entry.from, to: newValuePath } : entry,\n )\n : [...existing, { from, to: newValuePath }]\n\n renamedExpandedParameterPaths.value = {\n ...renamedExpandedParameterPaths.value,\n [key]: next,\n }\n}\n\n/** Parameters grouped by type (path, query, header, cookie) */\nconst sections = computed(() =>\n groupBy(\n operation.parameters\n ?.map((param) => getResolvedRef(param))\n .flatMap((param) =>\n createParameterRows(param, exampleKey, {\n hiddenValuePaths:\n param.in === 'query' ? getHiddenValuePaths(param) : [],\n renamedValuePaths:\n param.in === 'query' ? getRenamedValuePaths(param) : [],\n }).map((row) => ({\n ...row,\n in: param.in,\n })),\n ) ?? [],\n 'in',\n ({ in: _in, ...row }) => {\n return row 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\n/** Resolved request URL, used to filter global cookies by their domain and path. */\nconst requestCookieUrl = computed(() => {\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrl = getResolvedUrl({ server, path })\n return replaceEnvVariables(resolvedUrl, environmentVariables)\n})\n\n/**\n * Global cookies grouped by name. Same-named cookies collapse into a single logical cookie so\n * duplicate names no longer share one disabled state (which made toggling one toggle them all).\n * A name with multiple values becomes a preset the user can switch between.\n */\nconst globalCookieGroups = computed(() =>\n groupGlobalCookies({\n sources: [\n { location: 'workspace', cookies: workspaceCookies },\n { location: 'document', cookies: documentCookies },\n ],\n // Keep domain/path filtering but ignore the disabled flag, so a disabled preset value stays\n // selectable in the switcher (it is only hidden from the request, not from the dropdown).\n shouldInclude: (cookie) =>\n filterGlobalCookie({\n cookie: { ...cookie, isDisabled: false },\n url: requestCookieUrl.value,\n disabledGlobalCookies: {},\n }),\n }),\n)\n\nconst defaultCookies = computed<TableRow[]>(() => {\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[\n exampleKey\n ] ?? {}\n\n return globalCookieGroups.value.map((group) => ({\n name: group.name,\n value: group.selectedValue,\n globalRoute: { page: group.location, path: 'cookies' } as const,\n isReadonly: true,\n isDisabled: disabledGlobalCookies[group.name.toLowerCase()] ?? false,\n presetOptions: group.isPreset ? group.options : undefined,\n }))\n})\n\n/**\n * Switch a grouped global cookie preset to the chosen value by enabling the matching sibling and\n * disabling the rest. Selection is written back to the source `x-scalar-cookies` list so it\n * persists, and the send path drops disabled cookies, so only the selected value is sent.\n */\nconst handleSelectCookiePreset = (index: number, value: string): void => {\n const group = globalCookieGroups.value[index]\n if (!group) {\n return\n }\n\n for (const sibling of group.siblings) {\n eventBus.emit('cookie:upsert:cookie', {\n collectionType: sibling.location,\n index: sibling.index,\n payload: { isDisabled: sibling.value !== value },\n })\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// Expanded-row bookkeeping is keyed by `in:name`, which is not unique across operations or\n// examples. Reset both maps when the operation or example changes so stale delete/rename mappings\n// do not leak onto another request that happens to share a query parameter name.\nwatch(\n () => [method, path, exampleKey],\n () => {\n deletedExpandedParameterPaths.value = {}\n renamedExpandedParameterPaths.value = {}\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: {\n ...createParameterHandlers('cookie', eventBus, meta.value, {\n context: cookies.value ?? [],\n globalParameters: defaultCookies.value.length,\n }),\n selectPreset: handleSelectCookiePreset,\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 onDeleteExpandedRow: hideExpandedRowPath,\n onRenameExpandedRow: recordExpandedRowRename,\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 :defaultView=\"defaultRequestBodyView\"\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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"}
@@ -11,6 +11,12 @@ type __VLS_Props = {
11
11
  environment: XScalarEnvironment;
12
12
  /** Selected anyOf/oneOf request-body variants keyed by schema path */
13
13
  requestBodyCompositionSelection?: Record<string, number>;
14
+ /**
15
+ * Initial view for structured (JSON/YAML) bodies. Comes from the
16
+ * `x-scalar-default-request-body-view` document extension and falls back to `raw`
17
+ * whenever the body cannot be shown as a form.
18
+ */
19
+ defaultView?: 'form' | 'raw';
14
20
  };
15
21
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
16
22
  "update:formValue": (payload: Pick<{
@@ -1 +1 @@
1
- {"version":3,"file":"RequestBody.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"names":[],"mappings":"AAkeA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,8DAA8D,CAAA;AAkBrE,KAAK,WAAW,GAAG;IACjB,mBAAmB;IACnB,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAA;IAClB,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,WAAW,EAAE,kBAAkB,CAAA;IAC/B,sEAAsE;IACtE,+BAA+B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzD,CAAC;AAutBF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;eA5sB4B,MAAM;;;;;;;;;;;;;;;;;;eAAN,MAAM;;kFA+sBlD,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"RequestBody.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"names":[],"mappings":"AAyeA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,8DAA8D,CAAA;AAkBrE,KAAK,WAAW,GAAG;IACjB,mBAAmB;IACnB,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAA;IAClB,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,WAAW,EAAE,kBAAkB,CAAA;IAC/B,sEAAsE;IACtE,+BAA+B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;CAC7B,CAAC;AAwtBF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;eA5sB4B,MAAM;;;;;;;;;;;;;;;;;;eAAN,MAAM;;kFA+sBlD,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -2,7 +2,7 @@ import _plugin_vue_export_helper_default from "../../../../_virtual/_plugin-vue_
2
2
  import RequestBody_vue_vue_type_script_setup_true_lang_default from "./RequestBody.vue.script.js";
3
3
  /* empty css */
4
4
  //#region src/v2/blocks/request-block/components/RequestBody.vue
5
- var RequestBody_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RequestBody_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-c8ab7ba2"]]);
5
+ var RequestBody_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RequestBody_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-928dfb0c"]]);
6
6
  //#endregion
7
7
  export { RequestBody_default as default };
8
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"RequestBody.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSchemaExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\nimport { computed, ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport RequestBodyStructured from '@/v2/blocks/request-block/components/RequestBodyStructured.vue'\nimport RequestBodyViewToggle from '@/v2/blocks/request-block/components/RequestBodyViewToggle.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n title,\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n}>()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(CONTENT_TYPES).map(([id, label]) => ({\n id,\n label,\n }))\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n CONTENT_TYPES[essence as keyof typeof CONTENT_TYPES] ?? essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const value = example.value.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema,\n )\n})\n\n/** Codec for structured (JSON/YAML) bodies, undefined for everything else */\nconst structuredCodec = computed(() =>\n selectedContentType.value === 'none'\n ? undefined\n : getStructuredBodyCodec(selectedContentType.value),\n)\n\n/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */\nconst parsedBody = computed<{ ok: boolean; value?: unknown }>(() => {\n const codec = structuredCodec.value\n if (!codec) {\n return { ok: false }\n }\n\n const raw = example.value?.value\n // An empty body is still form-editable: rows come from the schema.\n if (raw === undefined || raw === null || raw === '') {\n return { ok: true, value: {} }\n }\n if (typeof raw === 'string') {\n try {\n return { ok: true, value: codec.parse(raw) }\n } catch {\n return { ok: false }\n }\n }\n // Spec-provided examples can be stored as objects directly.\n return { ok: true, value: raw }\n})\n\n/**\n * An edited example normally takes precedence over schema-generated data. When a different\n * composition branch is selected, however, keeping that example leaves the editor showing the\n * previous branch. Track the selection itself instead of inspecting discriminator fields so this\n * also works for compositions whose members are distinguished only by their shape.\n */\nconst compositionSelectionKey = computed(() =>\n JSON.stringify(requestBodyCompositionSelection ?? {}),\n)\n\n// Switching operations changes the body (and often the selection) at once, which is not a branch\n// change, so we only regenerate the body when the selection changes while the operation and example\n// stay the same. Watching all three together and comparing against the watcher's own previous values\n// keeps this correct regardless of the order in which props update or watchers flush.\nwatch(\n [() => requestBody, () => exampleKey, compositionSelectionKey],\n (\n [, , selection],\n [previousRequestBody, previousExampleKey, previousSelection],\n ) => {\n const operationChanged =\n requestBody !== previousRequestBody || exampleKey !== previousExampleKey\n const selectionChanged = selection !== previousSelection\n\n // Only a genuine branch switch within the same operation should reset the edited body. An empty\n // selection means there is no composition to switch between (or the selection was cleared on an\n // operation change), so there is nothing to reset.\n if (\n operationChanged ||\n !selectionChanged ||\n Object.keys(requestBodyCompositionSelection ?? {}).length === 0\n ) {\n return\n }\n\n const codec = structuredCodec.value\n if (!requestBody || !codec) {\n return\n }\n\n // Regenerate through the same helper the initial example uses so a reset produces exactly the\n // value a fresh render of this branch would, rather than a hand-copied second code path.\n const selectedValue = getSchemaExampleFromBody(\n requestBody,\n selectedContentType.value,\n requestBodyCompositionSelection,\n )\n\n emits('update:value', {\n // A branch with no writable content generates `null`/`undefined`; clear the editor rather than\n // writing the literal text `null` or leaving the previously selected branch's body behind.\n payload: selectedValue == null ? '' : codec.stringify(selectedValue),\n contentType: selectedContentType.value,\n })\n },\n)\n\n/** The form view only works on an object-shaped body root */\nconst isFormViewAvailable = computed(\n () => parsedBody.value.ok && isObject(parsedBody.value.value),\n)\n\n/**\n * Show the Form/Raw toggle for structured bodies that are (or per schema should be)\n * an object at the root. Non-object roots (arrays, primitives) stay raw-only.\n */\nconst showBodyViewToggle = computed(\n () =>\n Boolean(structuredCodec.value) &&\n (isFormViewAvailable.value ||\n Boolean(bodySchema.value && isObjectSchema(bodySchema.value))),\n)\n\n/** Selected body view, raw by default so existing behavior is unchanged */\nconst bodyView = ref<'form' | 'raw'>('raw')\n\n// Fall back to raw when the form view stops being available (e.g. the content type\n// changed to a non-structured one, or an external edit made the body unparseable).\nwatch(isFormViewAvailable, (ok) => {\n if (!ok) {\n bodyView.value = 'raw'\n }\n})\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n <RequestBodyViewToggle\n v-if=\"showBodyViewToggle\"\n :disabled=\"!isFormViewAvailable\"\n :modelValue=\"bodyView\"\n @update:modelValue=\"(v) => (bodyView = v)\" />\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>No Body</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n Delete\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <!-- Schema-driven form view for structured (JSON/YAML) bodies -->\n <RequestBodyStructured\n v-if=\"bodyView === 'form' && isFormViewAvailable\"\n :bodySchema\n :contentType=\"selectedContentType\"\n :environment\n :parsedValue=\"parsedBody.value\"\n @update:value=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <CodeInput\n v-else\n class=\"border-t px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"\n contentTypeToLanguageMap[\n selectedContentType as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext'\n \"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"RequestBody.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSchemaExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\nimport { computed, ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport RequestBodyStructured from '@/v2/blocks/request-block/components/RequestBodyStructured.vue'\nimport RequestBodyViewToggle from '@/v2/blocks/request-block/components/RequestBodyViewToggle.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n title,\n defaultView = 'raw',\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /**\n * Initial view for structured (JSON/YAML) bodies. Comes from the\n * `x-scalar-default-request-body-view` document extension and falls back to `raw`\n * whenever the body cannot be shown as a form.\n */\n defaultView?: 'form' | 'raw'\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n}>()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(CONTENT_TYPES).map(([id, label]) => ({\n id,\n label,\n }))\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n CONTENT_TYPES[essence as keyof typeof CONTENT_TYPES] ?? essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const value = example.value.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema,\n )\n})\n\n/** Codec for structured (JSON/YAML) bodies, undefined for everything else */\nconst structuredCodec = computed(() =>\n selectedContentType.value === 'none'\n ? undefined\n : getStructuredBodyCodec(selectedContentType.value),\n)\n\n/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */\nconst parsedBody = computed<{ ok: boolean; value?: unknown }>(() => {\n const codec = structuredCodec.value\n if (!codec) {\n return { ok: false }\n }\n\n const raw = example.value?.value\n // An empty body is still form-editable: rows come from the schema.\n if (raw === undefined || raw === null || raw === '') {\n return { ok: true, value: {} }\n }\n if (typeof raw === 'string') {\n try {\n return { ok: true, value: codec.parse(raw) }\n } catch {\n return { ok: false }\n }\n }\n // Spec-provided examples can be stored as objects directly.\n return { ok: true, value: raw }\n})\n\n/**\n * An edited example normally takes precedence over schema-generated data. When a different\n * composition branch is selected, however, keeping that example leaves the editor showing the\n * previous branch. Track the selection itself instead of inspecting discriminator fields so this\n * also works for compositions whose members are distinguished only by their shape.\n */\nconst compositionSelectionKey = computed(() =>\n JSON.stringify(requestBodyCompositionSelection ?? {}),\n)\n\n// Switching operations changes the body (and often the selection) at once, which is not a branch\n// change, so we only regenerate the body when the selection changes while the operation and example\n// stay the same. Watching all three together and comparing against the watcher's own previous values\n// keeps this correct regardless of the order in which props update or watchers flush.\nwatch(\n [() => requestBody, () => exampleKey, compositionSelectionKey],\n (\n [, , selection],\n [previousRequestBody, previousExampleKey, previousSelection],\n ) => {\n const operationChanged =\n requestBody !== previousRequestBody || exampleKey !== previousExampleKey\n const selectionChanged = selection !== previousSelection\n\n // Only a genuine branch switch within the same operation should reset the edited body. An empty\n // selection means there is no composition to switch between (or the selection was cleared on an\n // operation change), so there is nothing to reset.\n if (\n operationChanged ||\n !selectionChanged ||\n Object.keys(requestBodyCompositionSelection ?? {}).length === 0\n ) {\n return\n }\n\n const codec = structuredCodec.value\n if (!requestBody || !codec) {\n return\n }\n\n // Regenerate through the same helper the initial example uses so a reset produces exactly the\n // value a fresh render of this branch would, rather than a hand-copied second code path.\n const selectedValue = getSchemaExampleFromBody(\n requestBody,\n selectedContentType.value,\n requestBodyCompositionSelection,\n )\n\n emits('update:value', {\n // A branch with no writable content generates `null`/`undefined`; clear the editor rather than\n // writing the literal text `null` or leaving the previously selected branch's body behind.\n payload: selectedValue == null ? '' : codec.stringify(selectedValue),\n contentType: selectedContentType.value,\n })\n },\n)\n\n/** The form view only works on an object-shaped body root */\nconst isFormViewAvailable = computed(\n () => parsedBody.value.ok && isObject(parsedBody.value.value),\n)\n\n/**\n * Show the Form/Raw toggle for structured bodies that are (or per schema should be)\n * an object at the root. Non-object roots (arrays, primitives) stay raw-only.\n */\nconst showBodyViewToggle = computed(\n () =>\n Boolean(structuredCodec.value) &&\n (isFormViewAvailable.value ||\n Boolean(bodySchema.value && isObjectSchema(bodySchema.value))),\n)\n\n/** Selected body view, seeded from the document default (raw unless configured) */\nconst bodyView = ref<'form' | 'raw'>(defaultView)\n\n// Fall back to raw when the form view stops being available (e.g. the content type\n// changed to a non-structured one, or an external edit made the body unparseable).\nwatch(isFormViewAvailable, (ok) => {\n if (!ok) {\n bodyView.value = 'raw'\n }\n})\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n <RequestBodyViewToggle\n v-if=\"showBodyViewToggle\"\n :disabled=\"!isFormViewAvailable\"\n :modelValue=\"bodyView\"\n @update:modelValue=\"(v) => (bodyView = v)\" />\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>No Body</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n Delete\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <!-- Schema-driven form view for structured (JSON/YAML) bodies -->\n <RequestBodyStructured\n v-if=\"bodyView === 'form' && isFormViewAvailable\"\n :bodySchema\n :contentType=\"selectedContentType\"\n :environment\n :parsedValue=\"parsedBody.value\"\n @update:value=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <CodeInput\n v-else\n class=\"border-t px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"\n contentTypeToLanguageMap[\n selectedContentType as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext'\n \"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":""}
@@ -38,7 +38,8 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
38
38
  exampleKey: {},
39
39
  title: {},
40
40
  environment: {},
41
- requestBodyCompositionSelection: {}
41
+ requestBodyCompositionSelection: {},
42
+ defaultView: { default: "raw" }
42
43
  },
43
44
  emits: [
44
45
  "update:contentType",
@@ -168,8 +169,8 @@ var RequestBody_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
168
169
  * an object at the root. Non-object roots (arrays, primitives) stay raw-only.
169
170
  */
170
171
  const showBodyViewToggle = computed(() => Boolean(structuredCodec.value) && (isFormViewAvailable.value || Boolean(bodySchema.value && isObjectSchema(bodySchema.value))));
171
- /** Selected body view, raw by default so existing behavior is unchanged */
172
- const bodyView = ref("raw");
172
+ /** Selected body view, seeded from the document default (raw unless configured) */
173
+ const bodyView = ref(__props.defaultView);
173
174
  watch(isFormViewAvailable, (ok) => {
174
175
  if (!ok) bodyView.value = "raw";
175
176
  });
@@ -1 +1 @@
1
- {"version":3,"file":"RequestBody.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSchemaExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\nimport { computed, ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport RequestBodyStructured from '@/v2/blocks/request-block/components/RequestBodyStructured.vue'\nimport RequestBodyViewToggle from '@/v2/blocks/request-block/components/RequestBodyViewToggle.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n title,\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n}>()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(CONTENT_TYPES).map(([id, label]) => ({\n id,\n label,\n }))\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n CONTENT_TYPES[essence as keyof typeof CONTENT_TYPES] ?? essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const value = example.value.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema,\n )\n})\n\n/** Codec for structured (JSON/YAML) bodies, undefined for everything else */\nconst structuredCodec = computed(() =>\n selectedContentType.value === 'none'\n ? undefined\n : getStructuredBodyCodec(selectedContentType.value),\n)\n\n/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */\nconst parsedBody = computed<{ ok: boolean; value?: unknown }>(() => {\n const codec = structuredCodec.value\n if (!codec) {\n return { ok: false }\n }\n\n const raw = example.value?.value\n // An empty body is still form-editable: rows come from the schema.\n if (raw === undefined || raw === null || raw === '') {\n return { ok: true, value: {} }\n }\n if (typeof raw === 'string') {\n try {\n return { ok: true, value: codec.parse(raw) }\n } catch {\n return { ok: false }\n }\n }\n // Spec-provided examples can be stored as objects directly.\n return { ok: true, value: raw }\n})\n\n/**\n * An edited example normally takes precedence over schema-generated data. When a different\n * composition branch is selected, however, keeping that example leaves the editor showing the\n * previous branch. Track the selection itself instead of inspecting discriminator fields so this\n * also works for compositions whose members are distinguished only by their shape.\n */\nconst compositionSelectionKey = computed(() =>\n JSON.stringify(requestBodyCompositionSelection ?? {}),\n)\n\n// Switching operations changes the body (and often the selection) at once, which is not a branch\n// change, so we only regenerate the body when the selection changes while the operation and example\n// stay the same. Watching all three together and comparing against the watcher's own previous values\n// keeps this correct regardless of the order in which props update or watchers flush.\nwatch(\n [() => requestBody, () => exampleKey, compositionSelectionKey],\n (\n [, , selection],\n [previousRequestBody, previousExampleKey, previousSelection],\n ) => {\n const operationChanged =\n requestBody !== previousRequestBody || exampleKey !== previousExampleKey\n const selectionChanged = selection !== previousSelection\n\n // Only a genuine branch switch within the same operation should reset the edited body. An empty\n // selection means there is no composition to switch between (or the selection was cleared on an\n // operation change), so there is nothing to reset.\n if (\n operationChanged ||\n !selectionChanged ||\n Object.keys(requestBodyCompositionSelection ?? {}).length === 0\n ) {\n return\n }\n\n const codec = structuredCodec.value\n if (!requestBody || !codec) {\n return\n }\n\n // Regenerate through the same helper the initial example uses so a reset produces exactly the\n // value a fresh render of this branch would, rather than a hand-copied second code path.\n const selectedValue = getSchemaExampleFromBody(\n requestBody,\n selectedContentType.value,\n requestBodyCompositionSelection,\n )\n\n emits('update:value', {\n // A branch with no writable content generates `null`/`undefined`; clear the editor rather than\n // writing the literal text `null` or leaving the previously selected branch's body behind.\n payload: selectedValue == null ? '' : codec.stringify(selectedValue),\n contentType: selectedContentType.value,\n })\n },\n)\n\n/** The form view only works on an object-shaped body root */\nconst isFormViewAvailable = computed(\n () => parsedBody.value.ok && isObject(parsedBody.value.value),\n)\n\n/**\n * Show the Form/Raw toggle for structured bodies that are (or per schema should be)\n * an object at the root. Non-object roots (arrays, primitives) stay raw-only.\n */\nconst showBodyViewToggle = computed(\n () =>\n Boolean(structuredCodec.value) &&\n (isFormViewAvailable.value ||\n Boolean(bodySchema.value && isObjectSchema(bodySchema.value))),\n)\n\n/** Selected body view, raw by default so existing behavior is unchanged */\nconst bodyView = ref<'form' | 'raw'>('raw')\n\n// Fall back to raw when the form view stops being available (e.g. the content type\n// changed to a non-structured one, or an external edit made the body unparseable).\nwatch(isFormViewAvailable, (ok) => {\n if (!ok) {\n bodyView.value = 'raw'\n }\n})\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n <RequestBodyViewToggle\n v-if=\"showBodyViewToggle\"\n :disabled=\"!isFormViewAvailable\"\n :modelValue=\"bodyView\"\n @update:modelValue=\"(v) => (bodyView = v)\" />\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>No Body</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n Delete\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <!-- Schema-driven form view for structured (JSON/YAML) bodies -->\n <RequestBodyStructured\n v-if=\"bodyView === 'form' && isFormViewAvailable\"\n :bodySchema\n :contentType=\"selectedContentType\"\n :environment\n :parsedValue=\"parsedBody.value\"\n @update:value=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <CodeInput\n v-else\n class=\"border-t px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"\n contentTypeToLanguageMap[\n selectedContentType as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext'\n \"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RequestBody.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSchemaExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\nimport { computed, ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport RequestBodyStructured from '@/v2/blocks/request-block/components/RequestBodyStructured.vue'\nimport RequestBodyViewToggle from '@/v2/blocks/request-block/components/RequestBodyViewToggle.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n title,\n defaultView = 'raw',\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /**\n * Initial view for structured (JSON/YAML) bodies. Comes from the\n * `x-scalar-default-request-body-view` document extension and falls back to `raw`\n * whenever the body cannot be shown as a form.\n */\n defaultView?: 'form' | 'raw'\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n}>()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(CONTENT_TYPES).map(([id, label]) => ({\n id,\n label,\n }))\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n CONTENT_TYPES[essence as keyof typeof CONTENT_TYPES] ?? essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const value = example.value.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema,\n )\n})\n\n/** Codec for structured (JSON/YAML) bodies, undefined for everything else */\nconst structuredCodec = computed(() =>\n selectedContentType.value === 'none'\n ? undefined\n : getStructuredBodyCodec(selectedContentType.value),\n)\n\n/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */\nconst parsedBody = computed<{ ok: boolean; value?: unknown }>(() => {\n const codec = structuredCodec.value\n if (!codec) {\n return { ok: false }\n }\n\n const raw = example.value?.value\n // An empty body is still form-editable: rows come from the schema.\n if (raw === undefined || raw === null || raw === '') {\n return { ok: true, value: {} }\n }\n if (typeof raw === 'string') {\n try {\n return { ok: true, value: codec.parse(raw) }\n } catch {\n return { ok: false }\n }\n }\n // Spec-provided examples can be stored as objects directly.\n return { ok: true, value: raw }\n})\n\n/**\n * An edited example normally takes precedence over schema-generated data. When a different\n * composition branch is selected, however, keeping that example leaves the editor showing the\n * previous branch. Track the selection itself instead of inspecting discriminator fields so this\n * also works for compositions whose members are distinguished only by their shape.\n */\nconst compositionSelectionKey = computed(() =>\n JSON.stringify(requestBodyCompositionSelection ?? {}),\n)\n\n// Switching operations changes the body (and often the selection) at once, which is not a branch\n// change, so we only regenerate the body when the selection changes while the operation and example\n// stay the same. Watching all three together and comparing against the watcher's own previous values\n// keeps this correct regardless of the order in which props update or watchers flush.\nwatch(\n [() => requestBody, () => exampleKey, compositionSelectionKey],\n (\n [, , selection],\n [previousRequestBody, previousExampleKey, previousSelection],\n ) => {\n const operationChanged =\n requestBody !== previousRequestBody || exampleKey !== previousExampleKey\n const selectionChanged = selection !== previousSelection\n\n // Only a genuine branch switch within the same operation should reset the edited body. An empty\n // selection means there is no composition to switch between (or the selection was cleared on an\n // operation change), so there is nothing to reset.\n if (\n operationChanged ||\n !selectionChanged ||\n Object.keys(requestBodyCompositionSelection ?? {}).length === 0\n ) {\n return\n }\n\n const codec = structuredCodec.value\n if (!requestBody || !codec) {\n return\n }\n\n // Regenerate through the same helper the initial example uses so a reset produces exactly the\n // value a fresh render of this branch would, rather than a hand-copied second code path.\n const selectedValue = getSchemaExampleFromBody(\n requestBody,\n selectedContentType.value,\n requestBodyCompositionSelection,\n )\n\n emits('update:value', {\n // A branch with no writable content generates `null`/`undefined`; clear the editor rather than\n // writing the literal text `null` or leaving the previously selected branch's body behind.\n payload: selectedValue == null ? '' : codec.stringify(selectedValue),\n contentType: selectedContentType.value,\n })\n },\n)\n\n/** The form view only works on an object-shaped body root */\nconst isFormViewAvailable = computed(\n () => parsedBody.value.ok && isObject(parsedBody.value.value),\n)\n\n/**\n * Show the Form/Raw toggle for structured bodies that are (or per schema should be)\n * an object at the root. Non-object roots (arrays, primitives) stay raw-only.\n */\nconst showBodyViewToggle = computed(\n () =>\n Boolean(structuredCodec.value) &&\n (isFormViewAvailable.value ||\n Boolean(bodySchema.value && isObjectSchema(bodySchema.value))),\n)\n\n/** Selected body view, seeded from the document default (raw unless configured) */\nconst bodyView = ref<'form' | 'raw'>(defaultView)\n\n// Fall back to raw when the form view stops being available (e.g. the content type\n// changed to a non-structured one, or an external edit made the body unparseable).\nwatch(isFormViewAvailable, (ok) => {\n if (!ok) {\n bodyView.value = 'raw'\n }\n})\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n <RequestBodyViewToggle\n v-if=\"showBodyViewToggle\"\n :disabled=\"!isFormViewAvailable\"\n :modelValue=\"bodyView\"\n @update:modelValue=\"(v) => (bodyView = v)\" />\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>No Body</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n Delete\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>Select File</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <!-- Schema-driven form view for structured (JSON/YAML) bodies -->\n <RequestBodyStructured\n v-if=\"bodyView === 'form' && isFormViewAvailable\"\n :bodySchema\n :contentType=\"selectedContentType\"\n :environment\n :parsedValue=\"parsedBody.value\"\n @update:value=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <CodeInput\n v-else\n class=\"border-t px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"\n contentTypeToLanguageMap[\n selectedContentType as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext'\n \"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-form-body-rows.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAG/G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0DAA0D,CAAA;AAExF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,WAQ5C,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,MAAM,EAAE,YAAY,GAAG,SAAS,CAAA;IAChC,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,YAAY,EACpB,aAAY,MAAM,EAAO,EACzB,wBAAqB,KACpB,OAAO,EA6BT,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,OAAO,EAChB,QAAQ,YAAY,GAAG,SAAS,EAChC,aAAY,MAAM,EAAO,KACxB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,EAqCpC,CAAA;AAED,uGAAuG;AACvG,eAAO,MAAM,eAAe,GAC1B,SAAS,aAAa,GAAG,SAAS,GAAG,IAAI,EACzC,aAAa,MAAM,EACnB,iBAAiB,YAAY,KAC5B,QAAQ,EAuFV,CAAA"}
1
+ {"version":3,"file":"get-form-body-rows.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAG/G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0DAA0D,CAAA;AAExF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,WAQ5C,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,MAAM,EAAE,YAAY,GAAG,SAAS,CAAA;IAChC,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,YAAY,EACpB,aAAY,MAAM,EAAO,EACzB,wBAAqB,KACpB,OAAO,EA6BT,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,OAAO,EAChB,QAAQ,YAAY,GAAG,SAAS,EAChC,aAAY,MAAM,EAAO,KACxB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,EAqCpC,CAAA;AAED,uGAAuG;AACvG,eAAO,MAAM,eAAe,GAC1B,SAAS,aAAa,GAAG,SAAS,GAAG,IAAI,EACzC,aAAa,MAAM,EACnB,iBAAiB,YAAY,KAC5B,QAAQ,EAqGV,CAAA"}
@@ -77,11 +77,11 @@ var getFormBodyRows = (example, contentType, formBodySchema) => {
77
77
  const requiredSet = schemaWithProperties ? new Set(schemaWithProperties.required ?? []) : void 0;
78
78
  const leafByDottedName = /* @__PURE__ */ new Map();
79
79
  if (schemaWithProperties) for (const leaf of collectLeafProperties(schemaWithProperties)) leafByDottedName.set(leaf.path.join("."), leaf);
80
- const mapRow = ({ name, value, isDisabled = false }) => {
80
+ const mapRow = ({ name, value, isDisabled }) => {
81
81
  const row = {
82
82
  name,
83
83
  value,
84
- isDisabled
84
+ isDisabled: isDisabled ?? false
85
85
  };
86
86
  if (!schemaWithProperties || !name) return row;
87
87
  const leaf = leafByDottedName.get(name);
@@ -89,6 +89,7 @@ var getFormBodyRows = (example, contentType, formBodySchema) => {
89
89
  row.schema = propSchema;
90
90
  row.description = propSchema?.description;
91
91
  row.isRequired = leaf?.isRequired ?? requiredSet?.has(name) ?? false;
92
+ if (isDisabled === void 0 && schemaWithProperties.properties && Object.hasOwn(schemaWithProperties.properties, name)) row.isDisabled = !row.isRequired;
92
93
  return row;
93
94
  };
94
95
  if (Array.isArray(example.value)) return example.value.map((exampleValue) => {
@@ -1 +1 @@
1
- {"version":3,"file":"get-form-body-rows.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"sourcesContent":["import { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { ExampleObject, SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\n\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\n\nexport const stringifyValue = (value: unknown) => {\n if (value instanceof File) {\n return value.name\n }\n if (typeof value === 'object' && value !== null) {\n return JSON.stringify(value)\n }\n return String(value)\n}\n\nexport type LeafRow = {\n path: string[]\n schema: SchemaObject | undefined\n isRequired: boolean\n}\n\n/**\n * Walk a (possibly nested) object schema and collect one entry per leaf property.\n *\n * A \"leaf\" is any property whose resolved schema is not an `{ type: 'object' }` schema\n * with named `properties` — i.e. primitives, arrays, files, and `additionalProperties`\n * placeholders all stop the recursion. Object properties recurse, so the widget example\n * (`file` + `props.{name,description,created_at}`) yields four leaves.\n */\nexport const collectLeafProperties = (\n schema: SchemaObject,\n parentPath: string[] = [],\n parentRequired = true,\n): LeafRow[] => {\n // Nothing to walk if this is not an object schema or has no declared properties.\n if (!isObjectSchema(schema) || !schema.properties) {\n return []\n }\n\n // `required` is a list of property names on *this* schema, so build it once per level.\n const requiredSet = new Set(schema.required ?? [])\n const leaves: LeafRow[] = []\n\n for (const [key, rawChildSchema] of objectEntries(schema.properties)) {\n const childSchema = resolve.schema(rawChildSchema)\n const path = [...parentPath, String(key)]\n\n // A leaf is only required when *every* ancestor was also required — a non-required\n // parent makes the whole subtree optional regardless of inner `required` lists.\n const isRequired = parentRequired && requiredSet.has(String(key))\n\n // Nested objects with named properties recurse so each leaf gets its own row;\n // everything else (primitives, arrays, files, additionalProperties placeholders)\n // is treated as a leaf and stops the descent.\n if (childSchema && isObjectSchema(childSchema) && childSchema.properties) {\n leaves.push(...collectLeafProperties(childSchema, path, isRequired))\n } else {\n leaves.push({ path, schema: childSchema, isRequired })\n }\n }\n\n return leaves\n}\n\n/**\n * Walk a schema + example together and collect one row per leaf, including example-only\n * keys that the schema does not declare. Schema-declared properties come first in schema\n * order; extras present in the example follow in example order at each level.\n *\n * Unlike `collectLeafProperties` (which is schema-only and feeds the dotted-name index),\n * this walker drives what the user actually sees in the form: a row for every leaf the\n * example contains, even when the schema is partial or out of date.\n */\nexport const collectExampleRows = (\n example: unknown,\n schema: SchemaObject | undefined,\n parentPath: string[] = [],\n): { path: string[]; value: unknown }[] => {\n const rows: { path: string[]; value: unknown }[] = []\n const exampleEntries = isObject(example) ? objectEntries(example as Record<string, unknown>) : []\n const exampleByKey = new Map<string, unknown>(exampleEntries.map(([key, value]) => [String(key), value]))\n const declaredKeys = new Set<string>()\n const schemaProperties = schema && isObjectSchema(schema) ? (schema.properties ?? {}) : {}\n\n // Schema-declared properties first, in schema order. Nested object schemas recurse so\n // each leaf gets its own row; otherwise the schema-declared key is a leaf and uses the\n // matching example value (or `undefined` when the example does not provide one).\n for (const [key, rawChildSchema] of objectEntries(schemaProperties)) {\n const keyStr = String(key)\n declaredKeys.add(keyStr)\n const childSchema = resolve.schema(rawChildSchema)\n const path = [...parentPath, keyStr]\n const exampleSubvalue = exampleByKey.get(keyStr)\n\n if (childSchema && isObjectSchema(childSchema) && childSchema.properties) {\n rows.push(...collectExampleRows(exampleSubvalue, childSchema, path))\n } else {\n rows.push({ path, value: exampleSubvalue })\n }\n }\n\n // Example-only properties at this level (preserve example order). Their inner shape is\n // not descended into — without a schema saying \"this is a nested object\" we have no\n // signal to flatten, so an object value becomes a single JSON-stringified row, matching\n // the schema-less fallback below.\n for (const [key, value] of exampleEntries) {\n const keyStr = String(key)\n if (declaredKeys.has(keyStr)) {\n continue\n }\n rows.push({ path: [...parentPath, keyStr], value })\n }\n\n return rows\n}\n\n/** Build the table rows for the form data, optionally enriched with schema (e.g. enum) per property */\nexport const getFormBodyRows = (\n example: ExampleObject | undefined | null,\n contentType: string,\n formBodySchema?: SchemaObject,\n): TableRow[] => {\n // We only need the rows for formData\n if (\n !example?.value ||\n (contentType !== 'multipart/form-data' && contentType !== 'application/x-www-form-urlencoded')\n ) {\n return []\n }\n\n // Get all the schema properties if the schema is an object schema\n const schemaWithProperties = formBodySchema && isObjectSchema(formBodySchema) ? formBodySchema : undefined\n const requiredSet = schemaWithProperties ? new Set(schemaWithProperties.required ?? []) : undefined\n\n // Pre-compute the leaf-by-dotted-name index up front so both the array and the\n // schema-driven branches can pull each row's leaf schema and required flag without\n // re-walking the schema. Without this, edited rows lose the per-leaf `Required`\n // badge because a top-level `properties[name]` lookup misses dotted names.\n const leafByDottedName = new Map<string, LeafRow>()\n if (schemaWithProperties) {\n for (const leaf of collectLeafProperties(schemaWithProperties)) {\n leafByDottedName.set(leaf.path.join('.'), leaf)\n }\n }\n\n const mapRow = ({\n name,\n value,\n isDisabled = false,\n }: {\n name: string\n value: string | File\n isDisabled?: boolean\n }): TableRow => {\n const row: TableRow = { name, value, isDisabled }\n if (!schemaWithProperties || !name) {\n return row\n }\n // Prefer the pre-resolved leaf (handles dotted names like `props.name`); fall back\n // to a top-level property lookup so user-added rows keep working.\n const leaf = leafByDottedName.get(name)\n const propSchema = leaf?.schema ?? resolve.schema(schemaWithProperties.properties?.[name])\n row.schema = propSchema\n row.description = propSchema?.description\n row.isRequired = leaf?.isRequired ?? requiredSet?.has(name) ?? false\n return row\n }\n\n // We have form data stored as an array\n if (Array.isArray(example.value)) {\n return example.value.map((exampleValue) => {\n if (isObject(exampleValue)) {\n const name = String(exampleValue.name)\n const value = exampleValue.value instanceof File ? exampleValue.value : String(exampleValue.value)\n const isDisabled = Boolean(exampleValue.isDisabled)\n return mapRow({ name, value, isDisabled })\n }\n return { name: '', value: exampleValue, isDisabled: false }\n })\n }\n\n // Schema-driven path: when the form body schema describes nested objects, emit one row\n // per leaf so users can edit individual fields. The dotted name (`props.name`) is folded\n // back into a single JSON multipart part by `build-request-body.ts` before the request is\n // sent. Gated to `multipart/form-data` because urlencoded has no regrouping step on send,\n // so dotted leaves would otherwise reach the wire as separate `props.name`-style fields.\n //\n // We walk the example alongside the schema so example-only properties (top-level extras\n // or undeclared keys inside a nested object) are still visible and editable instead of\n // being silently dropped to whatever the schema happens to declare.\n if (contentType === 'multipart/form-data' && schemaWithProperties && typeof example.value === 'object') {\n return collectExampleRows(example.value, schemaWithProperties).map(({ path, value }) => {\n // Missing values and explicit `null` (e.g. for a nullable schema) render as empty\n // inputs so the user can type a value rather than seeing the string \"null\".\n const rendered =\n value instanceof File ? value : value === undefined || value === null ? '' : stringifyValue(value)\n return mapRow({ name: path.join('.'), value: rendered })\n })\n }\n\n // We got an object try to convert it to an array of rows\n if (typeof example.value === 'object' && example.value) {\n return objectEntries(example.value).map(([key, value]) =>\n mapRow({ name: String(key), value: stringifyValue(value) }),\n )\n }\n\n return []\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"get-form-body-rows.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"sourcesContent":["import { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { ExampleObject, SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\n\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\n\nexport const stringifyValue = (value: unknown) => {\n if (value instanceof File) {\n return value.name\n }\n if (typeof value === 'object' && value !== null) {\n return JSON.stringify(value)\n }\n return String(value)\n}\n\nexport type LeafRow = {\n path: string[]\n schema: SchemaObject | undefined\n isRequired: boolean\n}\n\n/**\n * Walk a (possibly nested) object schema and collect one entry per leaf property.\n *\n * A \"leaf\" is any property whose resolved schema is not an `{ type: 'object' }` schema\n * with named `properties` — i.e. primitives, arrays, files, and `additionalProperties`\n * placeholders all stop the recursion. Object properties recurse, so the widget example\n * (`file` + `props.{name,description,created_at}`) yields four leaves.\n */\nexport const collectLeafProperties = (\n schema: SchemaObject,\n parentPath: string[] = [],\n parentRequired = true,\n): LeafRow[] => {\n // Nothing to walk if this is not an object schema or has no declared properties.\n if (!isObjectSchema(schema) || !schema.properties) {\n return []\n }\n\n // `required` is a list of property names on *this* schema, so build it once per level.\n const requiredSet = new Set(schema.required ?? [])\n const leaves: LeafRow[] = []\n\n for (const [key, rawChildSchema] of objectEntries(schema.properties)) {\n const childSchema = resolve.schema(rawChildSchema)\n const path = [...parentPath, String(key)]\n\n // A leaf is only required when *every* ancestor was also required — a non-required\n // parent makes the whole subtree optional regardless of inner `required` lists.\n const isRequired = parentRequired && requiredSet.has(String(key))\n\n // Nested objects with named properties recurse so each leaf gets its own row;\n // everything else (primitives, arrays, files, additionalProperties placeholders)\n // is treated as a leaf and stops the descent.\n if (childSchema && isObjectSchema(childSchema) && childSchema.properties) {\n leaves.push(...collectLeafProperties(childSchema, path, isRequired))\n } else {\n leaves.push({ path, schema: childSchema, isRequired })\n }\n }\n\n return leaves\n}\n\n/**\n * Walk a schema + example together and collect one row per leaf, including example-only\n * keys that the schema does not declare. Schema-declared properties come first in schema\n * order; extras present in the example follow in example order at each level.\n *\n * Unlike `collectLeafProperties` (which is schema-only and feeds the dotted-name index),\n * this walker drives what the user actually sees in the form: a row for every leaf the\n * example contains, even when the schema is partial or out of date.\n */\nexport const collectExampleRows = (\n example: unknown,\n schema: SchemaObject | undefined,\n parentPath: string[] = [],\n): { path: string[]; value: unknown }[] => {\n const rows: { path: string[]; value: unknown }[] = []\n const exampleEntries = isObject(example) ? objectEntries(example as Record<string, unknown>) : []\n const exampleByKey = new Map<string, unknown>(exampleEntries.map(([key, value]) => [String(key), value]))\n const declaredKeys = new Set<string>()\n const schemaProperties = schema && isObjectSchema(schema) ? (schema.properties ?? {}) : {}\n\n // Schema-declared properties first, in schema order. Nested object schemas recurse so\n // each leaf gets its own row; otherwise the schema-declared key is a leaf and uses the\n // matching example value (or `undefined` when the example does not provide one).\n for (const [key, rawChildSchema] of objectEntries(schemaProperties)) {\n const keyStr = String(key)\n declaredKeys.add(keyStr)\n const childSchema = resolve.schema(rawChildSchema)\n const path = [...parentPath, keyStr]\n const exampleSubvalue = exampleByKey.get(keyStr)\n\n if (childSchema && isObjectSchema(childSchema) && childSchema.properties) {\n rows.push(...collectExampleRows(exampleSubvalue, childSchema, path))\n } else {\n rows.push({ path, value: exampleSubvalue })\n }\n }\n\n // Example-only properties at this level (preserve example order). Their inner shape is\n // not descended into — without a schema saying \"this is a nested object\" we have no\n // signal to flatten, so an object value becomes a single JSON-stringified row, matching\n // the schema-less fallback below.\n for (const [key, value] of exampleEntries) {\n const keyStr = String(key)\n if (declaredKeys.has(keyStr)) {\n continue\n }\n rows.push({ path: [...parentPath, keyStr], value })\n }\n\n return rows\n}\n\n/** Build the table rows for the form data, optionally enriched with schema (e.g. enum) per property */\nexport const getFormBodyRows = (\n example: ExampleObject | undefined | null,\n contentType: string,\n formBodySchema?: SchemaObject,\n): TableRow[] => {\n // We only need the rows for formData\n if (\n !example?.value ||\n (contentType !== 'multipart/form-data' && contentType !== 'application/x-www-form-urlencoded')\n ) {\n return []\n }\n\n // Get all the schema properties if the schema is an object schema\n const schemaWithProperties = formBodySchema && isObjectSchema(formBodySchema) ? formBodySchema : undefined\n const requiredSet = schemaWithProperties ? new Set(schemaWithProperties.required ?? []) : undefined\n\n // Pre-compute the leaf-by-dotted-name index up front so both the array and the\n // schema-driven branches can pull each row's leaf schema and required flag without\n // re-walking the schema. Without this, edited rows lose the per-leaf `Required`\n // badge because a top-level `properties[name]` lookup misses dotted names.\n const leafByDottedName = new Map<string, LeafRow>()\n if (schemaWithProperties) {\n for (const leaf of collectLeafProperties(schemaWithProperties)) {\n leafByDottedName.set(leaf.path.join('.'), leaf)\n }\n }\n\n const mapRow = ({\n name,\n value,\n isDisabled,\n }: {\n name: string\n value: string | File\n isDisabled?: boolean\n }): TableRow => {\n const row: TableRow = { name, value, isDisabled: isDisabled ?? false }\n if (!schemaWithProperties || !name) {\n return row\n }\n // Prefer the pre-resolved leaf (handles dotted names like `props.name`); fall back\n // to a top-level property lookup so user-added rows keep working.\n const leaf = leafByDottedName.get(name)\n const propSchema = leaf?.schema ?? resolve.schema(schemaWithProperties.properties?.[name])\n row.schema = propSchema\n row.description = propSchema?.description\n row.isRequired = leaf?.isRequired ?? requiredSet?.has(name) ?? false\n\n // Top-level optional properties default to disabled (unchecked), matching how optional\n // parameters behave. Scoped to top-level declared properties on purpose: the send side\n // (`build-request-body`) only drops top-level optional keys, so auto-disabling a nested\n // leaf (e.g. `props.name`) would leave it unchecked yet still transmitted. `Object.hasOwn`\n // (not `in`) keeps example-only keys named like `Object.prototype` members enabled. An\n // explicit `isDisabled` (from a stored form-row array) always wins.\n if (\n isDisabled === undefined &&\n schemaWithProperties.properties &&\n Object.hasOwn(schemaWithProperties.properties, name)\n ) {\n row.isDisabled = !row.isRequired\n }\n return row\n }\n\n // We have form data stored as an array\n if (Array.isArray(example.value)) {\n return example.value.map((exampleValue) => {\n if (isObject(exampleValue)) {\n const name = String(exampleValue.name)\n const value = exampleValue.value instanceof File ? exampleValue.value : String(exampleValue.value)\n const isDisabled = Boolean(exampleValue.isDisabled)\n return mapRow({ name, value, isDisabled })\n }\n return { name: '', value: exampleValue, isDisabled: false }\n })\n }\n\n // Schema-driven path: when the form body schema describes nested objects, emit one row\n // per leaf so users can edit individual fields. The dotted name (`props.name`) is folded\n // back into a single JSON multipart part by `build-request-body.ts` before the request is\n // sent. Gated to `multipart/form-data` because urlencoded has no regrouping step on send,\n // so dotted leaves would otherwise reach the wire as separate `props.name`-style fields.\n //\n // We walk the example alongside the schema so example-only properties (top-level extras\n // or undeclared keys inside a nested object) are still visible and editable instead of\n // being silently dropped to whatever the schema happens to declare.\n if (contentType === 'multipart/form-data' && schemaWithProperties && typeof example.value === 'object') {\n return collectExampleRows(example.value, schemaWithProperties).map(({ path, value }) => {\n // Missing values and explicit `null` (e.g. for a nullable schema) render as empty\n // inputs so the user can type a value rather than seeing the string \"null\".\n const rendered =\n value instanceof File ? value : value === undefined || value === null ? '' : stringifyValue(value)\n return mapRow({ name: path.join('.'), value: rendered })\n })\n }\n\n // We got an object try to convert it to an array of rows\n if (typeof example.value === 'object' && example.value) {\n return objectEntries(example.value).map(([key, value]) =>\n mapRow({ name: String(key), value: stringifyValue(value) }),\n )\n }\n\n return []\n}\n"],"mappings":"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"}