diff --git a/.changeset/early-cameras-battle.md b/.changeset/early-cameras-battle.md deleted file mode 100644 index e0b8241eb..000000000 --- a/.changeset/early-cameras-battle.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@gitbook/openapi-parser': patch -'gitbook': patch ---- - -Implement a trusted mode to speed up OpenAPI spec validation diff --git a/packages/gitbook/src/lib/openapi/fetch.ts b/packages/gitbook/src/lib/openapi/fetch.ts index c7ad5d08e..af6443c87 100644 --- a/packages/gitbook/src/lib/openapi/fetch.ts +++ b/packages/gitbook/src/lib/openapi/fetch.ts @@ -82,13 +82,7 @@ const fetchFilesystem = cache({ } const text = await response.text(); - const filesystem = await parseOpenAPI({ - value: text, - rootURL: url, - // If we fetch the OpenAPI specification - // it's the legacy system, it means the spec can be trusted here. - trust: true, - }); + const filesystem = await parseOpenAPI({ value: text, rootURL: url }); const richFilesystem = await enrichFilesystem(filesystem); return { // Cache for 4 hours diff --git a/packages/openapi-parser/src/parse.ts b/packages/openapi-parser/src/parse.ts index 4efbbe402..9c102d457 100644 --- a/packages/openapi-parser/src/parse.ts +++ b/packages/openapi-parser/src/parse.ts @@ -3,7 +3,12 @@ import { OpenAPIParseError } from './error'; import { convertOpenAPIV2ToOpenAPIV3 } from './v2'; import { parseOpenAPIV3 } from './v3'; -export interface ParseOpenAPIInput { +/** + * Parse a raw string into an OpenAPI document. + * It will also convert Swagger 2.0 to OpenAPI 3.0. + * It can throw an `OpenAPIParseError` if the document is invalid. + */ +export async function parseOpenAPI(input: { /** * The API definition to parse. */ @@ -12,18 +17,7 @@ export interface ParseOpenAPIInput { * The root URL of the specified OpenAPI document. */ rootURL: string | null; - /** - * Trust the input. This will skip advanced validation. - */ - trust?: boolean; -} - -/** - * Parse a raw string into an OpenAPI document. - * It will also convert Swagger 2.0 to OpenAPI 3.0. - * It can throw an `OpenAPIParseError` if the document is invalid. - */ -export async function parseOpenAPI(input: ParseOpenAPIInput) { +}) { try { return await parseOpenAPIV3(input); } catch (error) { diff --git a/packages/openapi-parser/src/scalar-plugins/fetchURLs.ts b/packages/openapi-parser/src/scalar-plugins/fetchURLs.ts index 9076c4477..df820196e 100644 --- a/packages/openapi-parser/src/scalar-plugins/fetchURLs.ts +++ b/packages/openapi-parser/src/scalar-plugins/fetchURLs.ts @@ -43,7 +43,7 @@ export const fetchURLs: (customConfiguration: { return true; }, async get(value?: any) { - // Limit the number of requests + // Limit ht enumber of requests if (configuration?.limit !== false && numberOfRequests >= configuration?.limit) { console.warn( `[fetchUrls] Maximum number of requests reeached (${configuration?.limit}), skipping request`, diff --git a/packages/openapi-parser/src/v2.ts b/packages/openapi-parser/src/v2.ts index ac5e889b1..d219e354b 100644 --- a/packages/openapi-parser/src/v2.ts +++ b/packages/openapi-parser/src/v2.ts @@ -1,15 +1,22 @@ import { OpenAPIParseError } from './error'; import { parseOpenAPIV3 } from './v3'; +import type { AnyApiDefinitionFormat } from '@scalar/openapi-parser'; import type { Filesystem, OpenAPIV3xDocument } from './types'; -import type { ParseOpenAPIInput } from './parse'; import { upgrade } from '@scalar/openapi-parser'; /** * Convert a Swagger 2.0 schema to an OpenAPI 3.0 schema. */ -export async function convertOpenAPIV2ToOpenAPIV3( - input: ParseOpenAPIInput, -): Promise> { +export async function convertOpenAPIV2ToOpenAPIV3(input: { + /** + * The API definition to parse. + */ + value: AnyApiDefinitionFormat; + /** + * The root URL of the specified OpenAPI document. + */ + rootURL: string | null; +}): Promise> { const { value, rootURL } = input; const upgradeResult = (() => { diff --git a/packages/openapi-parser/src/v3.ts b/packages/openapi-parser/src/v3.ts index b084a81c0..557c2350c 100644 --- a/packages/openapi-parser/src/v3.ts +++ b/packages/openapi-parser/src/v3.ts @@ -1,33 +1,23 @@ -import { validate } from '@scalar/openapi-parser'; +import { type AnyApiDefinitionFormat, validate } from '@scalar/openapi-parser'; import { OpenAPIParseError } from './error'; import { createFileSystem } from './filesystem'; import type { Filesystem, OpenAPIV3xDocument } from './types'; -import type { ParseOpenAPIInput } from './parse'; /** * Parse a raw string into an OpenAPI document. * It will also convert Swagger 2.0 to OpenAPI 3.0. * It can throw an `OpenAPIFetchError` if the document is invalid. */ -export async function parseOpenAPIV3( - input: ParseOpenAPIInput, -): Promise> { - const { value, rootURL, trust } = input; - const specification = trust - ? trustedValidate({ value, rootURL }) - : await untrustedValidate({ value, rootURL }); - - const filesystem = await createFileSystem({ value: specification, rootURL }); - - return filesystem; -} - -type ValidateOpenAPIV3Input = Pick; - -/** - * Validate an untrusted OpenAPI v3 document. - */ -async function untrustedValidate(input: ValidateOpenAPIV3Input) { +export async function parseOpenAPIV3(input: { + /** + * The API definition to parse. + */ + value: AnyApiDefinitionFormat; + /** + * The root URL of the specified OpenAPI document. + */ + rootURL: string | null; +}): Promise> { const { value, rootURL } = input; const result = await validate(value); @@ -46,36 +36,7 @@ async function untrustedValidate(input: ValidateOpenAPIV3Input) { }); } - return result.specification; -} - -/** - * Validate a trusted OpenAPI v3 document. - * It assumes the specification is already a valid specification. - * It's faster than `untrustedValidate`. - */ -function trustedValidate(input: ValidateOpenAPIV3Input) { - const { value, rootURL } = input; - const result = (() => { - if (typeof value === 'string') { - try { - return JSON.parse(value); - } catch (error) { - throw new OpenAPIParseError('Invalid JSON', { - code: 'invalid', - rootURL, - }); - } - } - return value; - })(); - - if ('swagger' in result && result.swagger) { - throw new OpenAPIParseError('Only OpenAPI v3 is supported', { - code: 'parse-v2-in-v3', - rootURL, - }); - } - - return result; + const filesystem = await createFileSystem({ value: result.specification, rootURL }); + + return filesystem; }