diff --git a/.changeset/short-toes-accept.md b/.changeset/short-toes-accept.md new file mode 100644 index 000000000..604256e63 --- /dev/null +++ b/.changeset/short-toes-accept.md @@ -0,0 +1,5 @@ +--- +'@gitbook/react-openapi': patch +--- + +Fix read-only in generateSchemaExample diff --git a/packages/react-openapi/src/OpenAPIResponseExample.tsx b/packages/react-openapi/src/OpenAPIResponseExample.tsx index fe7f2666e..94a7399f9 100644 --- a/packages/react-openapi/src/OpenAPIResponseExample.tsx +++ b/packages/react-openapi/src/OpenAPIResponseExample.tsx @@ -268,6 +268,7 @@ function getExamplesFromMediaTypeObject(args: { value: { [root]: generateSchemaExample(mediaTypeObject.schema, { xml: mediaType === 'application/xml', + mode: 'read', }), }, }, @@ -277,7 +278,11 @@ function getExamplesFromMediaTypeObject(args: { return [ { key: 'default', - example: { value: generateSchemaExample(mediaTypeObject.schema) }, + example: { + value: generateSchemaExample(mediaTypeObject.schema, { + mode: 'read', + }), + }, }, ]; } diff --git a/packages/react-openapi/src/generateSchemaExample.ts b/packages/react-openapi/src/generateSchemaExample.ts index 01d06565c..d1153afb6 100644 --- a/packages/react-openapi/src/generateSchemaExample.ts +++ b/packages/react-openapi/src/generateSchemaExample.ts @@ -16,14 +16,10 @@ export function generateSchemaExample( schema: OpenAPIV3.SchemaObject, options?: GenerateSchemaExampleOptions ): JSONValue | undefined { - return getExampleFromSchema( - schema, - { - emptyString: 'text', - ...options, - }, - 3 // Max depth for circular references - ); + return getExampleFromSchema(schema, { + emptyString: 'text', + ...options, + }); } /** @@ -103,21 +99,6 @@ function guessFromFormat(schema: Record, fallback = '') { return genericExampleValues[schema.format] ?? fallback; } -/** Map of all the results */ -const resultCache = new WeakMap, any>(); - -/** Store result in the cache, and return the result */ -function cache(schema: Record, result: unknown) { - // Avoid unnecessary WeakMap operations for primitive values - if (typeof result !== 'object' || result === null) { - return result; - } - - resultCache.set(schema, result); - - return result; -} - /** * This function takes an OpenAPI schema and generates an example from it * Forked from : https://github.com/scalar/scalar/blob/main/packages/oas-utils/src/spec-getters/getExampleFromSchema.ts @@ -152,8 +133,20 @@ const getExampleFromSchema = ( }, level = 0, parentSchema?: Record, - name?: string + name?: string, + resultCache = new WeakMap, any>() ): any => { + // Store result in the cache, and return the result + function cache(schema: Record, result: unknown) { + // Avoid unnecessary WeakMap operations for primitive values + if (typeof result !== 'object' || result === null) { + return result; + } + + resultCache.set(schema, result); + return result; + } + // Check if the result is already cached if (resultCache.has(schema)) { return resultCache.get(schema); @@ -245,7 +238,8 @@ const getExampleFromSchema = ( options, level + 1, schema, - propertyName + propertyName, + resultCache ); if (typeof response[propertyXmlTagName ?? propertyName] === 'undefined') { @@ -269,7 +263,8 @@ const getExampleFromSchema = ( options, level + 1, schema, - exampleKey + exampleKey, + resultCache ); } } @@ -290,21 +285,51 @@ const getExampleFromSchema = ( response.ANY_ADDITIONAL_PROPERTY = getExampleFromSchema( schema.additionalProperties, options, - level + 1 + level + 1, + undefined, + undefined, + resultCache ); } } if (schema.anyOf !== undefined) { - Object.assign(response, getExampleFromSchema(schema.anyOf[0], options, level + 1)); + Object.assign( + response, + getExampleFromSchema( + schema.anyOf[0], + options, + level + 1, + undefined, + undefined, + resultCache + ) + ); } else if (schema.oneOf !== undefined) { - Object.assign(response, getExampleFromSchema(schema.oneOf[0], options, level + 1)); + Object.assign( + response, + getExampleFromSchema( + schema.oneOf[0], + options, + level + 1, + undefined, + undefined, + resultCache + ) + ); } else if (schema.allOf !== undefined) { Object.assign( response, ...schema.allOf .map((item: Record) => - getExampleFromSchema(item, options, level + 1, schema) + getExampleFromSchema( + item, + options, + level + 1, + schema, + undefined, + resultCache + ) ) .filter((item: any) => item !== undefined) ); @@ -335,7 +360,9 @@ const getExampleFromSchema = ( { type: 'object', allOf: schema.items.allOf }, options, level + 1, - schema + schema, + undefined, + resultCache ); return cache( @@ -346,7 +373,14 @@ const getExampleFromSchema = ( // For non-objects (like strings), collect all examples const examples = schema.items.allOf .map((item: Record) => - getExampleFromSchema(item, options, level + 1, schema) + getExampleFromSchema( + item, + options, + level + 1, + schema, + undefined, + resultCache + ) ) .filter((item: any) => item !== undefined); @@ -368,7 +402,14 @@ const getExampleFromSchema = ( const schemas = schema.items[rule].slice(0, 1); const exampleFromRule = schemas .map((item: Record) => - getExampleFromSchema(item, options, level + 1, schema) + getExampleFromSchema( + item, + options, + level + 1, + schema, + undefined, + resultCache + ) ) .filter((item: any) => item !== undefined); @@ -380,7 +421,14 @@ const getExampleFromSchema = ( } if (schema.items?.type) { - const exampleFromSchema = getExampleFromSchema(schema.items, options, level + 1); + const exampleFromSchema = getExampleFromSchema( + schema.items, + options, + level + 1, + undefined, + undefined, + resultCache + ); return wrapItems ? [{ [itemsXmlTagName]: exampleFromSchema }] : [exampleFromSchema]; } @@ -407,7 +455,14 @@ const getExampleFromSchema = ( const firstOneOfItem = discriminateSchema[0]; // Return an example for the first item - return getExampleFromSchema(firstOneOfItem, options, level + 1); + return getExampleFromSchema( + firstOneOfItem, + options, + level + 1, + undefined, + undefined, + resultCache + ); } // Check if schema has the `allOf` key @@ -417,7 +472,14 @@ const getExampleFromSchema = ( // Loop through all `allOf` schemas schema.allOf.forEach((allOfItem: Record) => { // Return an example from the schema - const newExample = getExampleFromSchema(allOfItem, options, level + 1); + const newExample = getExampleFromSchema( + allOfItem, + options, + level + 1, + undefined, + undefined, + resultCache + ); // Merge or overwrite the example example =