@scalar/blocks 0.1.19 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/code-example/components/CodeExample.vue.d.ts.map +1 -1
- package/dist/code-example/components/CodeExample.vue.js +1 -1
- package/dist/code-example/components/CodeExample.vue.js.map +1 -1
- package/dist/code-example/components/CodeExample.vue.script.js +125 -111
- package/dist/code-example/components/CodeExample.vue.script.js.map +1 -1
- package/dist/code-example/helpers/operation-to-har/operation-to-har.d.ts.map +1 -1
- package/dist/code-example/helpers/operation-to-har/operation-to-har.js +16 -11
- package/dist/code-example/helpers/operation-to-har/operation-to-har.js.map +1 -1
- package/dist/code-example/helpers/operation-to-har/process-body.d.ts.map +1 -1
- package/dist/code-example/helpers/operation-to-har/process-body.js +89 -68
- package/dist/code-example/helpers/operation-to-har/process-body.js.map +1 -1
- package/dist/code-example/helpers/operation-to-har/process-parameters.d.ts +2 -0
- package/dist/code-example/helpers/operation-to-har/process-parameters.d.ts.map +1 -1
- package/dist/code-example/helpers/operation-to-har/process-parameters.js +71 -53
- package/dist/code-example/helpers/operation-to-har/process-parameters.js.map +1 -1
- package/dist/code-example/mount.d.ts.map +1 -1
- package/dist/code-example/mount.js +30 -29
- package/dist/code-example/mount.js.map +1 -1
- package/dist/constants.js +1 -1
- package/dist/style.css +1 -1
- package/dist/vue-styles.css +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeExample.vue.script.js","names":["$slots"],"sources":["../../../src/code-example/components/CodeExample.vue"],"sourcesContent":["<script lang=\"ts\">\nexport type CodeExampleProps = {\n /**\n * Integration type: determines if the code sample is displayed in a client environment\n * or in an API reference environment.\n */\n integration?: 'client' | 'reference'\n /**\n * List of all http clients formatted into option groups for the client selector\n */\n clientOptions: ClientOptionGroup[]\n /**\n * Pre-selected client, this will determine which client is initially selected in the dropdown.\n * Either a built-in client id (e.g. `js/fetch`) or a custom sample id (e.g. `custom/python`).\n *\n * Typed as a plain string: unioning the (large) client-id union with the `custom/...`\n * ids overflows TypeScript's union-complexity limit inside this component's `defineProps`.\n *\n * @defaults to a custom sample if one is available, otherwise shell/curl\n */\n selectedClient?: string\n /**\n * Which server from the spec to use for the code example\n */\n selectedServer?: ServerObject | null\n /**\n * The selected content type from the requestBody.content, this will determine which examples are available\n * as well as the content type of the code example\n *\n * @defaults to the first content type if not provided\n */\n selectedContentType?: string\n /**\n * Example name to use for resolving example values for parameters AND requestBody.\n *\n * This is the document-wide selection: it is honored only when this operation defines an example\n * with the same key, so picking an example on one operation syncs the rest without blanking out\n * operations that do not share that key.\n *\n * @example \"limited\"\n * ```ts\n * parameters: {\n * name: 'foobar',\n * in: 'query',\n * examples: {\n * limited: {\n * dataValue: 10,\n * }\n * }\n * },\n * body: {\n * content: {\n * 'application/json': {\n * examples: {\n * limited: {\n * dataValue: { foo: 'bar' },\n * }\n * }\n * }\n * }\n * }\n *\n * ```\n */\n selectedExample?: string\n /**\n * Event bus\n */\n eventBus: WorkspaceEventBus\n /**\n * The security schemes which are applicable to this operation\n */\n securitySchemes: SecuritySchemeObjectSecret[]\n /**\n * HTTP method of the operation\n */\n method: HttpMethodType\n /**\n * Path of the operation\n */\n path: string\n /**\n * De-referenced OpenAPI Operation object\n */\n operation: OperationObject\n /**\n * If true and there's no example, we will display a small card with the method and path only\n */\n fallback?: boolean\n /**\n * A method to generate the label of the block, should return an html string\n */\n generateLabel?: () => string\n /**\n * If true, render this as a webhook request example\n */\n isWebhook?: boolean\n /**\n * Workspace + document cookies\n */\n globalCookies?: XScalarCookie[]\n /**\n * When the request body schema uses oneOf/anyOf, use these selected variants\n * for the example snippet (e.g. from the schema dropdowns in the API reference).\n */\n requestBodyCompositionSelection?: Record<string, number>\n}\n\n/**\n * Request Example\n *\n * The core component for rendering a request example block,\n * this component does not have much of its own state but operates on props and custom events\n *\n * @event workspace:update:selected-client - Emitted when the selected client changes\n * @event workspace:update:selected-example - Emitted when the selected example changes, so other operations can sync\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport {\n ScalarCard,\n ScalarCardFooter,\n ScalarCardHeader,\n ScalarCardSection,\n} from '@scalar/components/card'\nimport { ScalarCodeBlock } from '@scalar/components/code-block'\nimport { ScalarCombobox } from '@scalar/components/combobox'\nimport { ScalarVirtualText } from '@scalar/components/virtual-text'\nimport { freezeElement } from '@scalar/helpers/dom/freeze-element'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { ScalarIconCaretDown } from '@scalar/icons'\nimport { type WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport type { SecuritySchemeObjectSecret } from '@scalar/workspace-store/request-example'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OperationObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport {\n computed,\n onBeforeMount,\n ref,\n useId,\n watch,\n watchEffect,\n type ComponentPublicInstance,\n} from 'vue'\n\nimport { filterClientsByQuery } from '../helpers/filter-clients-by-query'\nimport { findClient } from '../helpers/find-client'\nimport { generateCodeSnippet } from '../helpers/generate-code-snippet'\nimport { getClients } from '../helpers/get-clients'\nimport { getCustomCodeSamples } from '../helpers/get-custom-code-samples'\nimport { getSecrets } from '../helpers/get-secrets'\nimport { operationToHar } from '../helpers/operation-to-har/operation-to-har'\nimport type {\n ClientOption,\n ClientOptionGroup,\n CustomClientOption,\n} from '../types'\nimport ExamplePicker from './ExamplePicker.vue'\nimport HttpMethod from './HttpMethod.vue'\n\nconst {\n integration,\n clientOptions,\n selectedClient,\n selectedServer = null,\n selectedContentType,\n selectedExample,\n securitySchemes = [],\n method,\n eventBus,\n path,\n operation,\n isWebhook,\n generateLabel,\n globalCookies,\n requestBodyCompositionSelection,\n} = defineProps<CodeExampleProps>()\n\nconst emit = defineEmits<{\n /**\n * Emitted whenever the example key actually shown for this operation changes.\n *\n * This is the resolved local key (not the raw document-wide selection), so layouts that render\n * the test-request button outside this component can open the client with the same example the\n * snippet displays.\n */\n (e: 'update:exampleKey', value: string): void\n}>()\n\ndefineSlots<{\n header: () => unknown\n footer: ({ exampleName }: { exampleName: string }) => unknown\n}>()\n\n/** Grab the examples for the given content type */\nconst requestBodyExamples = computed(() => {\n const content = getResolvedRef(operation.requestBody)?.content ?? {}\n const contentType = selectedContentType || Object.keys(content)[0]\n if (!contentType) return {}\n\n const examples = content[contentType]?.examples ?? {}\n\n return examples\n})\n\n/**\n * The example key actually shown for this operation.\n *\n * `selectedExample` is the document-wide selection that syncs across operations. We honor it only\n * when this operation actually defines that key, otherwise we keep the local example: a selection\n * on one operation should never blank out an operation that does not share the same example.\n */\nconst localExampleKey = ref('')\n\n/** Resolve the key to display, preferring the document-wide selection when this operation has it */\nconst resolveExampleKey = (preferred: string | undefined): string => {\n const keys = Object.keys(requestBodyExamples.value)\n if (preferred && keys.includes(preferred)) {\n return preferred\n }\n // Keep the current local example when it is still valid, otherwise fall back to the first one\n if (localExampleKey.value && keys.includes(localExampleKey.value)) {\n return localExampleKey.value\n }\n return keys[0] ?? ''\n}\n\n// Set the initial example from the document-wide selection, falling back to the first example\nonBeforeMount(() => {\n localExampleKey.value = resolveExampleKey(selectedExample)\n})\n\n// Follow the document-wide selection when it changes and this operation has that example\nwatch(\n () => selectedExample,\n (preferred) => {\n localExampleKey.value = resolveExampleKey(preferred)\n },\n)\n\n/** Reset the selected example key if the content type changes and the new content type doesn't have the previously selected example */\nwatch(\n () => selectedContentType,\n () => {\n if (\n !Object.keys(requestBodyExamples.value).includes(localExampleKey.value)\n ) {\n // Re-resolve so the new content type still follows the document-wide selection when it has\n // that key, instead of always snapping back to the first example\n localExampleKey.value = resolveExampleKey(selectedExample)\n }\n },\n)\n\n// Keep the parent informed of the resolved key so a test-request button rendered outside this\n// component (e.g. the classic layout header) opens the client with the example the snippet shows\nwatchEffect(() => {\n emit('update:exampleKey', localExampleKey.value)\n})\n\n/** Select an example locally and sync the choice across the document */\nconst selectExample = (key: string) => {\n localExampleKey.value = key\n eventBus.emit('workspace:update:selected-example', key)\n}\n\n/** Grab any custom code samples from the operation */\nconst customCodeSamples = computed(() => getCustomCodeSamples(operation))\n\n/** Merge custom code samples with the client options */\nconst clients = computed(() =>\n getClients(\n customCodeSamples.value.samples,\n clientOptions,\n customCodeSamples.value.label,\n ),\n)\n\n/** Total number of available clients across all option groups */\nconst clientCount = computed(() =>\n clients.value.reduce((total, group) => total + group.options.length, 0),\n)\n\n/** The locally selected client which would include code samples from this operation only */\nconst localSelectedClient = ref<ClientOption | CustomClientOption | undefined>(\n findClient(clients.value, selectedClient),\n)\n\n/**\n * Re-resolve the local client whenever the global selection or the available\n * clients change. Watching `clients` matters when navigating between operations:\n * the stored id stays the same, but the matching option (e.g. a custom sample)\n * differs per operation, so without this the snippet could go stale.\n */\nwatch([() => selectedClient, clients], ([newClient]) => {\n const client = findClient(clients.value, newClient)\n if (client) {\n localSelectedClient.value = client\n }\n})\n\n/** Generate HAR data for webhook requests */\nconst webhookHar = computed(() => {\n if (!isWebhook) return null\n\n try {\n return operationToHar({\n operation,\n method,\n path,\n example: localExampleKey.value,\n requestBodyCompositionSelection,\n // Only required parameters are shown in code examples; optional parameters\n // are omitted unless explicitly enabled via `x-disabled: false`.\n defaultDisabledParameters: true,\n })\n } catch (error) {\n console.error('[webhookHar]', error)\n return null\n }\n})\n\n/** Generate the code snippet for the selected example */\nconst generatedCode = computed<string>(() => {\n if (isWebhook) {\n return webhookHar.value?.postData?.text ?? ''\n }\n\n return generateCodeSnippet({\n // Only required parameters are shown in code examples; optional parameters\n // are omitted unless explicitly enabled via `x-disabled: false`.\n defaultDisabledParameters: true,\n includeDefaultHeaders: integration === 'client',\n clientId: localSelectedClient.value?.id,\n customCodeSamples: customCodeSamples.value.samples,\n operation,\n method,\n path,\n contentType: selectedContentType,\n server: selectedServer,\n securitySchemes,\n example: localExampleKey.value,\n globalCookies,\n requestBodyCompositionSelection,\n })\n})\n\n/** The language for the code block, used for syntax highlighting */\nconst codeBlockLanguage = computed(() => {\n if (isWebhook) {\n return webhookLanguage.value\n }\n\n return localSelectedClient.value?.lang\n})\n\n/** Determine the language for webhook content based on MIME type */\nconst webhookLanguage = computed<string>(() => {\n if (!webhookHar.value?.postData) return 'json'\n\n const contentType = webhookHar.value.postData.mimeType\n if (contentType?.includes('json')) return 'json'\n if (contentType?.includes('xml')) return 'xml'\n if (contentType?.includes('yaml') || contentType?.includes('yml'))\n return 'yaml'\n if (contentType?.includes('text/plain')) return 'text'\n\n return 'json'\n})\n\n/** Block secrets from being shown in the code block */\nconst secretCredentials = computed(() => getSecrets(securitySchemes))\n\n/** Grab the ref to freeze the ui as the clients change so there's no jump as the size of the dom changes */\nconst elem = ref<ComponentPublicInstance | null>(null)\n\n/** Set custom example, or update the selected HTTP client globally */\nconst selectClient = (option: ClientOption) => {\n // We need to freeze the ui to prevent scrolling as the clients change\n if (elem.value) {\n const unfreeze = freezeElement(elem.value.$el)\n setTimeout(() => {\n unfreeze()\n }, 300)\n }\n // Update to the local example\n localSelectedClient.value = option\n\n // Sync the selection globally so other operations follow along. Custom samples\n // sync too (keyed by language), so picking e.g. the Python SDK example here\n // shows the Python example on every operation that ships one.\n if (option) {\n eventBus.emit('workspace:update:selected-client', option.id)\n }\n}\n\n// Virtualize the code block if it's too large\n// This prevents the entire app from freezing up if there's a massive example\n// We set a lower threshold here as code examples can get quite large\nconst VIRTUALIZATION_THRESHOLD = 20_000\n\nconst shouldVirtualize = computed(\n () => (generatedCode.value.length ?? 0) > VIRTUALIZATION_THRESHOLD,\n)\n\nconst id = useId()\n</script>\n<template>\n <ScalarCard\n v-if=\"generatedCode\"\n ref=\"elem\"\n class=\"request-card dark-mode\">\n <!-- Header -->\n <ScalarCardHeader class=\"pr-2.5\">\n <span class=\"sr-only\">Request Example for</span>\n <HttpMethod\n as=\"span\"\n class=\"request-method\"\n :method=\"method\" />\n <span\n v-if=\"generateLabel\"\n v-html=\"generateLabel()\" />\n <slot name=\"header\" />\n <!-- Client picker -->\n <template\n v-if=\"!isWebhook && clientCount\"\n #actions>\n <!-- Multiple clients: render a dropdown to switch between them -->\n <ScalarCombobox\n v-if=\"clientCount > 1\"\n class=\"max-h-80\"\n :filterFn=\"filterClientsByQuery\"\n :modelValue=\"localSelectedClient\"\n :options=\"clients\"\n placement=\"bottom-end\"\n teleport\n @update:modelValue=\"selectClient($event as ClientOption)\">\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\"\n variant=\"ghost\">\n {{ localSelectedClient?.title }}\n <ScalarIconCaretDown\n class=\"ui-open:rotate-180 mt-px size-3 transition-transform duration-100\"\n weight=\"bold\" />\n </ScalarButton>\n </ScalarCombobox>\n <!-- Single client: just show its label, no need for a dropdown -->\n <span\n v-else\n class=\"text-c-2 flex h-full w-fit items-center px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\">\n {{ localSelectedClient?.title }}\n </span>\n </template>\n </ScalarCardHeader>\n\n <!-- Code snippet -->\n <ScalarCardSection class=\"request-editor-section custom-scroll p-0\">\n <div\n :id=\"`${id}-example`\"\n class=\"code-snippet\">\n <ScalarCodeBlock\n v-if=\"!shouldVirtualize\"\n class=\"bg-b-2 h-full\"\n :content=\"generatedCode\"\n :hideCredentials=\"secretCredentials\"\n :lang=\"codeBlockLanguage\"\n lineNumbers />\n <ScalarVirtualText\n v-else\n containerClass=\"custom-scroll scalar-code-block border rounded-b flex flex-1 max-h-screen\"\n contentClass=\"language-plaintext whitespace-pre font-code text-base p-2\"\n :lineHeight=\"20\"\n :text=\"generatedCode\" />\n </div>\n </ScalarCardSection>\n\n <!-- Footer -->\n <ScalarCardFooter\n v-if=\"Object.keys(requestBodyExamples).length > 1 || $slots.footer\"\n class=\"request-card-footer bg-b-3\">\n <!-- Example picker -->\n <div\n v-if=\"Object.keys(requestBodyExamples).length > 1\"\n class=\"request-card-footer-addon\">\n <template v-if=\"Object.keys(requestBodyExamples).length\">\n <ExamplePicker\n :examples=\"requestBodyExamples\"\n :modelValue=\"localExampleKey\"\n @update:modelValue=\"selectExample\" />\n </template>\n </div>\n\n <!-- Footer -->\n <slot\n :exampleName=\"localExampleKey\"\n name=\"footer\" />\n </ScalarCardFooter>\n </ScalarCard>\n\n <!-- Fallback card with just method and path in the case of no examples -->\n <ScalarCard\n v-else-if=\"fallback\"\n class=\"request-card dark-mode\">\n <ScalarCardSection class=\"request-card-simple\">\n <div class=\"request-header\">\n <HttpMethod\n as=\"span\"\n class=\"request-method\"\n :method=\"method\" />\n <slot name=\"header\" />\n </div>\n <slot\n :exampleName=\"localExampleKey\"\n name=\"footer\" />\n </ScalarCardSection>\n </ScalarCard>\n</template>\n<style scoped>\n.request-card {\n color: var(--scalar-color-1);\n font-size: var(--scalar-font-size-3);\n}\n.request-method {\n font-family: var(--scalar-font-code);\n text-transform: uppercase;\n margin-right: 6px;\n}\n.request-card-footer {\n display: flex;\n justify-content: flex-end;\n padding: 6px;\n flex-shrink: 0;\n position: relative;\n}\n.request-card-footer-addon {\n display: flex;\n align-items: center;\n\n flex: 1;\n min-width: 0;\n}\n.request-editor-section {\n display: flex;\n flex: 1;\n}\n.request-card-simple {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 8px 8px 8px 12px;\n\n font-size: var(--scalar-small);\n}\n.code-snippet {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CodeExample.vue.script.js","names":["$slots"],"sources":["../../../src/code-example/components/CodeExample.vue"],"sourcesContent":["<script lang=\"ts\">\nexport type CodeExampleProps = {\n /**\n * Integration type: determines if the code sample is displayed in a client environment\n * or in an API reference environment.\n */\n integration?: 'client' | 'reference'\n /**\n * List of all http clients formatted into option groups for the client selector\n */\n clientOptions: ClientOptionGroup[]\n /**\n * Pre-selected client, this will determine which client is initially selected in the dropdown.\n * Either a built-in client id (e.g. `js/fetch`) or a custom sample id (e.g. `custom/python`).\n *\n * Typed as a plain string: unioning the (large) client-id union with the `custom/...`\n * ids overflows TypeScript's union-complexity limit inside this component's `defineProps`.\n *\n * @defaults to a custom sample if one is available, otherwise shell/curl\n */\n selectedClient?: string\n /**\n * Which server from the spec to use for the code example\n */\n selectedServer?: ServerObject | null\n /**\n * The selected content type from the requestBody.content, this will determine which examples are available\n * as well as the content type of the code example\n *\n * @defaults to the first content type if not provided\n */\n selectedContentType?: string\n /**\n * Example name to use for resolving example values for parameters AND requestBody.\n *\n * This is the document-wide selection: it is honored only when this operation defines an example\n * with the same key, so picking an example on one operation syncs the rest without blanking out\n * operations that do not share that key.\n *\n * @example \"limited\"\n * ```ts\n * parameters: {\n * name: 'foobar',\n * in: 'query',\n * examples: {\n * limited: {\n * dataValue: 10,\n * }\n * }\n * },\n * body: {\n * content: {\n * 'application/json': {\n * examples: {\n * limited: {\n * dataValue: { foo: 'bar' },\n * }\n * }\n * }\n * }\n * }\n *\n * ```\n */\n selectedExample?: string\n /**\n * Event bus\n */\n eventBus: WorkspaceEventBus\n /**\n * The security schemes which are applicable to this operation\n */\n securitySchemes: SecuritySchemeObjectSecret[]\n /**\n * HTTP method of the operation\n */\n method: HttpMethodType\n /**\n * Path of the operation\n */\n path: string\n /**\n * De-referenced OpenAPI Operation object\n */\n operation: OperationObject\n /**\n * If true and there's no example, we will display a small card with the method and path only\n */\n fallback?: boolean\n /**\n * A method to generate the label of the block, should return an html string\n */\n generateLabel?: () => string\n /**\n * If true, render this as a webhook request example\n */\n isWebhook?: boolean\n /**\n * Workspace + document cookies\n */\n globalCookies?: XScalarCookie[]\n /**\n * When the request body schema uses oneOf/anyOf, use these selected variants\n * for the example snippet (e.g. from the schema dropdowns in the API reference).\n */\n requestBodyCompositionSelection?: Record<string, number>\n}\n\n/**\n * Request Example\n *\n * The core component for rendering a request example block,\n * this component does not have much of its own state but operates on props and custom events\n *\n * @event workspace:update:selected-client - Emitted when the selected client changes\n * @event workspace:update:selected-example - Emitted when the selected example changes, so other operations can sync\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport {\n ScalarCard,\n ScalarCardFooter,\n ScalarCardHeader,\n ScalarCardSection,\n} from '@scalar/components/card'\nimport { ScalarCodeBlock } from '@scalar/components/code-block'\nimport { ScalarCombobox } from '@scalar/components/combobox'\nimport { ScalarVirtualText } from '@scalar/components/virtual-text'\nimport { freezeElement } from '@scalar/helpers/dom/freeze-element'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { ScalarIconCaretDown } from '@scalar/icons'\nimport { type WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport {\n getOperationExamples,\n resolveOperationExamples,\n} from '@scalar/workspace-store/helpers/operation-examples'\nimport {\n useExampleVisibility,\n useExternalExamples,\n} from '@scalar/workspace-store/helpers/use-external-examples'\nimport type { SecuritySchemeObjectSecret } from '@scalar/workspace-store/request-example'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OperationObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport {\n computed,\n onBeforeMount,\n ref,\n useId,\n watch,\n watchEffect,\n type ComponentPublicInstance,\n} from 'vue'\n\nimport { filterClientsByQuery } from '../helpers/filter-clients-by-query'\nimport { findClient } from '../helpers/find-client'\nimport { generateCodeSnippet } from '../helpers/generate-code-snippet'\nimport { getClients } from '../helpers/get-clients'\nimport { getCustomCodeSamples } from '../helpers/get-custom-code-samples'\nimport { getSecrets } from '../helpers/get-secrets'\nimport { operationToHar } from '../helpers/operation-to-har/operation-to-har'\nimport type {\n ClientOption,\n ClientOptionGroup,\n CustomClientOption,\n} from '../types'\nimport ExamplePicker from './ExamplePicker.vue'\nimport HttpMethod from './HttpMethod.vue'\n\nconst {\n integration,\n clientOptions,\n selectedClient,\n selectedServer = null,\n selectedContentType,\n selectedExample,\n securitySchemes = [],\n method,\n eventBus,\n path,\n operation,\n isWebhook,\n generateLabel,\n globalCookies,\n requestBodyCompositionSelection,\n} = defineProps<CodeExampleProps>()\n\nconst emit = defineEmits<{\n /**\n * Emitted whenever the example key actually shown for this operation changes.\n *\n * This is the resolved local key (not the raw document-wide selection), so layouts that render\n * the test-request button outside this component can open the client with the same example the\n * snippet displays.\n */\n (e: 'update:exampleKey', value: string): void\n}>()\n\ndefineSlots<{\n header: () => unknown\n footer: ({ exampleName }: { exampleName: string }) => unknown\n}>()\n\n/** Grab the examples for the given content type */\nconst requestBodyExamples = computed(() => {\n const content = getResolvedRef(operation.requestBody)?.content ?? {}\n const contentType = selectedContentType || Object.keys(content)[0]\n if (!contentType) return {}\n\n const examples = content[contentType]?.examples ?? {}\n\n return examples\n})\n\n/**\n * The example key actually shown for this operation.\n *\n * `selectedExample` is the document-wide selection that syncs across operations. We honor it only\n * when this operation actually defines that key, otherwise we keep the local example: a selection\n * on one operation should never blank out an operation that does not share the same example.\n */\nconst localExampleKey = ref('')\n\n/** Resolve the key to display, preferring the document-wide selection when this operation has it */\nconst resolveExampleKey = (preferred: string | undefined): string => {\n const keys = Object.keys(requestBodyExamples.value)\n if (preferred && keys.includes(preferred)) {\n return preferred\n }\n // Keep the current local example when it is still valid, otherwise fall back to the first one\n if (localExampleKey.value && keys.includes(localExampleKey.value)) {\n return localExampleKey.value\n }\n return keys[0] ?? ''\n}\n\n// Set the initial example from the document-wide selection, falling back to the first example\nonBeforeMount(() => {\n localExampleKey.value = resolveExampleKey(selectedExample)\n})\n\n// Follow the document-wide selection when it changes and this operation has that example\nwatch(\n () => selectedExample,\n (preferred) => {\n localExampleKey.value = resolveExampleKey(preferred)\n },\n)\n\n/** Reset the selected example key if the content type changes and the new content type doesn't have the previously selected example */\nwatch(\n () => selectedContentType,\n () => {\n if (\n !Object.keys(requestBodyExamples.value).includes(localExampleKey.value)\n ) {\n // Re-resolve so the new content type still follows the document-wide selection when it has\n // that key, instead of always snapping back to the first example\n localExampleKey.value = resolveExampleKey(selectedExample)\n }\n },\n)\n\n// Keep the parent informed of the resolved key so a test-request button rendered outside this\n// component (e.g. the classic layout header) opens the client with the example the snippet shows\nwatchEffect(() => {\n emit('update:exampleKey', localExampleKey.value)\n})\n\n/** Select an example locally and sync the choice across the document */\nconst selectExample = (key: string) => {\n localExampleKey.value = key\n eventBus.emit('workspace:update:selected-example', key)\n}\n\n/** Grab any custom code samples from the operation */\nconst customCodeSamples = computed(() => getCustomCodeSamples(operation))\n\n/** Merge custom code samples with the client options */\nconst clients = computed(() =>\n getClients(\n customCodeSamples.value.samples,\n clientOptions,\n customCodeSamples.value.label,\n ),\n)\n\n/** Total number of available clients across all option groups */\nconst clientCount = computed(() =>\n clients.value.reduce((total, group) => total + group.options.length, 0),\n)\n\n/** The locally selected client which would include code samples from this operation only */\nconst localSelectedClient = ref<ClientOption | CustomClientOption | undefined>(\n findClient(clients.value, selectedClient),\n)\n\n/**\n * Re-resolve the local client whenever the global selection or the available\n * clients change. Watching `clients` matters when navigating between operations:\n * the stored id stays the same, but the matching option (e.g. a custom sample)\n * differs per operation, so without this the snippet could go stale.\n */\nwatch([() => selectedClient, clients], ([newClient]) => {\n const client = findClient(clients.value, newClient)\n if (client) {\n localSelectedClient.value = client\n }\n})\n\nconst elem = ref<ComponentPublicInstance | null>(null)\nconst visible = useExampleVisibility(elem)\nconst externalExamples = useExternalExamples(\n () =>\n getOperationExamples(operation, localExampleKey.value, selectedContentType),\n () => visible.value,\n)\nconst resolvedOperation = computed(() =>\n resolveOperationExamples(\n operation,\n localExampleKey.value,\n selectedContentType,\n externalExamples.resolve,\n ),\n)\n\n/** Generate HAR data for webhook requests */\nconst webhookHar = computed(() => {\n if (!isWebhook) return null\n\n try {\n return operationToHar({\n operation: resolvedOperation.value,\n method,\n path,\n example: localExampleKey.value,\n requestBodyCompositionSelection,\n // Only required parameters are shown in code examples; optional parameters\n // are omitted unless explicitly enabled via `x-disabled: false`.\n defaultDisabledParameters: true,\n })\n } catch (error) {\n console.error('[webhookHar]', error)\n return null\n }\n})\n\n/** Generate the code snippet for the selected example */\nconst generatedCode = computed<string>(() => {\n if (isWebhook) {\n return webhookHar.value?.postData?.text ?? ''\n }\n\n return generateCodeSnippet({\n // Only required parameters are shown in code examples; optional parameters\n // are omitted unless explicitly enabled via `x-disabled: false`.\n defaultDisabledParameters: true,\n includeDefaultHeaders: integration === 'client',\n clientId: localSelectedClient.value?.id,\n customCodeSamples: customCodeSamples.value.samples,\n operation: resolvedOperation.value,\n method,\n path,\n contentType: selectedContentType,\n server: selectedServer,\n securitySchemes,\n example: localExampleKey.value,\n globalCookies,\n requestBodyCompositionSelection,\n })\n})\n\n/** The language for the code block, used for syntax highlighting */\nconst codeBlockLanguage = computed(() => {\n if (isWebhook) {\n return webhookLanguage.value\n }\n\n return localSelectedClient.value?.lang\n})\n\n/** Determine the language for webhook content based on MIME type */\nconst webhookLanguage = computed<string>(() => {\n if (!webhookHar.value?.postData) return 'json'\n\n const contentType = webhookHar.value.postData.mimeType\n if (contentType?.includes('json')) return 'json'\n if (contentType?.includes('xml')) return 'xml'\n if (contentType?.includes('yaml') || contentType?.includes('yml'))\n return 'yaml'\n if (contentType?.includes('text/plain')) return 'text'\n\n return 'json'\n})\n\n/** Block secrets from being shown in the code block */\nconst secretCredentials = computed(() => getSecrets(securitySchemes))\n\n/** Set custom example, or update the selected HTTP client globally */\nconst selectClient = (option: ClientOption) => {\n // We need to freeze the ui to prevent scrolling as the clients change\n if (elem.value) {\n const unfreeze = freezeElement(elem.value.$el)\n setTimeout(() => {\n unfreeze()\n }, 300)\n }\n // Update to the local example\n localSelectedClient.value = option\n\n // Sync the selection globally so other operations follow along. Custom samples\n // sync too (keyed by language), so picking e.g. the Python SDK example here\n // shows the Python example on every operation that ships one.\n if (option) {\n eventBus.emit('workspace:update:selected-client', option.id)\n }\n}\n\n// Virtualize the code block if it's too large\n// This prevents the entire app from freezing up if there's a massive example\n// We set a lower threshold here as code examples can get quite large\nconst VIRTUALIZATION_THRESHOLD = 20_000\n\nconst shouldVirtualize = computed(\n () => (generatedCode.value.length ?? 0) > VIRTUALIZATION_THRESHOLD,\n)\n\nconst id = useId()\n</script>\n<template>\n <ScalarCard\n v-if=\"generatedCode || externalExamples.pending.value\"\n ref=\"elem\"\n class=\"request-card dark-mode\">\n <!-- Header -->\n <ScalarCardHeader class=\"pr-2.5\">\n <span class=\"sr-only\">Request Example for</span>\n <HttpMethod\n as=\"span\"\n class=\"request-method\"\n :method=\"method\" />\n <span\n v-if=\"generateLabel\"\n v-html=\"generateLabel()\" />\n <slot name=\"header\" />\n <!-- Client picker -->\n <template\n v-if=\"!isWebhook && clientCount\"\n #actions>\n <!-- Multiple clients: render a dropdown to switch between them -->\n <ScalarCombobox\n v-if=\"clientCount > 1\"\n class=\"max-h-80\"\n :filterFn=\"filterClientsByQuery\"\n :modelValue=\"localSelectedClient\"\n :options=\"clients\"\n placement=\"bottom-end\"\n teleport\n @update:modelValue=\"selectClient($event as ClientOption)\">\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\"\n variant=\"ghost\">\n {{ localSelectedClient?.title }}\n <ScalarIconCaretDown\n class=\"ui-open:rotate-180 mt-px size-3 transition-transform duration-100\"\n weight=\"bold\" />\n </ScalarButton>\n </ScalarCombobox>\n <!-- Single client: just show its label, no need for a dropdown -->\n <span\n v-else\n class=\"text-c-2 flex h-full w-fit items-center px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\">\n {{ localSelectedClient?.title }}\n </span>\n </template>\n </ScalarCardHeader>\n\n <!-- Code snippet -->\n <ScalarCardSection class=\"request-editor-section custom-scroll p-0\">\n <div\n v-if=\"externalExamples.pending.value\"\n class=\"text-c-2 p-4\"\n role=\"status\">\n <template v-if=\"externalExamples.failed.value\">\n Could not load this example.\n <ScalarButton\n size=\"sm\"\n variant=\"ghost\"\n @click=\"externalExamples.retry\">\n Retry\n </ScalarButton>\n </template>\n <template v-else>Loading example…</template>\n </div>\n <div\n v-else\n :id=\"`${id}-example`\"\n class=\"code-snippet\">\n <ScalarCodeBlock\n v-if=\"!shouldVirtualize\"\n class=\"bg-b-2 h-full\"\n :content=\"generatedCode\"\n :hideCredentials=\"secretCredentials\"\n :lang=\"codeBlockLanguage\"\n lineNumbers />\n <ScalarVirtualText\n v-else\n containerClass=\"custom-scroll scalar-code-block border rounded-b flex flex-1 max-h-screen\"\n contentClass=\"language-plaintext whitespace-pre font-code text-base p-2\"\n :lineHeight=\"20\"\n :text=\"generatedCode\" />\n </div>\n </ScalarCardSection>\n\n <!-- Footer -->\n <ScalarCardFooter\n v-if=\"Object.keys(requestBodyExamples).length > 1 || $slots.footer\"\n class=\"request-card-footer bg-b-3\">\n <!-- Example picker -->\n <div\n v-if=\"Object.keys(requestBodyExamples).length > 1\"\n class=\"request-card-footer-addon\">\n <template v-if=\"Object.keys(requestBodyExamples).length\">\n <ExamplePicker\n :examples=\"requestBodyExamples\"\n :modelValue=\"localExampleKey\"\n @update:modelValue=\"selectExample\" />\n </template>\n </div>\n\n <!-- Footer -->\n <slot\n :exampleName=\"localExampleKey\"\n name=\"footer\" />\n </ScalarCardFooter>\n </ScalarCard>\n\n <!-- Fallback card with just method and path in the case of no examples -->\n <ScalarCard\n v-else-if=\"fallback\"\n class=\"request-card dark-mode\">\n <ScalarCardSection class=\"request-card-simple\">\n <div class=\"request-header\">\n <HttpMethod\n as=\"span\"\n class=\"request-method\"\n :method=\"method\" />\n <slot name=\"header\" />\n </div>\n <slot\n :exampleName=\"localExampleKey\"\n name=\"footer\" />\n </ScalarCardSection>\n </ScalarCard>\n</template>\n<style scoped>\n.request-card {\n color: var(--scalar-color-1);\n font-size: var(--scalar-font-size-3);\n}\n.request-method {\n font-family: var(--scalar-font-code);\n text-transform: uppercase;\n margin-right: 6px;\n}\n.request-card-footer {\n display: flex;\n justify-content: flex-end;\n padding: 6px;\n flex-shrink: 0;\n position: relative;\n}\n.request-card-footer-addon {\n display: flex;\n align-items: center;\n\n flex: 1;\n min-width: 0;\n}\n.request-editor-section {\n display: flex;\n flex: 1;\n}\n.request-card-simple {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 8px 8px 8px 12px;\n\n font-size: var(--scalar-small);\n}\n.code-snippet {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation-to-har.d.ts","sourceRoot":"","sources":["../../../../src/code-example/helpers/operation-to-har/operation-to-har.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAEnE,OAAO,EACL,KAAK,0BAA0B,EAIhC,MAAM,yCAAyC,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qEAAqE,CAAA;AACxG,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AACjH,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,YAAY,CAAA;AASvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,+BAA+B;IAC/B,SAAS,EAAE,eAAe,CAAA;IAC1B,mCAAmC;IACnC,MAAM,EAAE,UAAU,CAAA;IAClB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,0BAA0B,EAAE,CAAA;IAC9C,mCAAmC;IACnC,aAAa,CAAC,EAAE,aAAa,EAAE,CAAA;IAC/B;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;OAGG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxD;;;OAGG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAA;CACpC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,cAAc,GAAI,+KAY5B,mBAAmB,KAAG,
|
|
1
|
+
{"version":3,"file":"operation-to-har.d.ts","sourceRoot":"","sources":["../../../../src/code-example/helpers/operation-to-har/operation-to-har.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAEnE,OAAO,EACL,KAAK,0BAA0B,EAIhC,MAAM,yCAAyC,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qEAAqE,CAAA;AACxG,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AACjH,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,YAAY,CAAA;AASvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,+BAA+B;IAC/B,SAAS,EAAE,eAAe,CAAA;IAC1B,mCAAmC;IACnC,MAAM,EAAE,UAAU,CAAA;IAClB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,0BAA0B,EAAE,CAAA;IAC9C,mCAAmC;IACnC,aAAa,CAAC,EAAE,aAAa,EAAE,CAAA;IAC/B;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;OAGG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxD;;;OAGG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAA;CACpC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,cAAc,GAAI,+KAY5B,mBAAmB,KAAG,UAkIxB,CAAA"}
|
|
@@ -31,14 +31,14 @@ var u = ({ includeDefaultHeaders: u = !1, operation: d, contentType: f, method:
|
|
|
31
31
|
cookies: [],
|
|
32
32
|
headersSize: -1,
|
|
33
33
|
bodySize: -1
|
|
34
|
-
};
|
|
34
|
+
}, w = !1;
|
|
35
35
|
if (d.parameters) {
|
|
36
|
-
let { url: e, headers: t, queryString: r, cookies: i } = n({
|
|
36
|
+
let { url: e, headers: t, queryString: r, cookies: i, hasCookieStyleEntries: a } = n({
|
|
37
37
|
harRequest: C,
|
|
38
38
|
parameters: d.parameters,
|
|
39
39
|
example: g,
|
|
40
40
|
defaultDisabled: b
|
|
41
|
-
}),
|
|
41
|
+
}), o = v?.filter((t) => s({
|
|
42
42
|
cookie: t,
|
|
43
43
|
url: e,
|
|
44
44
|
disabledGlobalCookies: S
|
|
@@ -46,19 +46,19 @@ var u = ({ includeDefaultHeaders: u = !1, operation: d, contentType: f, method:
|
|
|
46
46
|
name: e.name,
|
|
47
47
|
value: e.value
|
|
48
48
|
})) ?? [];
|
|
49
|
-
C.url = e, C.headers = t, C.queryString = r, C.cookies = [...
|
|
49
|
+
w = a, C.url = e, C.headers = t, C.queryString = r, C.cookies = [...o, ...i];
|
|
50
50
|
}
|
|
51
|
-
let
|
|
52
|
-
if (
|
|
51
|
+
let T = a(d.requestBody);
|
|
52
|
+
if (T?.content) {
|
|
53
53
|
let e = t({
|
|
54
|
-
requestBody:
|
|
54
|
+
requestBody: T,
|
|
55
55
|
contentType: f,
|
|
56
56
|
example: g,
|
|
57
57
|
requestBodyCompositionSelection: y
|
|
58
58
|
});
|
|
59
59
|
if (e && (C.postData = e, C.bodySize = e.text?.length ?? -1, e.mimeType)) {
|
|
60
60
|
let t = C.headers.find((e) => e.name.toLowerCase() === "content-type");
|
|
61
|
-
t && !t.value ? t.value = e.mimeType : t || C.headers.push({
|
|
61
|
+
t && (!t.value || e.mimeType.startsWith("multipart/") && e.mimeType.includes("boundary=") && e.text !== void 0) ? t.value = e.mimeType : t || C.headers.push({
|
|
62
62
|
name: "Content-Type",
|
|
63
63
|
value: e.mimeType
|
|
64
64
|
});
|
|
@@ -68,9 +68,14 @@ var u = ({ includeDefaultHeaders: u = !1, operation: d, contentType: f, method:
|
|
|
68
68
|
let { headers: e, queryString: t, cookies: n } = r(_);
|
|
69
69
|
C.headers.push(...e), C.queryString.push(...t), C.cookies.push(...n);
|
|
70
70
|
}
|
|
71
|
-
let
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
let E = C.headers.find((e) => e.name.toLowerCase() === "cookie");
|
|
72
|
+
if (w && E && C.cookies.length) {
|
|
73
|
+
let e = C.cookies.map((e) => `${encodeURIComponent(e.name)}=${encodeURIComponent(e.value)}`).join("; ");
|
|
74
|
+
E.value = E.value ? `${E.value}; ${e}` : e, C.cookies = [];
|
|
75
|
+
}
|
|
76
|
+
let D = 0;
|
|
77
|
+
for (let e of C.headers) D += (e.name?.length ?? 0) + 2 + (e.value?.length ?? 0) + 2;
|
|
78
|
+
return C.headersSize = D, C;
|
|
74
79
|
};
|
|
75
80
|
//#endregion
|
|
76
81
|
export { u as operationToHar };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation-to-har.js","names":[],"sources":["../../../../src/code-example/helpers/operation-to-har/operation-to-har.ts"],"sourcesContent":["import { isElectron } from '@scalar/helpers/general/is-electron'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport {\n type SecuritySchemeObjectSecret,\n filterGlobalCookie,\n getDefaultHeaders,\n restoreConventionalDefaultHeaderNames,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type { OperationObject, ServerObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport type { Request as HarRequest } from 'har-format'\n\nimport { APP_VERSION } from '@/constants'\n\nimport { processBody } from './process-body'\nimport { processParameters } from './process-parameters'\nimport { processSecuritySchemes } from './process-security-schemes'\nimport { processServerUrl } from './process-server-url'\n\nexport type OperationToHarProps = {\n /** OpenAPI Operation object */\n operation: OperationObject\n /** HTTP method of the operation */\n method: HttpMethod\n /** Path of the operation */\n path: string\n /**\n * Name of the currently selected operation example\n *\n * Applies to both the body and the parameters\n */\n example?: string\n /**\n * Content type of the operation\n *\n * Applies to both the body and the parameters (if applicable)\n * @defaults to the first content type in the MediaTypeObject\n */\n contentType?: string\n /** OpenAPI Server object */\n server?: ServerObject | null\n /** OpenAPI SecurityScheme objects which are applicable to the operation */\n securitySchemes?: SecuritySchemeObjectSecret[]\n /** Workspace + document cookies */\n globalCookies?: XScalarCookie[]\n /**\n * Whether to include default headers (e.g., Accept, Content-Type) automatically.\n * If false, default headers will be omitted from the HAR request.\n * @default true\n */\n includeDefaultHeaders?: boolean\n /**\n * Selected oneOf/anyOf variants for nested request body example generation\n * (e.g. from the schema dropdowns in the API reference).\n */\n requestBodyCompositionSelection?: Record<string, number>\n /**\n * Whether to disable parameters by default.\n * @default false\n */\n defaultDisabledParameters?: boolean\n}\n\n/**\n * Converts an OpenAPI Operation to a HarRequest format for generating HTTP request snippets.\n *\n * This function transforms OpenAPI 3.1 operation objects into HAR (HTTP Archive) format requests,\n * which can be used to generate code snippets for various programming languages and HTTP clients.\n *\n * The conversion handles:\n * - Server URL processing and path parameter substitution\n * - Query parameter formatting based on OpenAPI parameter styles\n * - Request body processing with content type handling\n * - Security scheme integration (API keys, etc.)\n *\n * The resulting HarRequest object follows the HAR specification and includes:\n * - HTTP method and URL\n * - Headers and query parameters\n * - Request body (if present)\n * - Cookie information\n * - Size calculations for headers and body\n *\n * @see https://w3c.github.io/web-performance/specs/HAR/Overview.html\n * @see https://spec.openapis.org/oas/v3.1.0#operation-object\n */\nexport const operationToHar = ({\n includeDefaultHeaders = false,\n operation,\n contentType,\n method,\n path,\n server = null,\n example,\n securitySchemes,\n globalCookies,\n requestBodyCompositionSelection,\n defaultDisabledParameters = false,\n}: OperationToHarProps): HarRequest => {\n const defaultHeaders = includeDefaultHeaders\n ? getDefaultHeaders({\n method,\n operation,\n exampleName: example ?? 'default',\n hideDisabledHeaders: true,\n hideOverriddenHeaders: true,\n options: { isElectron: isElectron(), appVersion: APP_VERSION },\n })\n : {}\n const defaultHeadersWithConventionalNames = restoreConventionalDefaultHeaderNames(defaultHeaders)\n\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[example ?? 'default'] ?? {}\n\n const serverUrl = processServerUrl(server, path)\n\n // Initialize the HAR request with basic properties\n const harRequest: HarRequest = {\n method,\n url: serverUrl,\n headers: Object.entries(defaultHeadersWithConventionalNames).map(([name, value]) => ({ name, value })),\n queryString: [],\n postData: undefined,\n httpVersion: 'HTTP/1.1',\n cookies: [],\n headersSize: -1,\n bodySize: -1,\n }\n\n // Handle parameters\n if (operation.parameters) {\n const { url, headers, queryString, cookies } = processParameters({\n harRequest,\n parameters: operation.parameters,\n example,\n defaultDisabled: defaultDisabledParameters,\n })\n\n // Correctly filter the global cookies by the processed url\n const filteredGlobalCookies =\n globalCookies\n ?.filter((cookie) => filterGlobalCookie({ cookie, url, disabledGlobalCookies }))\n ?.map((cookie) => ({ name: cookie.name, value: cookie.value })) ?? []\n\n harRequest.url = url\n harRequest.headers = headers\n harRequest.queryString = queryString\n harRequest.cookies = [...filteredGlobalCookies, ...cookies]\n }\n\n const body = getResolvedRef(operation.requestBody)\n\n // Handle request body\n if (body?.content) {\n const postData = processBody({\n requestBody: body,\n contentType,\n example,\n requestBodyCompositionSelection,\n })\n\n if (postData) {\n harRequest.postData = postData\n harRequest.bodySize = postData.text?.length ?? -1\n\n // Add or update Content-Type header\n if (postData.mimeType) {\n const existingContentTypeHeader = harRequest.headers.find(\n (header) => header.name.toLowerCase() === 'content-type',\n )\n // Update existing header if it has an empty value\n if (existingContentTypeHeader && !existingContentTypeHeader.value) {\n existingContentTypeHeader.value = postData.mimeType\n }\n // Add new header if none exists\n else if (!existingContentTypeHeader) {\n harRequest.headers.push({\n name: 'Content-Type',\n value: postData.mimeType,\n })\n }\n }\n }\n }\n\n // Handle security schemes\n if (securitySchemes) {\n const { headers, queryString, cookies } = processSecuritySchemes(securitySchemes)\n harRequest.headers.push(...headers)\n harRequest.queryString.push(...queryString)\n harRequest.cookies.push(...cookies)\n }\n\n // Calculate headers size without allocating a large joined string\n let headersSize = 0\n for (const h of harRequest.headers) {\n // name + \": \" + value + \"\\r\\n\"\n headersSize += (h.name?.length ?? 0) + 2 + (h.value?.length ?? 0) + 2\n }\n harRequest.headersSize = headersSize\n\n return harRequest\n}\n"],"mappings":";;;;;;;;;AAsFA,IAAa,KAAkB,EAC7B,2BAAwB,IACxB,cACA,gBACA,WACA,SACA,YAAS,MACT,YACA,oBACA,kBACA,oCACA,+BAA4B,SACS;CAWrC,IAAM,IAAsC,EAVrB,IACnB,EAAkB;EAChB;EACA;EACA,aAAa,KAAW;EACxB,qBAAqB;EACrB,uBAAuB;EACvB,SAAS;GAAE,YAAY,EAAW;GAAG,YAAY;EAAY;CAC/D,CAAC,IACD,CAAC,CAC2F,GAE1F,IACJ,EAAU,8BAA8B,GAAG,iBAAiB,GAAG,KAAW,cAAc,CAAC,GAKrF,IAAyB;EAC7B;EACA,KALgB,EAAiB,GAAQ,CAKpC;EACL,SAAS,OAAO,QAAQ,CAAmC,CAAC,CAAC,KAAK,CAAC,GAAM,QAAY;GAAE;GAAM;EAAM,EAAE;EACrG,aAAa,CAAC;EACd,UAAU,KAAA;EACV,aAAa;EACb,SAAS,CAAC;EACV,aAAa;EACb,UAAU;CACZ;CAGA,IAAI,EAAU,YAAY;EACxB,IAAM,EAAE,QAAK,YAAS,gBAAa,eAAY,EAAkB;GAC/D;GACA,YAAY,EAAU;GACtB;GACA,iBAAiB;EACnB,CAAC,GAGK,IACJ,GACI,QAAQ,MAAW,EAAmB;GAAE;GAAQ;GAAK;EAAsB,CAAC,CAAC,CAAC,EAC9E,KAAK,OAAY;GAAE,MAAM,EAAO;GAAM,OAAO,EAAO;EAAM,EAAE,KAAK,CAAC;EAKxE,AAHA,EAAW,MAAM,GACjB,EAAW,UAAU,GACrB,EAAW,cAAc,GACzB,EAAW,UAAU,CAAC,GAAG,GAAuB,GAAG,CAAO;CAC5D;CAEA,IAAM,IAAO,EAAe,EAAU,WAAW;CAGjD,IAAI,GAAM,SAAS;EACjB,IAAM,IAAW,EAAY;GAC3B,aAAa;GACb;GACA;GACA;EACF,CAAC;EAED,IAAI,MACF,EAAW,WAAW,GACtB,EAAW,WAAW,EAAS,MAAM,UAAU,IAG3C,EAAS,WAAU;GACrB,IAAM,IAA4B,EAAW,QAAQ,MAClD,MAAW,EAAO,KAAK,YAAY,MAAM,cAC5C;GAEA,AAAI,KAA6B,CAAC,EAA0B,QAC1D,EAA0B,QAAQ,EAAS,WAGnC,KACR,EAAW,QAAQ,KAAK;IACtB,MAAM;IACN,OAAO,EAAS;GAClB,CAAC;EAEL;CAEJ;CAGA,IAAI,GAAiB;EACnB,IAAM,EAAE,YAAS,gBAAa,eAAY,EAAuB,CAAe;EAGhF,AAFA,EAAW,QAAQ,KAAK,GAAG,CAAO,GAClC,EAAW,YAAY,KAAK,GAAG,CAAW,GAC1C,EAAW,QAAQ,KAAK,GAAG,CAAO;CACpC;CAGA,IAAI,IAAc;CAClB,KAAK,IAAM,KAAK,EAAW,SAEzB,MAAgB,EAAE,MAAM,UAAU,KAAK,KAAK,EAAE,OAAO,UAAU,KAAK;CAItE,OAFA,EAAW,cAAc,GAElB;AACT"}
|
|
1
|
+
{"version":3,"file":"operation-to-har.js","names":[],"sources":["../../../../src/code-example/helpers/operation-to-har/operation-to-har.ts"],"sourcesContent":["import { isElectron } from '@scalar/helpers/general/is-electron'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport {\n type SecuritySchemeObjectSecret,\n filterGlobalCookie,\n getDefaultHeaders,\n restoreConventionalDefaultHeaderNames,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type { OperationObject, ServerObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport type { Request as HarRequest } from 'har-format'\n\nimport { APP_VERSION } from '@/constants'\n\nimport { processBody } from './process-body'\nimport { processParameters } from './process-parameters'\nimport { processSecuritySchemes } from './process-security-schemes'\nimport { processServerUrl } from './process-server-url'\n\nexport type OperationToHarProps = {\n /** OpenAPI Operation object */\n operation: OperationObject\n /** HTTP method of the operation */\n method: HttpMethod\n /** Path of the operation */\n path: string\n /**\n * Name of the currently selected operation example\n *\n * Applies to both the body and the parameters\n */\n example?: string\n /**\n * Content type of the operation\n *\n * Applies to both the body and the parameters (if applicable)\n * @defaults to the first content type in the MediaTypeObject\n */\n contentType?: string\n /** OpenAPI Server object */\n server?: ServerObject | null\n /** OpenAPI SecurityScheme objects which are applicable to the operation */\n securitySchemes?: SecuritySchemeObjectSecret[]\n /** Workspace + document cookies */\n globalCookies?: XScalarCookie[]\n /**\n * Whether to include default headers (e.g., Accept, Content-Type) automatically.\n * If false, default headers will be omitted from the HAR request.\n * @default true\n */\n includeDefaultHeaders?: boolean\n /**\n * Selected oneOf/anyOf variants for nested request body example generation\n * (e.g. from the schema dropdowns in the API reference).\n */\n requestBodyCompositionSelection?: Record<string, number>\n /**\n * Whether to disable parameters by default.\n * @default false\n */\n defaultDisabledParameters?: boolean\n}\n\n/**\n * Converts an OpenAPI Operation to a HarRequest format for generating HTTP request snippets.\n *\n * This function transforms OpenAPI 3.1 operation objects into HAR (HTTP Archive) format requests,\n * which can be used to generate code snippets for various programming languages and HTTP clients.\n *\n * The conversion handles:\n * - Server URL processing and path parameter substitution\n * - Query parameter formatting based on OpenAPI parameter styles\n * - Request body processing with content type handling\n * - Security scheme integration (API keys, etc.)\n *\n * The resulting HarRequest object follows the HAR specification and includes:\n * - HTTP method and URL\n * - Headers and query parameters\n * - Request body (if present)\n * - Cookie information\n * - Size calculations for headers and body\n *\n * @see https://w3c.github.io/web-performance/specs/HAR/Overview.html\n * @see https://spec.openapis.org/oas/v3.1.0#operation-object\n */\nexport const operationToHar = ({\n includeDefaultHeaders = false,\n operation,\n contentType,\n method,\n path,\n server = null,\n example,\n securitySchemes,\n globalCookies,\n requestBodyCompositionSelection,\n defaultDisabledParameters = false,\n}: OperationToHarProps): HarRequest => {\n const defaultHeaders = includeDefaultHeaders\n ? getDefaultHeaders({\n method,\n operation,\n exampleName: example ?? 'default',\n hideDisabledHeaders: true,\n hideOverriddenHeaders: true,\n options: { isElectron: isElectron(), appVersion: APP_VERSION },\n })\n : {}\n const defaultHeadersWithConventionalNames = restoreConventionalDefaultHeaderNames(defaultHeaders)\n\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[example ?? 'default'] ?? {}\n\n const serverUrl = processServerUrl(server, path)\n\n // Initialize the HAR request with basic properties\n const harRequest: HarRequest = {\n method,\n url: serverUrl,\n headers: Object.entries(defaultHeadersWithConventionalNames).map(([name, value]) => ({ name, value })),\n queryString: [],\n postData: undefined,\n httpVersion: 'HTTP/1.1',\n cookies: [],\n headersSize: -1,\n bodySize: -1,\n }\n\n let hasCookieStyleEntries = false\n\n // Handle parameters\n if (operation.parameters) {\n const {\n url,\n headers,\n queryString,\n cookies,\n hasCookieStyleEntries: processedCookieStyleEntries,\n } = processParameters({\n harRequest,\n parameters: operation.parameters,\n example,\n defaultDisabled: defaultDisabledParameters,\n })\n\n // Correctly filter the global cookies by the processed url\n const filteredGlobalCookies =\n globalCookies\n ?.filter((cookie) => filterGlobalCookie({ cookie, url, disabledGlobalCookies }))\n ?.map((cookie) => ({ name: cookie.name, value: cookie.value })) ?? []\n\n hasCookieStyleEntries = processedCookieStyleEntries\n harRequest.url = url\n harRequest.headers = headers\n harRequest.queryString = queryString\n harRequest.cookies = [...filteredGlobalCookies, ...cookies]\n }\n\n const body = getResolvedRef(operation.requestBody)\n\n // Handle request body\n if (body?.content) {\n const postData = processBody({\n requestBody: body,\n contentType,\n example,\n requestBodyCompositionSelection,\n })\n\n if (postData) {\n harRequest.postData = postData\n harRequest.bodySize = postData.text?.length ?? -1\n\n // Add or update Content-Type header\n if (postData.mimeType) {\n const existingContentTypeHeader = harRequest.headers.find(\n (header) => header.name.toLowerCase() === 'content-type',\n )\n // Update existing header if it has an empty value\n if (\n existingContentTypeHeader &&\n (!existingContentTypeHeader.value ||\n (postData.mimeType.startsWith('multipart/') &&\n postData.mimeType.includes('boundary=') &&\n postData.text !== undefined))\n ) {\n existingContentTypeHeader.value = postData.mimeType\n }\n // Add new header if none exists\n else if (!existingContentTypeHeader) {\n harRequest.headers.push({\n name: 'Content-Type',\n value: postData.mimeType,\n })\n }\n }\n }\n }\n\n // Handle security schemes\n if (securitySchemes) {\n const { headers, queryString, cookies } = processSecuritySchemes(securitySchemes)\n harRequest.headers.push(...headers)\n harRequest.queryString.push(...queryString)\n harRequest.cookies.push(...cookies)\n }\n\n // Keep authentication and global cookies in the explicit header as well, so\n // snippet generators cannot replace cookie-style parameters with their own header.\n const cookieHeader = harRequest.headers.find((header) => header.name.toLowerCase() === 'cookie')\n if (hasCookieStyleEntries && cookieHeader && harRequest.cookies.length) {\n const extraCookies = harRequest.cookies\n .map((cookie) => `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`)\n .join('; ')\n cookieHeader.value = cookieHeader.value ? `${cookieHeader.value}; ${extraCookies}` : extraCookies\n harRequest.cookies = []\n }\n\n // Calculate headers size without allocating a large joined string\n let headersSize = 0\n for (const h of harRequest.headers) {\n // name + \": \" + value + \"\\r\\n\"\n headersSize += (h.name?.length ?? 0) + 2 + (h.value?.length ?? 0) + 2\n }\n harRequest.headersSize = headersSize\n\n return harRequest\n}\n"],"mappings":";;;;;;;;;AAsFA,IAAa,KAAkB,EAC7B,2BAAwB,IACxB,cACA,gBACA,WACA,SACA,YAAS,MACT,YACA,oBACA,kBACA,oCACA,+BAA4B,SACS;CAWrC,IAAM,IAAsC,EAVrB,IACnB,EAAkB;EAChB;EACA;EACA,aAAa,KAAW;EACxB,qBAAqB;EACrB,uBAAuB;EACvB,SAAS;GAAE,YAAY,EAAW;GAAG,YAAY;EAAY;CAC/D,CAAC,IACD,CAAC,CAC2F,GAE1F,IACJ,EAAU,8BAA8B,GAAG,iBAAiB,GAAG,KAAW,cAAc,CAAC,GAKrF,IAAyB;EAC7B;EACA,KALgB,EAAiB,GAAQ,CAKpC;EACL,SAAS,OAAO,QAAQ,CAAmC,CAAC,CAAC,KAAK,CAAC,GAAM,QAAY;GAAE;GAAM;EAAM,EAAE;EACrG,aAAa,CAAC;EACd,UAAU,KAAA;EACV,aAAa;EACb,SAAS,CAAC;EACV,aAAa;EACb,UAAU;CACZ,GAEI,IAAwB;CAG5B,IAAI,EAAU,YAAY;EACxB,IAAM,EACJ,QACA,YACA,gBACA,YACA,uBAAuB,MACrB,EAAkB;GACpB;GACA,YAAY,EAAU;GACtB;GACA,iBAAiB;EACnB,CAAC,GAGK,IACJ,GACI,QAAQ,MAAW,EAAmB;GAAE;GAAQ;GAAK;EAAsB,CAAC,CAAC,CAAC,EAC9E,KAAK,OAAY;GAAE,MAAM,EAAO;GAAM,OAAO,EAAO;EAAM,EAAE,KAAK,CAAC;EAMxE,AAJA,IAAwB,GACxB,EAAW,MAAM,GACjB,EAAW,UAAU,GACrB,EAAW,cAAc,GACzB,EAAW,UAAU,CAAC,GAAG,GAAuB,GAAG,CAAO;CAC5D;CAEA,IAAM,IAAO,EAAe,EAAU,WAAW;CAGjD,IAAI,GAAM,SAAS;EACjB,IAAM,IAAW,EAAY;GAC3B,aAAa;GACb;GACA;GACA;EACF,CAAC;EAED,IAAI,MACF,EAAW,WAAW,GACtB,EAAW,WAAW,EAAS,MAAM,UAAU,IAG3C,EAAS,WAAU;GACrB,IAAM,IAA4B,EAAW,QAAQ,MAClD,MAAW,EAAO,KAAK,YAAY,MAAM,cAC5C;GAEA,AACE,MACC,CAAC,EAA0B,SACzB,EAAS,SAAS,WAAW,YAAY,KACxC,EAAS,SAAS,SAAS,WAAW,KACtC,EAAS,SAAS,KAAA,KAEtB,EAA0B,QAAQ,EAAS,WAGnC,KACR,EAAW,QAAQ,KAAK;IACtB,MAAM;IACN,OAAO,EAAS;GAClB,CAAC;EAEL;CAEJ;CAGA,IAAI,GAAiB;EACnB,IAAM,EAAE,YAAS,gBAAa,eAAY,EAAuB,CAAe;EAGhF,AAFA,EAAW,QAAQ,KAAK,GAAG,CAAO,GAClC,EAAW,YAAY,KAAK,GAAG,CAAW,GAC1C,EAAW,QAAQ,KAAK,GAAG,CAAO;CACpC;CAIA,IAAM,IAAe,EAAW,QAAQ,MAAM,MAAW,EAAO,KAAK,YAAY,MAAM,QAAQ;CAC/F,IAAI,KAAyB,KAAgB,EAAW,QAAQ,QAAQ;EACtE,IAAM,IAAe,EAAW,QAC7B,KAAK,MAAW,GAAG,mBAAmB,EAAO,IAAI,EAAE,GAAG,mBAAmB,EAAO,KAAK,GAAG,CAAC,CACzF,KAAK,IAAI;EAEZ,AADA,EAAa,QAAQ,EAAa,QAAQ,GAAG,EAAa,MAAM,IAAI,MAAiB,GACrF,EAAW,UAAU,CAAC;CACxB;CAGA,IAAI,IAAc;CAClB,KAAK,IAAM,KAAK,EAAW,SAEzB,MAAgB,EAAE,MAAM,UAAU,KAAK,KAAK,EAAE,OAAO,UAAU,KAAK;CAItE,OAFA,EAAW,cAAc,GAElB;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-body.d.ts","sourceRoot":"","sources":["../../../../src/code-example/helpers/operation-to-har/process-body.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"process-body.d.ts","sourceRoot":"","sources":["../../../../src/code-example/helpers/operation-to-har/process-body.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAEV,iBAAiB,EAElB,MAAM,8DAA8D,CAAA;AACrE,OAAO,KAAK,EAAS,QAAQ,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAE7D,KAAK,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,EAAE,aAAa,GAAG,SAAS,GAAG,iCAAiC,CAAC,GAAG;IACjH,WAAW,EAAE,iBAAiB,CAAA;CAC/B,CAAA;AA8JD;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,yEAKzB,gBAAgB,KAAG,QAAQ,GAAG,SAgIhC,CAAA"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { getResolvedRefDeep as e } from "@scalar/workspace-store/helpers/get-resolved-ref-deep";
|
|
2
2
|
import { getResolvedRef as t, mergeSiblingReferences as n } from "@scalar/workspace-store/helpers/get-resolved-ref";
|
|
3
|
-
import {
|
|
4
|
-
import { json2xml as
|
|
5
|
-
import {
|
|
3
|
+
import { buildRequestBody as r, coerceLeafValueToSchemaType as i, getExample as a, getExampleFromSchema as o, needsMultipartEncoding as s, resolveLeafSchema as c, serializeFormPropertyWithEncoding as l, serializeMultipartArray as u, serializeMultipartBody as d } from "@scalar/workspace-store/request-example";
|
|
4
|
+
import { json2xml as f } from "@scalar/helpers/file/json2xml";
|
|
5
|
+
import { getExampleValue as p, getExplicitExampleText as m } from "@scalar/workspace-store/helpers/get-example-value";
|
|
6
|
+
import { serializeStreamExample as h } from "@scalar/workspace-store/helpers/serialize-stream-example";
|
|
7
|
+
import { unpackProxyObject as g } from "@scalar/workspace-store/helpers/unpack-proxy";
|
|
6
8
|
//#region src/code-example/helpers/operation-to-har/process-body.ts
|
|
7
|
-
var
|
|
8
|
-
let
|
|
9
|
-
for (let [
|
|
10
|
-
if (
|
|
11
|
-
let
|
|
12
|
-
if (
|
|
13
|
-
let t = Array.isArray(e) ?
|
|
9
|
+
var _ = (e, t, n, r = !1, a) => {
|
|
10
|
+
let o = [], s = Array.isArray(e) ? e.filter((e) => !e.isDisabled).map((e) => [String(e.name), e.value]) : Object.entries(e);
|
|
11
|
+
for (let [d, f] of s) {
|
|
12
|
+
if (f == null) continue;
|
|
13
|
+
let s = n ? void 0 : t?.[d];
|
|
14
|
+
if (r && !n) {
|
|
15
|
+
let t = Array.isArray(e) ? i(f, c(a, [d])) : f, n = u(d, Array.isArray(t) ? t : f, s);
|
|
14
16
|
if (n) {
|
|
15
|
-
for (let e of n)
|
|
17
|
+
for (let e of n) o.push({
|
|
16
18
|
name: e.key,
|
|
17
19
|
value: e.value instanceof File ? `@${e.value.name}` : e.value,
|
|
18
20
|
...e.contentType ? { contentType: e.contentType } : {}
|
|
@@ -20,89 +22,108 @@ var d = (e, t, n, i = !1, a) => {
|
|
|
20
22
|
continue;
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
|
-
let
|
|
24
|
-
if (
|
|
25
|
+
let p = s && (s.style !== void 0 || s.explode !== void 0 || s.allowReserved !== void 0), m = p || !r ? void 0 : s?.contentType, h = n ? null : l(d, f, s);
|
|
26
|
+
if (h) for (let e of h) o.push({
|
|
25
27
|
name: e.key,
|
|
26
28
|
value: e.value
|
|
27
29
|
});
|
|
28
|
-
else if (
|
|
29
|
-
let e =
|
|
30
|
-
|
|
31
|
-
name:
|
|
30
|
+
else if (f instanceof File) {
|
|
31
|
+
let e = g(f);
|
|
32
|
+
o.push({
|
|
33
|
+
name: d,
|
|
32
34
|
value: `@${e.name}`,
|
|
33
|
-
...
|
|
35
|
+
...m ? { contentType: m } : {}
|
|
34
36
|
});
|
|
35
|
-
} else if (
|
|
36
|
-
name:
|
|
37
|
-
value: JSON.stringify(
|
|
38
|
-
contentType:
|
|
37
|
+
} else if (m && typeof f == "object") o.push({
|
|
38
|
+
name: d,
|
|
39
|
+
value: JSON.stringify(g(f)),
|
|
40
|
+
contentType: m
|
|
39
41
|
});
|
|
40
|
-
else if (
|
|
41
|
-
name:
|
|
42
|
-
value: JSON.stringify(
|
|
42
|
+
else if (r && !n && !p && typeof f == "object" && !Array.isArray(f)) o.push({
|
|
43
|
+
name: d,
|
|
44
|
+
value: JSON.stringify(g(f)),
|
|
43
45
|
contentType: "application/json"
|
|
44
46
|
});
|
|
45
|
-
else if (Array.isArray(
|
|
46
|
-
let t =
|
|
47
|
-
|
|
48
|
-
name:
|
|
47
|
+
else if (Array.isArray(f)) for (let e of f) if (e instanceof File) {
|
|
48
|
+
let t = g(e);
|
|
49
|
+
o.push({
|
|
50
|
+
name: d,
|
|
49
51
|
value: `@${t.name}`
|
|
50
52
|
});
|
|
51
|
-
} else
|
|
52
|
-
name:
|
|
53
|
+
} else o.push({
|
|
54
|
+
name: d,
|
|
53
55
|
value: String(e)
|
|
54
56
|
});
|
|
55
|
-
else if (typeof
|
|
56
|
-
let e =
|
|
57
|
-
for (let t of e)
|
|
58
|
-
name: `${
|
|
57
|
+
else if (typeof f == "object") {
|
|
58
|
+
let e = _(f, void 0, d);
|
|
59
|
+
for (let t of e) o.push({
|
|
60
|
+
name: `${d}.${t.name}`,
|
|
59
61
|
value: t.value
|
|
60
62
|
});
|
|
61
|
-
} else
|
|
62
|
-
name:
|
|
63
|
-
value: String(
|
|
64
|
-
...
|
|
63
|
+
} else o.push({
|
|
64
|
+
name: d,
|
|
65
|
+
value: String(f),
|
|
66
|
+
...m ? { contentType: m } : {}
|
|
65
67
|
});
|
|
66
68
|
}
|
|
67
|
-
return
|
|
68
|
-
},
|
|
69
|
-
let
|
|
70
|
-
if (
|
|
71
|
-
let e =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
return o;
|
|
70
|
+
}, v = ({ requestBody: i, contentType: c, example: l, requestBodyCompositionSelection: u }) => {
|
|
71
|
+
let v = c || Object.keys(i.content)[0] || "", y = v === "other" ? "" : v, b = (e) => `@${g(e).name || "filename"}`, x = i.content[v]?.encoding, S = i.content[v];
|
|
72
|
+
if (S && s(v, S)) {
|
|
73
|
+
let e = l ?? Object.keys(S.examples ?? {})[0] ?? "default", t = r({
|
|
74
|
+
...i,
|
|
75
|
+
"x-scalar-selected-content-type": { [e]: v }
|
|
76
|
+
}, e, u);
|
|
77
|
+
if (t?.mode === "multipart") {
|
|
78
|
+
let e = d(t.value, t.contentType);
|
|
79
|
+
return {
|
|
80
|
+
mimeType: e.contentType,
|
|
81
|
+
text: e.chunks.map((e) => e instanceof File ? b(e) : e instanceof Blob ? "BINARY" : String(e)).join("")
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
let C = v === "multipart/form-data" || v === "application/x-www-form-urlencoded", w = v === "application/xml", T = p(a(i, l, c)), E = m(T, v);
|
|
86
|
+
if (E !== void 0) return {
|
|
87
|
+
mimeType: y,
|
|
88
|
+
text: E
|
|
89
|
+
};
|
|
90
|
+
let D = T?.value;
|
|
91
|
+
if (D !== void 0) {
|
|
92
|
+
let e = typeof D == "object" && D ? g(D) : D;
|
|
93
|
+
return C && typeof e == "object" && e ? {
|
|
94
|
+
mimeType: y,
|
|
95
|
+
params: _(e, x, void 0, v === "multipart/form-data", t(i.content[v]?.schema, n))
|
|
96
|
+
} : w && typeof e == "object" && e ? {
|
|
97
|
+
mimeType: y,
|
|
98
|
+
text: f(e)
|
|
78
99
|
} : e instanceof File ? {
|
|
79
|
-
mimeType:
|
|
80
|
-
text:
|
|
100
|
+
mimeType: y,
|
|
101
|
+
text: b(e)
|
|
81
102
|
} : {
|
|
82
|
-
mimeType:
|
|
83
|
-
text: typeof e == "string" ? e : JSON.stringify(e)
|
|
103
|
+
mimeType: y,
|
|
104
|
+
text: typeof e == "string" ? e : h(e, v, !1) ?? JSON.stringify(e)
|
|
84
105
|
};
|
|
85
106
|
}
|
|
86
|
-
let
|
|
87
|
-
if (
|
|
88
|
-
let t =
|
|
89
|
-
compositionSelection:
|
|
107
|
+
let O = i.content[v], k = t(O?.schema ?? O?.itemSchema);
|
|
108
|
+
if (k !== void 0) {
|
|
109
|
+
let t = o(e(k), {
|
|
110
|
+
compositionSelection: u,
|
|
90
111
|
mode: "write",
|
|
91
|
-
xml:
|
|
112
|
+
xml: w
|
|
92
113
|
}, { schemaPath: ["requestBody"] });
|
|
93
|
-
if (t !== void 0) return
|
|
94
|
-
mimeType:
|
|
95
|
-
params:
|
|
96
|
-
} :
|
|
97
|
-
mimeType:
|
|
98
|
-
text:
|
|
114
|
+
if (t !== void 0) return C && typeof t == "object" && t ? {
|
|
115
|
+
mimeType: y,
|
|
116
|
+
params: _(t, x, void 0, v === "multipart/form-data")
|
|
117
|
+
} : w && typeof t == "object" && t ? {
|
|
118
|
+
mimeType: y,
|
|
119
|
+
text: f(t)
|
|
99
120
|
} : {
|
|
100
|
-
mimeType:
|
|
101
|
-
text: typeof t == "string" ? t : JSON.stringify(t)
|
|
121
|
+
mimeType: y,
|
|
122
|
+
text: h(t, v, O?.schema === void 0) ?? (typeof t == "string" ? t : JSON.stringify(t))
|
|
102
123
|
};
|
|
103
124
|
}
|
|
104
125
|
};
|
|
105
126
|
//#endregion
|
|
106
|
-
export {
|
|
127
|
+
export { v as processBody };
|
|
107
128
|
|
|
108
129
|
//# sourceMappingURL=process-body.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-body.js","names":[],"sources":["../../../../src/code-example/helpers/operation-to-har/process-body.ts"],"sourcesContent":["import { json2xml } from '@scalar/helpers/file/json2xml'\nimport { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { getResolvedRefDeep } from '@scalar/workspace-store/helpers/get-resolved-ref-deep'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n coerceLeafValueToSchemaType,\n getExample,\n getExampleFromSchema,\n resolveLeafSchema,\n serializeFormPropertyWithEncoding,\n serializeMultipartArray,\n} from '@scalar/workspace-store/request-example'\nimport type {\n MediaTypeObject,\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport type { Param, PostData } from 'har-format'\n\nimport type { OperationToHarProps } from './operation-to-har'\n\ntype ProcessBodyProps = Pick<OperationToHarProps, 'contentType' | 'example' | 'requestBodyCompositionSelection'> & {\n requestBody: RequestBodyObject\n}\n\ntype MultipartEncodingMap = MediaTypeObject['encoding']\n\n/**\n * Converts a form-data body into HAR `Param[]` entries for `multipart/form-data`\n * and `application/x-www-form-urlencoded` requests.\n *\n * Per OpenAPI 3.1.x Encoding Object, each property's serialization is governed\n * by an optional Encoding entry. When `style` / `explode` / `allowReserved` is\n * set, the value is serialized RFC6570-style and `contentType` is ignored.\n * Multipart arrays apply the encoding to each item and repeat the property name.\n * Object items default to JSON, and uploaded files retain their filename references.\n *\n * @param obj - The form-data payload, either an object keyed by property name\n * or a HAR-style `{ name, value, isDisabled }[]` array.\n * @param encoding - The `encoding` map from the Media Type Object, if any.\n * Used only at the top level (`parentKey` undefined).\n * @param parentKey - When set, we are flattening a nested object inside another\n * property; encoding is ignored and keys are joined with `.` (legacy default).\n * @param isMultipart - True for `multipart/form-data`. Gates the spec defaults\n * for object properties and array items. Urlencoded bodies retain their existing\n * serialization and flattening behavior.\n */\nconst objectToFormParams = (\n obj: object | { name: string; value: unknown; isDisabled: boolean }[],\n encoding?: MultipartEncodingMap,\n parentKey?: string,\n isMultipart = false,\n schema?: SchemaObject,\n): Param[] => {\n const params: Param[] = []\n\n /** Ensure we do not include disabled items */\n const entries: [string, unknown][] = Array.isArray(obj)\n ? obj.filter((item) => !item.isDisabled).map((item) => [String(item.name), item.value])\n : Object.entries(obj)\n\n for (const [key, value] of entries) {\n if (value === undefined || value === null) {\n continue\n }\n\n const partEncoding = parentKey ? undefined : encoding?.[key]\n\n if (isMultipart && !parentKey) {\n const restored = Array.isArray(obj) ? coerceLeafValueToSchemaType(value, resolveLeafSchema(schema, [key])) : value\n const arrayParts = serializeMultipartArray(key, Array.isArray(restored) ? restored : value, partEncoding)\n if (arrayParts) {\n for (const part of arrayParts) {\n params.push({\n name: part.key,\n value: part.value instanceof File ? `@${part.value.name}` : part.value,\n ...(part.contentType ? { contentType: part.contentType } : {}),\n })\n }\n continue\n }\n }\n /**\n * Per OpenAPI 3.1.1: when style, explode, or allowReserved is explicitly set on the\n * encoding entry, contentType (implicit or explicit) is ignored and the value is\n * serialized as if it were a query-style parameter.\n */\n const hasFormStyle =\n partEncoding &&\n (partEncoding.style !== undefined ||\n partEncoding.explode !== undefined ||\n partEncoding.allowReserved !== undefined)\n /**\n * Per OAS 3.1.x Encoding Object: `contentType` SHALL be ignored if the request body\n * media type is not a multipart. For `application/x-www-form-urlencoded` we still\n * honor `style`/`explode`/`allowReserved` (handled above), but a `contentType`-only\n * entry has no effect — values fall through to the dotted-key flattening default.\n */\n const explicitContentType = hasFormStyle || !isMultipart ? undefined : partEncoding?.contentType\n\n /**\n * When the encoding sets style/explode/allowReserved, serialize the value RFC6570-style\n * (bracket/exploded notation) and ignore contentType. See `serializeFormPropertyWithEncoding`\n * for the per-style rules; it returns null for primitives, Files, and arrays of Files, which\n * then fall through to the dedicated branches below. HAR represents multipart and urlencoded\n * the same way via `PostData.params`, so the resulting parts work for both.\n */\n const styleParams = parentKey ? null : serializeFormPropertyWithEncoding(key, value, partEncoding)\n if (styleParams) {\n for (const param of styleParams) {\n params.push({ name: param.key, value: param.value })\n }\n } else if (value instanceof File) {\n /**\n * File values render as `@filename` references, the conventional cURL syntax for an\n * attached file. Picked up by snippet renderers downstream (e.g. `--form 'x=@file.png'`).\n */\n const file = unpackProxyObject(value)\n params.push({\n name: key,\n value: `@${file.name}`,\n ...(explicitContentType ? { contentType: explicitContentType } : {}),\n })\n } else if (explicitContentType && typeof value === 'object') {\n /**\n * Per OAS 3.1.x Encoding Object: an explicit `encoding[key].contentType` on a\n * complex value overrides the default and emits a single part containing the value\n * JSON-stringified into that media type. Only reachable when style/explode/allowReserved\n * are unset (otherwise contentType is ignored — see the style branch above).\n */\n params.push({\n name: key,\n value: JSON.stringify(unpackProxyObject(value)),\n contentType: explicitContentType,\n })\n } else if (isMultipart && !parentKey && !hasFormStyle && typeof value === 'object' && !Array.isArray(value)) {\n /**\n * Per OpenAPI 3.x: a top-level multipart property whose value is an object (and not a File)\n * defaults to a single part encoded as application/json, rather than being flattened\n * into multiple parts with dotted keys.\n */\n params.push({\n name: key,\n value: JSON.stringify(unpackProxyObject(value)),\n contentType: 'application/json',\n })\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (item instanceof File) {\n const file = unpackProxyObject(item)\n params.push({ name: key, value: `@${file.name}` })\n } else {\n params.push({ name: key, value: String(item) })\n }\n }\n } else if (typeof value === 'object') {\n /**\n * Legacy fallback: flatten nested objects into dotted-key parts. Reached when the\n * multipart JSON-default branches above don't apply — i.e. either inside a recursive\n * call (`parentKey` set) or on `application/x-www-form-urlencoded` bodies without an\n * explicit encoding entry. Strict OAS 3.1.x would use `style: form, explode: true` as\n * the urlencoded default (inner keys become top-level params); we keep dotted keys for\n * backwards compatibility with consumers that already parse this shape.\n */\n const nestedParams = objectToFormParams(value, undefined, key)\n\n for (const param of nestedParams) {\n params.push({ name: `${key}.${param.name}`, value: param.value })\n }\n } else {\n params.push({\n name: key,\n value: String(value),\n ...(explicitContentType ? { contentType: explicitContentType } : {}),\n })\n }\n }\n\n return params\n}\n\n/**\n * Processes the request body and returns the processed data\n * Returns undefined if no example is found\n */\nexport const processBody = ({\n requestBody,\n contentType,\n example,\n requestBodyCompositionSelection,\n}: ProcessBodyProps): PostData | undefined => {\n const _contentType = contentType || Object.keys(requestBody.content)[0] || ''\n /** Empty when \"Other\" is selected so snippets do not inject `Content-Type: other`. */\n const harMimeType = _contentType === 'other' ? '' : _contentType\n const formatBinaryFile = (file: File) => {\n const unwrappedFile = unpackProxyObject(file)\n return `@${unwrappedFile.name || 'filename'}`\n }\n const encoding = requestBody.content[_contentType]?.encoding\n\n // Check if this is a form data content type\n const isFormData = _contentType === 'multipart/form-data' || _contentType === 'application/x-www-form-urlencoded'\n\n // Check if this is an XML content type\n const isXml = _contentType === 'application/xml'\n\n // Get the example value\n const _example = getExample(requestBody, example, contentType)?.value\n\n // Return the provided top level example\n if (typeof _example !== 'undefined') {\n const exampleValue = _example !== null && typeof _example === 'object' ? unpackProxyObject(_example) : _example\n\n if (isFormData && typeof exampleValue === 'object' && exampleValue !== null) {\n return {\n mimeType: harMimeType,\n params: objectToFormParams(\n exampleValue,\n encoding,\n undefined,\n _contentType === 'multipart/form-data',\n getResolvedRef(requestBody.content[_contentType]?.schema, mergeSiblingReferences),\n ),\n }\n }\n\n if (isXml && typeof exampleValue === 'object' && exampleValue !== null) {\n return {\n mimeType: harMimeType,\n text: json2xml(exampleValue),\n }\n }\n\n if (exampleValue instanceof File) {\n return {\n mimeType: harMimeType,\n text: formatBinaryFile(exampleValue),\n }\n }\n\n return {\n mimeType: harMimeType,\n text: typeof exampleValue === 'string' ? exampleValue : JSON.stringify(exampleValue),\n }\n }\n\n // Try to extract examples from the schema\n const contentSchema = getResolvedRef(requestBody.content[_contentType]?.schema)\n if (typeof contentSchema !== 'undefined') {\n const resolvedContentSchema = getResolvedRefDeep(contentSchema) as SchemaObject\n const extractedExample = getExampleFromSchema(\n resolvedContentSchema,\n {\n compositionSelection: requestBodyCompositionSelection,\n mode: 'write',\n xml: isXml,\n },\n {\n schemaPath: ['requestBody'],\n },\n )\n\n if (extractedExample !== undefined) {\n if (isFormData && typeof extractedExample === 'object' && extractedExample !== null) {\n return {\n mimeType: harMimeType,\n params: objectToFormParams(extractedExample, encoding, undefined, _contentType === 'multipart/form-data'),\n }\n }\n\n if (isXml && typeof extractedExample === 'object' && extractedExample !== null) {\n return {\n mimeType: harMimeType,\n text: json2xml(extractedExample),\n }\n }\n\n return {\n mimeType: harMimeType,\n text: typeof extractedExample === 'string' ? extractedExample : JSON.stringify(extractedExample),\n }\n }\n }\n\n return undefined\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"process-body.js","names":[],"sources":["../../../../src/code-example/helpers/operation-to-har/process-body.ts"],"sourcesContent":["import { json2xml } from '@scalar/helpers/file/json2xml'\nimport { getExampleValue, getExplicitExampleText } from '@scalar/workspace-store/helpers/get-example-value'\nimport { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { getResolvedRefDeep } from '@scalar/workspace-store/helpers/get-resolved-ref-deep'\nimport { serializeStreamExample } from '@scalar/workspace-store/helpers/serialize-stream-example'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n buildRequestBody,\n coerceLeafValueToSchemaType,\n getExample,\n getExampleFromSchema,\n needsMultipartEncoding,\n resolveLeafSchema,\n serializeFormPropertyWithEncoding,\n serializeMultipartArray,\n serializeMultipartBody,\n} from '@scalar/workspace-store/request-example'\nimport type {\n MediaTypeObject,\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport type { Param, PostData } from 'har-format'\n\nimport type { OperationToHarProps } from './operation-to-har'\n\ntype ProcessBodyProps = Pick<OperationToHarProps, 'contentType' | 'example' | 'requestBodyCompositionSelection'> & {\n requestBody: RequestBodyObject\n}\n\ntype MultipartEncodingMap = MediaTypeObject['encoding']\n\n/**\n * Converts a form-data body into HAR `Param[]` entries for `multipart/form-data`\n * and `application/x-www-form-urlencoded` requests.\n *\n * Per OpenAPI 3.1.x Encoding Object, each property's serialization is governed\n * by an optional Encoding entry. When `style` / `explode` / `allowReserved` is\n * set, the value is serialized RFC6570-style and `contentType` is ignored.\n * Multipart arrays apply the encoding to each item and repeat the property name.\n * Object items default to JSON, and uploaded files retain their filename references.\n *\n * @param obj - The form-data payload, either an object keyed by property name\n * or a HAR-style `{ name, value, isDisabled }[]` array.\n * @param encoding - The `encoding` map from the Media Type Object, if any.\n * Used only at the top level (`parentKey` undefined).\n * @param parentKey - When set, we are flattening a nested object inside another\n * property; encoding is ignored and keys are joined with `.` (legacy default).\n * @param isMultipart - True for `multipart/form-data`. Gates the spec defaults\n * for object properties and array items. Urlencoded bodies retain their existing\n * serialization and flattening behavior.\n */\nconst objectToFormParams = (\n obj: object | { name: string; value: unknown; isDisabled: boolean }[],\n encoding?: MultipartEncodingMap,\n parentKey?: string,\n isMultipart = false,\n schema?: SchemaObject,\n): Param[] => {\n const params: Param[] = []\n\n /** Ensure we do not include disabled items */\n const entries: [string, unknown][] = Array.isArray(obj)\n ? obj.filter((item) => !item.isDisabled).map((item) => [String(item.name), item.value])\n : Object.entries(obj)\n\n for (const [key, value] of entries) {\n if (value === undefined || value === null) {\n continue\n }\n\n const partEncoding = parentKey ? undefined : encoding?.[key]\n\n if (isMultipart && !parentKey) {\n const restored = Array.isArray(obj) ? coerceLeafValueToSchemaType(value, resolveLeafSchema(schema, [key])) : value\n const arrayParts = serializeMultipartArray(key, Array.isArray(restored) ? restored : value, partEncoding)\n if (arrayParts) {\n for (const part of arrayParts) {\n params.push({\n name: part.key,\n value: part.value instanceof File ? `@${part.value.name}` : part.value,\n ...(part.contentType ? { contentType: part.contentType } : {}),\n })\n }\n continue\n }\n }\n /**\n * Per OpenAPI 3.1.1: when style, explode, or allowReserved is explicitly set on the\n * encoding entry, contentType (implicit or explicit) is ignored and the value is\n * serialized as if it were a query-style parameter.\n */\n const hasFormStyle =\n partEncoding &&\n (partEncoding.style !== undefined ||\n partEncoding.explode !== undefined ||\n partEncoding.allowReserved !== undefined)\n /**\n * Per OAS 3.1.x Encoding Object: `contentType` SHALL be ignored if the request body\n * media type is not a multipart. For `application/x-www-form-urlencoded` we still\n * honor `style`/`explode`/`allowReserved` (handled above), but a `contentType`-only\n * entry has no effect — values fall through to the dotted-key flattening default.\n */\n const explicitContentType = hasFormStyle || !isMultipart ? undefined : partEncoding?.contentType\n\n /**\n * When the encoding sets style/explode/allowReserved, serialize the value RFC6570-style\n * (bracket/exploded notation) and ignore contentType. See `serializeFormPropertyWithEncoding`\n * for the per-style rules; it returns null for primitives, Files, and arrays of Files, which\n * then fall through to the dedicated branches below. HAR represents multipart and urlencoded\n * the same way via `PostData.params`, so the resulting parts work for both.\n */\n const styleParams = parentKey ? null : serializeFormPropertyWithEncoding(key, value, partEncoding)\n if (styleParams) {\n for (const param of styleParams) {\n params.push({ name: param.key, value: param.value })\n }\n } else if (value instanceof File) {\n /**\n * File values render as `@filename` references, the conventional cURL syntax for an\n * attached file. Picked up by snippet renderers downstream (e.g. `--form 'x=@file.png'`).\n */\n const file = unpackProxyObject(value)\n params.push({\n name: key,\n value: `@${file.name}`,\n ...(explicitContentType ? { contentType: explicitContentType } : {}),\n })\n } else if (explicitContentType && typeof value === 'object') {\n /**\n * Per OAS 3.1.x Encoding Object: an explicit `encoding[key].contentType` on a\n * complex value overrides the default and emits a single part containing the value\n * JSON-stringified into that media type. Only reachable when style/explode/allowReserved\n * are unset (otherwise contentType is ignored — see the style branch above).\n */\n params.push({\n name: key,\n value: JSON.stringify(unpackProxyObject(value)),\n contentType: explicitContentType,\n })\n } else if (isMultipart && !parentKey && !hasFormStyle && typeof value === 'object' && !Array.isArray(value)) {\n /**\n * Per OpenAPI 3.x: a top-level multipart property whose value is an object (and not a File)\n * defaults to a single part encoded as application/json, rather than being flattened\n * into multiple parts with dotted keys.\n */\n params.push({\n name: key,\n value: JSON.stringify(unpackProxyObject(value)),\n contentType: 'application/json',\n })\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (item instanceof File) {\n const file = unpackProxyObject(item)\n params.push({ name: key, value: `@${file.name}` })\n } else {\n params.push({ name: key, value: String(item) })\n }\n }\n } else if (typeof value === 'object') {\n /**\n * Legacy fallback: flatten nested objects into dotted-key parts. Reached when the\n * multipart JSON-default branches above don't apply — i.e. either inside a recursive\n * call (`parentKey` set) or on `application/x-www-form-urlencoded` bodies without an\n * explicit encoding entry. Strict OAS 3.1.x would use `style: form, explode: true` as\n * the urlencoded default (inner keys become top-level params); we keep dotted keys for\n * backwards compatibility with consumers that already parse this shape.\n */\n const nestedParams = objectToFormParams(value, undefined, key)\n\n for (const param of nestedParams) {\n params.push({ name: `${key}.${param.name}`, value: param.value })\n }\n } else {\n params.push({\n name: key,\n value: String(value),\n ...(explicitContentType ? { contentType: explicitContentType } : {}),\n })\n }\n }\n\n return params\n}\n\n/**\n * Processes the request body and returns the processed data\n * Returns undefined if no example is found\n */\nexport const processBody = ({\n requestBody,\n contentType,\n example,\n requestBodyCompositionSelection,\n}: ProcessBodyProps): PostData | undefined => {\n const _contentType = contentType || Object.keys(requestBody.content)[0] || ''\n /** Empty when \"Other\" is selected so snippets do not inject `Content-Type: other`. */\n const harMimeType = _contentType === 'other' ? '' : _contentType\n const formatBinaryFile = (file: File) => {\n const unwrappedFile = unpackProxyObject(file)\n return `@${unwrappedFile.name || 'filename'}`\n }\n const encoding = requestBody.content[_contentType]?.encoding\n\n const media = requestBody.content[_contentType]\n if (media && needsMultipartEncoding(_contentType, media)) {\n const exampleName = example ?? Object.keys(media.examples ?? {})[0] ?? 'default'\n const body = buildRequestBody(\n { ...requestBody, 'x-scalar-selected-content-type': { [exampleName]: _contentType } },\n exampleName,\n requestBodyCompositionSelection,\n )\n if (body?.mode === 'multipart') {\n const encoded = serializeMultipartBody(body.value, body.contentType)\n return {\n mimeType: encoded.contentType,\n // HAR text cannot embed file bytes synchronously. Keep visible file placeholders.\n text: encoded.chunks\n .map((chunk) =>\n chunk instanceof File ? formatBinaryFile(chunk) : chunk instanceof Blob ? 'BINARY' : String(chunk),\n )\n .join(''),\n }\n }\n }\n\n // Check if this is a form data content type\n const isFormData = _contentType === 'multipart/form-data' || _contentType === 'application/x-www-form-urlencoded'\n\n // Check if this is an XML content type\n const isXml = _contentType === 'application/xml'\n\n // Get the example value\n const selected = getExampleValue(getExample(requestBody, example, contentType))\n const explicitText = getExplicitExampleText(selected, _contentType)\n if (explicitText !== undefined) {\n return { mimeType: harMimeType, text: explicitText }\n }\n const _example = selected?.value\n\n // Return the provided top level example\n if (typeof _example !== 'undefined') {\n const exampleValue = _example !== null && typeof _example === 'object' ? unpackProxyObject(_example) : _example\n\n if (isFormData && typeof exampleValue === 'object' && exampleValue !== null) {\n return {\n mimeType: harMimeType,\n params: objectToFormParams(\n exampleValue,\n encoding,\n undefined,\n _contentType === 'multipart/form-data',\n getResolvedRef(requestBody.content[_contentType]?.schema, mergeSiblingReferences),\n ),\n }\n }\n\n if (isXml && typeof exampleValue === 'object' && exampleValue !== null) {\n return {\n mimeType: harMimeType,\n text: json2xml(exampleValue),\n }\n }\n\n if (exampleValue instanceof File) {\n return {\n mimeType: harMimeType,\n text: formatBinaryFile(exampleValue),\n }\n }\n\n return {\n mimeType: harMimeType,\n text:\n typeof exampleValue === 'string'\n ? exampleValue\n : (serializeStreamExample(exampleValue, _contentType, false) ?? JSON.stringify(exampleValue)),\n }\n }\n\n // Try to extract examples from the schema\n const mediaType = requestBody.content[_contentType]\n const contentSchema = getResolvedRef(mediaType?.schema ?? mediaType?.itemSchema)\n if (typeof contentSchema !== 'undefined') {\n const resolvedContentSchema = getResolvedRefDeep(contentSchema) as SchemaObject\n const extractedExample = getExampleFromSchema(\n resolvedContentSchema,\n {\n compositionSelection: requestBodyCompositionSelection,\n mode: 'write',\n xml: isXml,\n },\n {\n schemaPath: ['requestBody'],\n },\n )\n\n if (extractedExample !== undefined) {\n if (isFormData && typeof extractedExample === 'object' && extractedExample !== null) {\n return {\n mimeType: harMimeType,\n params: objectToFormParams(extractedExample, encoding, undefined, _contentType === 'multipart/form-data'),\n }\n }\n\n if (isXml && typeof extractedExample === 'object' && extractedExample !== null) {\n return {\n mimeType: harMimeType,\n text: json2xml(extractedExample),\n }\n }\n\n return {\n mimeType: harMimeType,\n text:\n serializeStreamExample(extractedExample, _contentType, mediaType?.schema === undefined) ??\n (typeof extractedExample === 'string' ? extractedExample : JSON.stringify(extractedExample)),\n }\n }\n }\n\n return undefined\n}\n"],"mappings":"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"}
|
|
@@ -5,6 +5,8 @@ type ProcessedParameters = {
|
|
|
5
5
|
headers: HarRequest['headers'];
|
|
6
6
|
queryString: HarRequest['queryString'];
|
|
7
7
|
cookies: HarRequest['cookies'];
|
|
8
|
+
/** Whether serialized cookie-style values require a complete Cookie header. */
|
|
9
|
+
hasCookieStyleEntries: boolean;
|
|
8
10
|
};
|
|
9
11
|
/**
|
|
10
12
|
* Process OpenAPI parameters and return the updated properties.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-parameters.d.ts","sourceRoot":"","sources":["../../../../src/code-example/helpers/operation-to-har/process-parameters.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"process-parameters.d.ts","sourceRoot":"","sources":["../../../../src/code-example/helpers/operation-to-har/process-parameters.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,8DAA8D,CAAA;AACpH,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvD,KAAK,mBAAmB,GAAG;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;IAC9B,WAAW,EAAE,UAAU,CAAC,aAAa,CAAC,CAAA;IACtC,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;IAC9B,+EAA+E;IAC/E,qBAAqB,EAAE,OAAO,CAAA;CAC/B,CAAA;AAiGD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,uDAK/B;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,UAAU,EAAE,eAAe,CAAC,YAAY,CAAC,CAAA;IACzC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,gDAAgD;IAChD,eAAe,EAAE,OAAO,CAAA;CACzB,KAAG,mBAiKH,CAAA"}
|