diff --git a/docs/customizations/parts/code-samples-example.md b/docs/customizations/parts/code-samples-example.md
index 36bd13a..52aa0b1 100644
--- a/docs/customizations/parts/code-samples-example.md
+++ b/docs/customizations/parts/code-samples-example.md
@@ -18,7 +18,7 @@ useTheme({
...useTheme().getCodeSamplesAvailableLanguages(),
],
defaultLang: 'bruno',
- generator: (lang, request) => {
+ generator: async (lang, request) => {
if (lang === 'bruno') {
return generateBruRequest(request)
}
diff --git a/package.json b/package.json
index 3f4b3cc..712df6c 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"prepublishOnly": "pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
- "dev": "cd docs && pnpm run dev",
+ "dev": "cd docs && VITE_DEBUG=1 pnpm run dev",
"docs:build": "pnpm run build && cd docs && pnpm run build",
"test": "vitest",
"test:run": "vitest --run",
diff --git a/src/components/Feature/OAContext.vue b/src/components/Feature/OAContext.vue
new file mode 100644
index 0000000..cbd6738
--- /dev/null
+++ b/src/components/Feature/OAContext.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/src/components/Feature/OAContextProvider.vue b/src/components/Feature/OAContextProvider.vue
new file mode 100644
index 0000000..7eeb966
--- /dev/null
+++ b/src/components/Feature/OAContextProvider.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Feature/OAInfo.vue b/src/components/Feature/OAInfo.vue
index 5af73bb..e5217dd 100644
--- a/src/components/Feature/OAInfo.vue
+++ b/src/components/Feature/OAInfo.vue
@@ -1,108 +1,19 @@
-
+
diff --git a/src/components/Feature/OAInfoContent.vue b/src/components/Feature/OAInfoContent.vue
new file mode 100644
index 0000000..726aaf7
--- /dev/null
+++ b/src/components/Feature/OAInfoContent.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
diff --git a/src/components/Feature/OAIntroduction.vue b/src/components/Feature/OAIntroduction.vue
index dc6b85c..de549be 100644
--- a/src/components/Feature/OAIntroduction.vue
+++ b/src/components/Feature/OAIntroduction.vue
@@ -1,27 +1,20 @@
-
-
+
+
+
+
+
+
diff --git a/src/components/Feature/OAOperation.vue b/src/components/Feature/OAOperation.vue
index a39b2fb..1328a09 100644
--- a/src/components/Feature/OAOperation.vue
+++ b/src/components/Feature/OAOperation.vue
@@ -1,10 +1,7 @@
-
-
-
-
-
-
-
-
-
- {{ header.operation.summary }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('Parameters') }}
-
-
-
-
-
-
-
-
-
-
- {{ $t('Request Body') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
- {{ $t('Samples') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/src/components/Feature/OAOperationContent.vue b/src/components/Feature/OAOperationContent.vue
new file mode 100644
index 0000000..635eaf6
--- /dev/null
+++ b/src/components/Feature/OAOperationContent.vue
@@ -0,0 +1,352 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ header.operation.summary }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('Parameters') }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t('Request Body') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('Samples') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Feature/OAServers.vue b/src/components/Feature/OAServers.vue
index 398a192..eedaa3d 100644
--- a/src/components/Feature/OAServers.vue
+++ b/src/components/Feature/OAServers.vue
@@ -1,44 +1,19 @@
-
-
- {{ $t('Servers') }}
-
-
-
-
-
- {{ server.url }}
-
-
-
- {{ server.description }}
-
-
-
-
+
+
+
+
+
diff --git a/src/components/Feature/OAServersContent.vue b/src/components/Feature/OAServersContent.vue
new file mode 100644
index 0000000..893bc4d
--- /dev/null
+++ b/src/components/Feature/OAServersContent.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+ {{ $t('Servers') }}
+
+
+
+
+
+ {{ server.url }}
+
+
+
+ {{ server.description }}
+
+
+
+
+
diff --git a/src/components/Feature/OASpec.vue b/src/components/Feature/OASpec.vue
index c69c1b9..dce6fc8 100644
--- a/src/components/Feature/OASpec.vue
+++ b/src/components/Feature/OASpec.vue
@@ -1,14 +1,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Feature/OASpecContent.vue b/src/components/Feature/OASpecContent.vue
new file mode 100644
index 0000000..ed76bc5
--- /dev/null
+++ b/src/components/Feature/OASpecContent.vue
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Path/OAPath.vue b/src/components/Path/OAPath.vue
index fe46815..305ff14 100644
--- a/src/components/Path/OAPath.vue
+++ b/src/components/Path/OAPath.vue
@@ -4,6 +4,7 @@ import { computed, defineProps, provide, ref } from 'vue'
import { useTheme } from '../../composables/useTheme'
import { buildRequest } from '../../lib/codeSamples/buildRequest'
import { initOperationData, OPERATION_DATA_KEY } from '../../lib/operationData'
+import { resolveBaseUrl } from '../../lib/resolveBaseUrl'
const props = defineProps({
id: {
@@ -76,15 +77,15 @@ const selectedServer = servers?.length > 1 && typeof localStorage !== 'undefined
: ref(defaultServer)
const baseUrl = computed(() => {
- const value = selectedServer.value
+ let value = selectedServer.value
if (servers.length > 1 && !servers.some(server => server.url === value)) {
updateSelectedServer(servers[0]?.url)
- return servers[0]?.url
+ value = servers[0]?.url
}
- return value
+ return resolveBaseUrl(value)
})
const shouldBuildRequest = computed(() => ['try-it', 'code-samples'].some(slot => operationSlots.value.includes(slot)))
@@ -103,6 +104,8 @@ const request = ref(
: {},
)
+const codeSamples = props.operation.codeSamples
+
function updateRequest(newRequest) {
request.value = newRequest
}
@@ -276,6 +279,7 @@ function updateSelectedServer(server) {
:path="operationPath"
:request="request"
:update-request="updateRequest"
+ :code-samples="codeSamples"
/>
diff --git a/src/components/Path/OAPaths.vue b/src/components/Path/OAPaths.vue
index 54db6a4..6fccbb8 100644
--- a/src/components/Path/OAPaths.vue
+++ b/src/components/Path/OAPaths.vue
@@ -1,8 +1,10 @@
@@ -35,7 +39,8 @@ const lazyRendering = themeConfig.getSpecConfig()?.lazyRendering?.value
:key="operation.operationId"
:is-lazy="lazyRendering && operationIdx > 0"
>
-
-
+
diff --git a/src/components/Sample/OACodeSamples.vue b/src/components/Sample/OACodeSamples.vue
index 30732f7..30d04dd 100644
--- a/src/components/Sample/OACodeSamples.vue
+++ b/src/components/Sample/OACodeSamples.vue
@@ -12,6 +12,10 @@ const props = defineProps({
type: Object,
default: () => (new OARequest()),
},
+ codeSamples: {
+ type: Object,
+ default: () => ({}),
+ },
})
const themeConfig = useTheme()
@@ -24,7 +28,7 @@ const generator = themeConfig.getCodeSamplesGenerator()
const defaultLang = themeConfig.getCodeSamplesDefaultLang()
-const samples = ref([])
+const samples = ref(props.codeSamples)
const loadSamples = async () => {
samples.value = await Promise.all(
@@ -44,9 +48,7 @@ const loadSamples = async () => {
)
}
-loadSamples()
-
-watch(() => props.request, loadSamples, { deep: true })
+watch(() => props.request, loadSamples, { deep: true, immediate: true })
diff --git a/src/composables/useOpenapi.ts b/src/composables/useOpenapi.ts
index b42831a..a890383 100644
--- a/src/composables/useOpenapi.ts
+++ b/src/composables/useOpenapi.ts
@@ -1,26 +1,20 @@
import type { OpenAPI } from '@scalar/openapi-types'
+import type { OpenApi } from '../lib/OpenApi'
import type { PartialUseThemeConfig } from './useTheme'
import { createOpenApiInstance } from '../lib/createOpenApiInstance'
import { useTheme } from './useTheme'
export interface OpenAPIData {
- id: string
spec: OpenAPI.Document
openapi: ReturnType
config?: PartialUseThemeConfig
}
-export type Schemas = Map
-
-export const DEFAULT_SCHEMA = 'main'
-
export const OPENAPI_GLOBAL_KEY = Symbol('openapi')
export const OPENAPI_LOCAL_KEY = Symbol('openapiLocal')
-const schemas: Schemas = new Map()
-
-let mainSchema: OpenAPI.Document | null = null
+let openapi: ReturnType | null = null
export function useOpenapi({
spec,
@@ -37,38 +31,16 @@ export function useOpenapi({
setupOpenApi({ spec, config })
}
- /**
- * @deprecated Use `useOpenapi({ spec })` instead.
- */
- function setSpec(value: OpenAPI.Document) {
- console.warn('Deprecated usage of `setSpec`. Use `useOpenapi({ spec })` instead.')
- setupOpenApi({ spec: value })
- }
-
function setupOpenApi({ spec, config }: { spec: OpenAPI.Document, config?: PartialUseThemeConfig }) {
- addSchema({ id: DEFAULT_SCHEMA, spec, config })
- mainSchema = (schemas.get(DEFAULT_SCHEMA) ?? {}) as OpenAPI.Document
- }
-
- function addSchema({ id, spec, config }: { id: string, spec: OpenAPI.Document, config?: PartialUseThemeConfig }) {
- const openapi = createOpenApiInstance({
+ openapi = createOpenApiInstance({
spec,
defaultTag: config?.spec?.defaultTag,
defaultTagDescription: config?.spec?.defaultTagDescription,
})
-
- // @ts-expect-error: This adds all the properties of the OpenAPI instance to the schema.
- schemas.set(id, {
- ...openapi,
- id,
- config,
- })
}
return {
- ...mainSchema,
- json: mainSchema?.spec,
- setSpec,
- schemas,
+ ...openapi,
+ json: openapi?.spec,
}
}
diff --git a/src/composables/useTheme.ts b/src/composables/useTheme.ts
index d02784b..8401a66 100644
--- a/src/composables/useTheme.ts
+++ b/src/composables/useTheme.ts
@@ -156,7 +156,7 @@ export const DEFAULT_OPERATION_SLOTS: OperationSlot[] = [
export const DEFAULT_BASE_URL = 'http://localhost'
-const availableLanguages: LanguageConfig[] = [
+export const availableLanguages: LanguageConfig[] = [
{
lang: 'curl',
label: 'cURL',
@@ -669,11 +669,19 @@ export function useTheme(initialConfig: PartialUseThemeConfig = {}) {
}
function getCodeSamplesLangs() {
- return themeConfig?.codeSamples?.langs
+ return themeConfig?.codeSamples?.langs?.filter((lang, index, self) => self.indexOf(lang) === index)
}
function getCodeSamplesDefaultLang() {
- return themeConfig?.codeSamples?.defaultLang
+ const availableLangs = getCodeSamplesLangs() || []
+
+ const defaultLang = themeConfig?.codeSamples?.defaultLang
+
+ if (defaultLang && availableLangs.includes(defaultLang)) {
+ return defaultLang
+ }
+
+ return availableLangs[0]
}
function getCodeSamplesAvailableLanguages() {
@@ -691,7 +699,7 @@ export function useTheme(initialConfig: PartialUseThemeConfig = {}) {
function setCodeSamplesConfig(config: Partial>) {
if (config.langs) {
// @ts-expect-error: This is a valid expression.
- themeConfig.codeSamples.langs = config.langs
+ themeConfig.codeSamples.langs = config.langs.filter((lang, index, self) => self.indexOf(lang) === index)
}
if (config.defaultLang) {
@@ -700,8 +708,7 @@ export function useTheme(initialConfig: PartialUseThemeConfig = {}) {
}
if (config.availableLanguages) {
- // @ts-expect-error: This is a valid expression.
- themeConfig.codeSamples.availableLanguages = config.availableLanguages
+ setCodeSamplesAvailableLanguages(config.availableLanguages)
}
if (config.generator) {
@@ -715,6 +722,19 @@ export function useTheme(initialConfig: PartialUseThemeConfig = {}) {
}
}
+ function setCodeSamplesAvailableLanguages(languages: LanguageConfig[]) {
+ if (!themeConfig.codeSamples) {
+ return
+ }
+
+ const uniqueLanguages = [...new Set(languages.map(({ lang }) => lang))]
+
+ themeConfig.codeSamples.availableLanguages = uniqueLanguages.map((lang) => {
+ const language = availableLanguages.find(l => l.lang === lang)
+ return language || { lang, label: lang, highlighter: 'plaintext' }
+ })
+ }
+
function getLinksPrefixesConfig() {
return themeConfig.linksPrefixes
}
diff --git a/src/lib/OpenApi.ts b/src/lib/OpenApi.ts
index 9caa51c..198087e 100644
--- a/src/lib/OpenApi.ts
+++ b/src/lib/OpenApi.ts
@@ -1,7 +1,7 @@
-import type { OpenAPIV3 } from '@scalar/openapi-types'
+import type { OpenAPI, OpenAPIV3 } from '@scalar/openapi-types'
import type { ParsedOpenAPI } from '../types'
import { httpVerbs } from '../index'
-import { processOpenAPI } from './processOpenAPI'
+import { processOpenAPI } from './processOpenAPI/processOpenAPI'
export function OpenApi({
spec,
@@ -16,6 +16,24 @@ export function OpenApi({
transformedSpec: null,
parsedSpec: null,
}) {
+ let innerSpec: OpenAPI.Document | null = null
+
+ function setSpec(spec: any) {
+ innerSpec = spec
+ }
+
+ function getSpec(): OpenAPI.Document {
+ if (!innerSpec) {
+ setSpec(parsedSpec ?? transformedSpec ?? spec ?? {})
+ }
+
+ if (!innerSpec) {
+ throw new Error('OpenAPI spec is not defined')
+ }
+
+ return innerSpec
+ }
+
function findOperation(paths: OpenAPIV3.PathsObject, operationId: string) {
for (const path of Object.values(paths)) {
for (const verb of httpVerbs) {
@@ -27,11 +45,7 @@ export function OpenApi({
return null
}
- function getSpec(): ParsedOpenAPI {
- return parsedSpec ?? transformedSpec ?? spec ?? {}
- }
-
- function getParsedSpec() {
+ function getParsedSpec(): ParsedOpenAPI {
if (!parsedSpec) {
parsedSpec = processOpenAPI(transformedSpec ?? spec)
}
@@ -40,21 +54,25 @@ export function OpenApi({
}
function getOperation(operationId: string) {
- if (!getSpec().paths) {
+ const paths = getSpec().paths as OpenAPIV3.PathsObject
+
+ if (!paths) {
return null
}
- return findOperation(getSpec().paths, operationId)
+ return findOperation(paths, operationId)
}
function getOperationPath(operationId: string) {
- if (!getSpec().paths) {
+ const paths = getSpec().paths as OpenAPIV3.PathsObject
+
+ if (!paths) {
return null
}
- for (const [path, methods] of Object.entries(getSpec().paths)) {
+ for (const [path, methods] of Object.entries(paths)) {
for (const verb of httpVerbs) {
- if (methods[verb]?.operationId === operationId) {
+ if (methods && methods[verb]?.operationId === operationId) {
return path
}
}
@@ -64,13 +82,15 @@ export function OpenApi({
}
function getOperationMethod(operationId: string) {
- if (!getSpec().paths) {
+ const paths = getSpec().paths as OpenAPIV3.PathsObject
+
+ if (!paths) {
return null
}
- for (const path of Object.values(getSpec().paths)) {
+ for (const path of Object.values(paths)) {
for (const verb of httpVerbs) {
- if (path[verb]?.operationId === operationId) {
+ if (path && path[verb]?.operationId === operationId) {
return verb
}
}
@@ -96,7 +116,9 @@ export function OpenApi({
}
function getPaths(): OpenAPIV3.PathsObject {
- return getSpec().paths ?? {}
+ const paths = getSpec().paths as OpenAPIV3.PathsObject
+
+ return paths ?? {}
}
function getPathsByVerbs() {
@@ -145,7 +167,9 @@ export function OpenApi({
const operationPath = getOperationPath(operationId)
- const pathServers = getSpec().paths[(operationPath ?? '')]?.servers
+ const paths = getSpec().paths as OpenAPIV3.PathsObject
+
+ const pathServers = paths[(operationPath ?? '')]?.servers
return [
...(operation?.servers ?? []),
@@ -159,9 +183,11 @@ export function OpenApi({
return []
}
- return Object.values(getSpec().paths).reduce((tags: string[], path) => {
+ const paths = getSpec().paths as OpenAPIV3.PathsObject
+
+ return Object.values(paths).reduce((tags: string[], path) => {
for (const verb of httpVerbs) {
- if (path[verb]?.tags) {
+ if (path && path[verb]?.tags) {
path[verb].tags.forEach((tag: string) => {
if (!tags.includes(tag)) {
tags.push(tag)
@@ -233,6 +259,8 @@ export function OpenApi({
spec: getSpec(),
transformedSpec,
parsedSpec,
+ setSpec,
+ getSpec,
getOperation,
getOperationPath,
getOperationMethod,
diff --git a/src/lib/codeSamples/buildRequest.ts b/src/lib/codeSamples/buildRequest.ts
index ae0215b..8a6402b 100644
--- a/src/lib/codeSamples/buildRequest.ts
+++ b/src/lib/codeSamples/buildRequest.ts
@@ -25,11 +25,16 @@ function getPath(variables: Record, pathParameters: OpenAPIV3.Pa
return resolvedPath
}
-function getHeaders(variables: Record, headerParameters: OpenAPIV3.ParameterObject[], authorizations: PlaygroundSecurityScheme | PlaygroundSecurityScheme[] | null) {
- const headers = new Headers({})
+function getHeaders(
+ headers: Headers,
+ variables: Record,
+ headerParameters: OpenAPIV3.ParameterObject[],
+ authorizations: PlaygroundSecurityScheme | PlaygroundSecurityScheme[] | null,
+) {
+ const resolvedHeaders = new Headers(headers)
processParameters(variables, headerParameters, (key: string, value: string) => {
- headers.set(key, value)
+ resolvedHeaders.set(key, value)
})
if (authorizations && !Array.isArray(authorizations)) {
@@ -37,7 +42,7 @@ function getHeaders(variables: Record, headerParameters: OpenAPI
}
if (!authorizations?.length) {
- return headers
+ return resolvedHeaders
}
Object.entries(authorizations).forEach(([_, authorization]) => {
@@ -48,19 +53,19 @@ function getHeaders(variables: Record, headerParameters: OpenAPI
const value = unref(authorization.playgroundValue)
if (authorization.type === 'http') {
- headers.set('Authorization', `${authorization.scheme === 'basic' ? 'Basic' : 'Bearer'} ${value}`)
+ resolvedHeaders.set('Authorization', `${authorization.scheme === 'basic' ? 'Basic' : 'Bearer'} ${value}`)
} else if (authorization.type === 'apiKey') {
- headers.set(authorization.name ?? '', value)
+ resolvedHeaders.set(authorization.name ?? '', value)
} else if (authorization.type === 'openIdConnect') {
- headers.set('Authorization', `Bearer ${value}`)
+ resolvedHeaders.set('Authorization', `Bearer ${value}`)
} else if (authorization.type === 'oauth2') {
- headers.set('Authorization', `Bearer ${value}`)
+ resolvedHeaders.set('Authorization', `Bearer ${value}`)
} else {
console.warn('Unknown auth scheme:', authorization)
}
})
- return headers
+ return resolvedHeaders
}
function getQuery(variables: Record, queryParameters: OpenAPIV3.ParameterObject[]) {
@@ -99,6 +104,7 @@ export function buildRequest({
parameters,
authorizations,
body,
+ headers = {},
variables = {},
}: {
path: string
@@ -108,6 +114,7 @@ export function buildRequest({
authorizations: PlaygroundSecurityScheme | PlaygroundSecurityScheme[] | null
body: any
variables: any
+ headers?: Record
}) {
const resolvedVariables = setExamplesAsVariables(parameters, variables)
@@ -133,12 +140,17 @@ export function buildRequest({
const query = getQuery(resolvedVariables, queryParameters)
- const headers = getHeaders(resolvedVariables, headerParameters, authorizations)
+ const resolvedHeaders = getHeaders(
+ new Headers(headers),
+ resolvedVariables,
+ headerParameters,
+ authorizations,
+ )
return new OARequest(
`${baseUrl}${resolvedPath}`,
method,
- Object.fromEntries(headers),
+ Object.fromEntries(resolvedHeaders),
body,
query,
)
diff --git a/src/lib/codeSamples/convertOARequestToRequest.ts b/src/lib/codeSamples/convertOARequestToRequest.ts
index 457942e..de84f28 100644
--- a/src/lib/codeSamples/convertOARequestToRequest.ts
+++ b/src/lib/codeSamples/convertOARequestToRequest.ts
@@ -6,7 +6,7 @@ export function OARequestToRequest(oaRequest: OARequest): Request {
const query = new URLSearchParams(oaRequest.query).toString()
const headers = new Headers(oaRequest.headers)
- const body = oaRequest.body ? JSON.stringify(oaRequest.body) : undefined
+ const body = ['POST', 'PUT', 'PATCH'].includes(method) && oaRequest.body ? JSON.stringify(oaRequest.body) : undefined
if (body && !headers.has('content-type')) {
headers.set('content-type', 'application/json')
diff --git a/src/lib/createAsyncOpenApiInstance.ts b/src/lib/createAsyncOpenApiInstance.ts
new file mode 100644
index 0000000..c41ed43
--- /dev/null
+++ b/src/lib/createAsyncOpenApiInstance.ts
@@ -0,0 +1,9 @@
+import type { ParsedOpenAPI } from '../types'
+import { processAsyncOpenAPI } from './processOpenAPI/processAsyncOpenAPI'
+
+export async function createAsyncOpenApiInstance(openapi: any) {
+ let spec = openapi.getSpec() as ParsedOpenAPI
+ spec = await processAsyncOpenAPI(spec)
+ openapi.setSpec(spec)
+ return openapi
+}
diff --git a/src/lib/createOpenApiInstance.ts b/src/lib/createOpenApiInstance.ts
index 87e96ad..081dfdd 100644
--- a/src/lib/createOpenApiInstance.ts
+++ b/src/lib/createOpenApiInstance.ts
@@ -1,7 +1,7 @@
import type { OpenAPI } from '@scalar/openapi-types'
import { OpenApi } from './OpenApi'
import { prepareOpenAPI } from './prepareOpenAPI/prepareOpenAPI'
-import { processOpenAPI } from './processOpenAPI'
+import { processOpenAPI } from './processOpenAPI/processOpenAPI'
export function createOpenApiInstance({
spec,
diff --git a/src/lib/examples/getSchemaExample.ts b/src/lib/examples/getSchemaExample.ts
index 305b678..b513c71 100644
--- a/src/lib/examples/getSchemaExample.ts
+++ b/src/lib/examples/getSchemaExample.ts
@@ -1,5 +1,5 @@
import type { OAExampleObject } from '../../types'
-import type { OAProperty } from '../getSchemaUi'
+import type { OAProperty } from '../processOpenAPI/getSchemaUi'
import { getSchemaUiJson } from './getSchemaUiJson'
import { getSchemaUiXml } from './getSchemaUiXml'
diff --git a/src/lib/examples/getSchemaUiJson.ts b/src/lib/examples/getSchemaUiJson.ts
index dd684eb..5b45482 100644
--- a/src/lib/examples/getSchemaUiJson.ts
+++ b/src/lib/examples/getSchemaUiJson.ts
@@ -1,4 +1,4 @@
-import type { OAProperty } from '../getSchemaUi'
+import type { OAProperty } from '../processOpenAPI/getSchemaUi'
import { literalTypes } from '../../index'
import { getPropertyExample } from './getPropertyExample'
diff --git a/src/lib/examples/getSchemaUiXml.ts b/src/lib/examples/getSchemaUiXml.ts
index e688dcd..57fd898 100644
--- a/src/lib/examples/getSchemaUiXml.ts
+++ b/src/lib/examples/getSchemaUiXml.ts
@@ -1,4 +1,4 @@
-import type { OAProperty } from '../getSchemaUi'
+import type { OAProperty } from '../processOpenAPI/getSchemaUi'
import { jsXml } from 'json-xml-parse'
import { getSchemaUiJson } from './getSchemaUiJson'
diff --git a/src/lib/getAsyncOpenApiInstance.ts b/src/lib/getAsyncOpenApiInstance.ts
new file mode 100644
index 0000000..21bb1ce
--- /dev/null
+++ b/src/lib/getAsyncOpenApiInstance.ts
@@ -0,0 +1,18 @@
+import { createAsyncOpenApiInstance } from './createAsyncOpenApiInstance'
+import { getOpenApiInstance } from './getOpenApiInstance'
+
+export async function getAsyncOpenApiInstance({
+ custom,
+ injected,
+}: {
+ custom?: { spec: any, parsedSpec?: any }
+ injected?: any
+} = {}) {
+ const openapi = getOpenApiInstance({ custom, injected })
+
+ if (!openapi) {
+ return null
+ }
+
+ return createAsyncOpenApiInstance(openapi)
+}
diff --git a/src/lib/getOpenApiInstance.ts b/src/lib/getOpenApiInstance.ts
index 21904ed..a1cf6f7 100644
--- a/src/lib/getOpenApiInstance.ts
+++ b/src/lib/getOpenApiInstance.ts
@@ -1,33 +1,20 @@
-import type { Schemas } from '../composables/useOpenapi'
-import { DEFAULT_SCHEMA, useOpenapi } from '../composables/useOpenapi'
+import { useOpenapi } from '../composables/useOpenapi'
import { createOpenApiInstance } from './createOpenApiInstance'
export function getOpenApiInstance({
- id,
custom,
injected,
- injectedLocal,
}: {
- id?: string
custom?: { spec: any, parsedSpec?: any }
- injected?: Schemas | any
- injectedLocal?: ReturnType
+ injected?: any
} = {}) {
- if (id === undefined) {
- id = DEFAULT_SCHEMA
- }
-
if (custom?.spec) {
return createOpenApiInstance({ spec: custom.spec })
}
- if (injectedLocal) {
- return injectedLocal
- }
-
- if (injected && injected.schemas) {
+ if (injected) {
try {
- return injected.schemas.get(id)
+ return injected
} catch {
console.warn('Deprecated usage of injected.')
return injected
@@ -35,7 +22,7 @@ export function getOpenApiInstance({
}
const globalSpec = useOpenapi()
- if (globalSpec) {
+ if (globalSpec?.json) {
return globalSpec
}
diff --git a/src/lib/processOpenAPI/getCodeSamples.ts b/src/lib/processOpenAPI/getCodeSamples.ts
new file mode 100644
index 0000000..a44fb1d
--- /dev/null
+++ b/src/lib/processOpenAPI/getCodeSamples.ts
@@ -0,0 +1,56 @@
+import type { OpenAPIV3 } from '@scalar/openapi-types'
+import type { ParsedOpenAPI, ParsedOperation, PlaygroundSecurityScheme } from '../../types'
+import { availableLanguages, useTheme } from '../../composables/useTheme'
+import { buildRequest } from '../codeSamples/buildRequest'
+import { generateCodeSample } from '../codeSamples/generateCodeSample'
+import { resolveBaseUrl } from '../resolveBaseUrl'
+
+export async function getCodeSamples(spec: ParsedOpenAPI): Promise {
+ if (!spec?.paths) {
+ return spec
+ }
+
+ const baseUrl = resolveBaseUrl(spec.servers?.[0]?.url)
+
+ for (const [path, pathObject] of Object.entries(spec.paths)) {
+ for (const verb of Object.keys(pathObject) as OpenAPIV3.HttpMethods[]) {
+ const operation = pathObject[verb] as ParsedOperation
+
+ if (!operation) {
+ continue
+ }
+
+ const authorizations = operation.securityUi?.[0]?.schemes || []
+
+ const request = buildRequest({
+ path,
+ method: verb,
+ baseUrl,
+ parameters: operation.parameters || [],
+ authorizations: Object.entries(authorizations).map(([name, value]) => {
+ return {
+ ...spec.components?.securitySchemes?.[name],
+ playgroundValue: name,
+ label: String(name),
+ } as PlaygroundSecurityScheme
+ }),
+ body: operation.requestBody?.content?.['application/json']?.examples?.example?.value || {},
+ headers: {
+ ...(useTheme().getCodeSamplesDefaultHeaders() || {}),
+ },
+ variables: {},
+ })
+
+ operation.codeSamples = await Promise.all(
+ availableLanguages.map(async (language) => {
+ return {
+ ...language,
+ source: await generateCodeSample(language.lang, request),
+ }
+ }),
+ )
+ }
+ }
+
+ return spec
+}
diff --git a/src/lib/getSchemaUi.ts b/src/lib/processOpenAPI/getSchemaUi.ts
similarity index 97%
rename from src/lib/getSchemaUi.ts
rename to src/lib/processOpenAPI/getSchemaUi.ts
index 5c58d84..26be7e6 100644
--- a/src/lib/getSchemaUi.ts
+++ b/src/lib/processOpenAPI/getSchemaUi.ts
@@ -1,8 +1,8 @@
import type { OpenAPI } from '@scalar/openapi-types'
-import { literalTypes } from '../index'
-import { getConstraints, hasConstraints } from './constraintsParser'
-import { getPropertyExamples } from './examples/getPropertyExamples'
-import { resolveCircularRef } from './resolveCircularRef'
+import { literalTypes } from '../../index'
+import { getConstraints, hasConstraints } from '../constraintsParser'
+import { getPropertyExamples } from '../examples/getPropertyExamples'
+import { resolveCircularRef } from '../resolveCircularRef'
type JSONSchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object' | 'null'
diff --git a/src/lib/getSecurityUi.ts b/src/lib/processOpenAPI/getSecurityUi.ts
similarity index 93%
rename from src/lib/getSecurityUi.ts
rename to src/lib/processOpenAPI/getSecurityUi.ts
index 3c96c97..3ccc8cf 100644
--- a/src/lib/getSecurityUi.ts
+++ b/src/lib/processOpenAPI/getSecurityUi.ts
@@ -1,5 +1,5 @@
import type { OpenAPIV3 } from '@scalar/openapi-types'
-import type { SecurityUi, SecurityUiItem } from '../types'
+import type { SecurityUi, SecurityUiItem } from '../../types'
const NO_SECURITY = 'None'
diff --git a/src/lib/processOpenAPI/processAsyncOpenAPI.ts b/src/lib/processOpenAPI/processAsyncOpenAPI.ts
new file mode 100644
index 0000000..ba766a5
--- /dev/null
+++ b/src/lib/processOpenAPI/processAsyncOpenAPI.ts
@@ -0,0 +1,10 @@
+import type { ParsedOpenAPI } from '../../types'
+import { getCodeSamples } from './getCodeSamples'
+
+export async function processAsyncOpenAPI(spec: ParsedOpenAPI): Promise {
+ let parsedSpec = spec
+
+ parsedSpec = await getCodeSamples(parsedSpec)
+
+ return { ...parsedSpec }
+}
diff --git a/src/lib/processOpenAPI.ts b/src/lib/processOpenAPI/processOpenAPI.ts
similarity index 90%
rename from src/lib/processOpenAPI.ts
rename to src/lib/processOpenAPI/processOpenAPI.ts
index 186182d..d78f6c3 100644
--- a/src/lib/processOpenAPI.ts
+++ b/src/lib/processOpenAPI/processOpenAPI.ts
@@ -1,11 +1,12 @@
import type { OpenAPI, OpenAPIV3 } from '@scalar/openapi-types'
import type { JSONSchema } from '@trojs/openapi-dereference'
-import type { ParsedContent, ParsedOpenAPI, ParsedOperation } from '../types'
+import type { ParsedContent, ParsedOpenAPI, ParsedOperation } from '../../types'
import { dereferenceSync } from '@trojs/openapi-dereference'
import { merge } from 'allof-merge'
-import { getSchemaExample } from './examples/getSchemaExample'
+import { getSchemaExample } from '../examples/getSchemaExample'
import { getSchemaUi } from './getSchemaUi'
import { getSecurityUi } from './getSecurityUi'
+import { getCodeSamples } from './getCodeSamples'
function safelyMergeSpec(spec: OpenAPI.Document): ParsedOpenAPI {
try {
@@ -44,6 +45,11 @@ export function processOpenAPI(spec: OpenAPI.Document): ParsedOpenAPI {
parsedSpec = safelyGenerateSecurityUi(parsedSpec)
parsedSpec = safelyGenerateSchemaUi(parsedSpec)
+ parsedSpec.externalDocs = spec.externalDocs || parsedSpec.externalDocs || {}
+ parsedSpec.info = spec.info || parsedSpec.info || {}
+ parsedSpec.servers = spec.servers || parsedSpec.servers || []
+ parsedSpec.tags = spec.tags || parsedSpec.tags || []
+
return { ...parsedSpec }
}
diff --git a/src/lib/resolveBaseUrl.ts b/src/lib/resolveBaseUrl.ts
new file mode 100644
index 0000000..5204586
--- /dev/null
+++ b/src/lib/resolveBaseUrl.ts
@@ -0,0 +1,12 @@
+export function resolveBaseUrl(url: string, defaultUrl: string = 'http://localhost') {
+ try {
+ // Ensure that the URL is valid.
+ // eslint-disable-next-line no-new
+ new URL(url)
+
+ return url
+ } catch (error: Error | any) {
+ console.error('Failed to resolve base URL:', error?.message)
+ return defaultUrl
+ }
+}
diff --git a/src/types.ts b/src/types.ts
index ee687b1..d1ed9e3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -56,6 +56,7 @@ export type OperationObject = OpenAPIV3.Document | OpenAPIV3_1.OperationObject
export type PlaygroundSecurityScheme = OpenAPIV3.HttpSecurityScheme & OpenAPIV3.ApiKeySecurityScheme & OpenAPIV3.OAuth2SecurityScheme & OpenAPIV3.OpenIdSecurityScheme & {
playgroundValue: RemovableRef
+ // | any
label: string
}
diff --git a/test/lib/__snapshots__/processOpenAPI.test.ts.snap b/test/lib/__snapshots__/processOpenAPI.test.ts.snap
index 490d5d5..5c7ddd5 100644
--- a/test/lib/__snapshots__/processOpenAPI.test.ts.snap
+++ b/test/lib/__snapshots__/processOpenAPI.test.ts.snap
@@ -394,6 +394,7 @@ exports[`processOpenAPI > processes the OpenAPI spec 1`] = `
},
},
},
+ "externalDocs": {},
"info": {
"contact": {
"email": "hi@enzonotario.me",
diff --git a/test/lib/examples/hasExample.test.ts b/test/lib/examples/hasExample.test.ts
index d08bb29..3a582b3 100644
--- a/test/lib/examples/hasExample.test.ts
+++ b/test/lib/examples/hasExample.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest'
import { hasExample } from '../../../src/lib/examples/hasExample'
import { specWithCircularRef, specWithCircularRefAndExample } from '../../testsConstants'
-import { getSchemaUi } from '../../../src/lib/getSchemaUi'
+import { getSchemaUi } from '../../../src/lib/processOpenAPI/getSchemaUi'
describe('hasExample', () => {
it('returns true if schema has an example at the root level', () => {
diff --git a/test/lib/getSchemaUi.test.ts b/test/lib/getSchemaUi.test.ts
index 611f36d..d9f667a 100644
--- a/test/lib/getSchemaUi.test.ts
+++ b/test/lib/getSchemaUi.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'
import { dereferenceSync } from '@trojs/openapi-dereference'
-import { getSchemaUi } from '../../src/lib/getSchemaUi'
+import { getSchemaUi } from '../../src/lib/processOpenAPI/getSchemaUi'
import { specWithCircularRef } from '../testsConstants'
import { getSchemaExample } from '../../src/lib/examples/getSchemaExample'
diff --git a/test/lib/getSecurityUi.test.ts b/test/lib/getSecurityUi.test.ts
index aebc97e..a55aaba 100644
--- a/test/lib/getSecurityUi.test.ts
+++ b/test/lib/getSecurityUi.test.ts
@@ -1,6 +1,6 @@
import type { OpenAPIV3 } from '@scalar/openapi-types'
import { describe, expect, it } from 'vitest'
-import { getSecurityUi } from '../../src/lib/getSecurityUi'
+import { getSecurityUi } from '../../src/lib/processOpenAPI/getSecurityUi'
describe('getSecurityUi', () => {
it('returns an empty array when security is undefined', () => {
diff --git a/test/lib/processOpenAPI.test.ts b/test/lib/processOpenAPI.test.ts
index 178b2f4..485c180 100644
--- a/test/lib/processOpenAPI.test.ts
+++ b/test/lib/processOpenAPI.test.ts
@@ -1,10 +1,10 @@
import { describe, expect, it } from 'vitest'
-import { processOpenAPI } from '../../src/lib/processOpenAPI'
import realSpec from '../../docs/public/openapi.json'
+import { processOpenAPI } from '../../src/lib/processOpenAPI/processOpenAPI'
describe('processOpenAPI', () => {
- it('processes the OpenAPI spec', () => {
- const parsedSPec = processOpenAPI(realSpec)
+ it('processes the OpenAPI spec', async () => {
+ const parsedSPec = await processOpenAPI(realSpec)
expect(parsedSPec).toMatchSnapshot()
})